Commit 2296029e authored by Ken Rockot's avatar Ken Rockot Committed by Commit Bot

Enable Blink native Mojo bindings in WebUI

Adds the concept of ContextEnabled features as an extended attribute in
Web IDL, exposing a way for embedders to turn specific API features on
or off at context creation time.

Affixes this extended attribute to Mojo, MojoHandle, and MojoWatcher
IDLs to allow their exposure to be controlled per-context separately
from the MojoJS RuntimeEnabled state.

Finally, this also flips on the MojoJS ContextEnabled feature for all
RenderFrame contexts in which WebUI bindings are enabled.

BUG=699569,723923

Change-Id: Ie0fd6383ae86c133685b3e7b31eeca769bf83e06
Reviewed-on: https://chromium-review.googlesource.com/509390
Commit-Queue: Ken Rockot <rockot@chromium.org>
Reviewed-by: default avatarDaniel Cheng <dcheng@chromium.org>
Reviewed-by: default avatarKentaro Hara <haraken@chromium.org>
Reviewed-by: default avatarJeremy Roman <jbroman@chromium.org>
Reviewed-by: default avatarScott Violet <sky@chromium.org>
Cr-Commit-Position: refs/heads/master@{#474973}
parent d8a38496
......@@ -20,6 +20,7 @@
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_ui.h"
#include "content/public/browser/web_ui_controller.h"
#include "content/public/browser/web_ui_data_source.h"
#include "content/public/common/content_paths.h"
......@@ -158,6 +159,8 @@ class TestWebUIControllerFactory : public WebUIControllerFactory {
}
WebUI::TypeID GetWebUIType(BrowserContext* browser_context,
const GURL& url) const override {
if (!web_ui_enabled_)
return WebUI::kNoWebUI;
return reinterpret_cast<WebUI::TypeID>(1);
}
bool UseWebUIForURL(BrowserContext* browser_context,
......@@ -169,8 +172,11 @@ class TestWebUIControllerFactory : public WebUIControllerFactory {
return true;
}
void set_web_ui_enabled(bool enabled) { web_ui_enabled_ = enabled; }
private:
base::RunLoop* run_loop_;
bool web_ui_enabled_ = true;
DISALLOW_COPY_AND_ASSIGN(TestWebUIControllerFactory);
};
......@@ -237,5 +243,30 @@ IN_PROC_BROWSER_TEST_F(WebUIMojoTest, EndToEndPing) {
other_shell->web_contents()->GetRenderProcessHost());
}
IN_PROC_BROWSER_TEST_F(WebUIMojoTest, NativeMojoAvailable) {
ASSERT_TRUE(embedded_test_server()->Start());
const GURL kTestWebUIUrl("chrome://mojo-web-ui/web_ui_mojo_native.html");
NavigateToURL(shell(), kTestWebUIUrl);
bool is_native_mojo_available = false;
const std::string kTestScript("isNativeMojoAvailable()");
ASSERT_TRUE(ExecuteScriptAndExtractBool(
shell()->web_contents(),
"domAutomationController.send(" + kTestScript + ")",
&is_native_mojo_available));
EXPECT_TRUE(is_native_mojo_available);
// Now navigate again with WebUI disabled and ensure the native bindings are
// not available.
factory()->set_web_ui_enabled(false);
const std::string kTestNonWebUIUrl("/web_ui_mojo_native.html");
NavigateToURL(shell(), embedded_test_server()->GetURL(kTestNonWebUIUrl));
ASSERT_TRUE(ExecuteScriptAndExtractBool(
shell()->web_contents(),
"domAutomationController.send(" + kTestScript + ")",
&is_native_mojo_available));
EXPECT_FALSE(is_native_mojo_available);
}
} // namespace
} // namespace content
......@@ -10,6 +10,7 @@
#include "content/public/renderer/render_view.h"
#include "content/renderer/mojo_context_state.h"
#include "gin/per_context_data.h"
#include "third_party/WebKit/public/web/WebContextFeatures.h"
#include "third_party/WebKit/public/web/WebKit.h"
#include "third_party/WebKit/public/web/WebLocalFrame.h"
#include "third_party/WebKit/public/web/WebView.h"
......@@ -66,6 +67,22 @@ MojoContextState* MojoBindingsController::GetContextState() {
return context_state ? context_state->state.get() : NULL;
}
void MojoBindingsController::DidCreateScriptContext(
v8::Local<v8::Context> context,
int world_id) {
// NOTE: Layout tests already get this turned on by the RuntimeEnabled feature
// setting. We avoid manually installing them here for layout tests, because
// some layout tests (namely at
// least virtual/stable/webexposed/global-interface-listing.html) may be run
// with such features explicitly disabled. We do not want to unconditionally
// install Mojo bindings in such environments.
//
// We also only allow these bindings to be installed when creating the main
// world context.
if (bindings_type_ != MojoBindingsType::FOR_LAYOUT_TESTS && world_id == 0)
blink::WebContextFeatures::EnableMojoJS(context, true);
}
void MojoBindingsController::WillReleaseScriptContext(
v8::Local<v8::Context> context,
int world_id) {
......
......@@ -22,6 +22,10 @@ enum class MojoBindingsType { FOR_WEB_UI, FOR_LAYOUT_TESTS };
// bindings. It creates (and destroys) a MojoContextState at the appropriate
// times and handles the necessary browser messages. MojoBindingsController
// destroys itself when the RendererFrame it is created with is destroyed.
//
// TODO(rockot): Clean up this class as we migrate everything to the native
// Blink bindings, which only require the implementation of
// DidCreateScriptContext() we have here.
class MojoBindingsController
: public RenderFrameObserver,
public RenderFrameObserverTracker<MojoBindingsController> {
......@@ -39,6 +43,8 @@ class MojoBindingsController
MojoContextState* GetContextState();
// RenderFrameObserver overrides:
void DidCreateScriptContext(v8::Local<v8::Context> context,
int world_id) override;
void WillReleaseScriptContext(v8::Local<v8::Context> context,
int world_id) override;
void DidClearWindowObject() override;
......
<html>
<head><script src="web_ui_mojo_native.js"></script></head>
<body>hi</body>
</html>
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
'use strict';
window.isNativeMojoAvailable = () => {
return 'Mojo' in self && 'MojoHandle' in self && 'MojoWatcher' in self;
};
......@@ -672,6 +672,22 @@ world (note that the DOM object is shared among multiple worlds), it leaks the S
to the world. ScriptState must be carefully maintained in a way that doesn't leak
to another world.
### [ContextEnabled] _(i)_
Summary: `[ContextEnabled]` renders the generated interface bindings unavailable by default, but also generates code which allows individual script contexts opt into installing the bindings.
Usage: `[ContextEnabled=FeatureName]`. FeatureName is an arbitrary name used to identify the feature at runtime.
```webidl
[
ContextEnabled=MojoJS
] interface Mojo { ... };
```
When applied to an interface, the generated code for the relevant global object will include a public `installFeatureName()` method which can be called to install the interface on the global object.
Note that `[ContextEnabled]` is not mututally exclusive to `[RuntimeEnabled]`, and a feature which may be enabled by either mechanism will be enabled if the appropriate `[RuntimeEnabled]` feature is enabled; _or_ if the appropriate `[ContextEnabled]` feature is enabled; _or_ if both are enabled.
### [Custom] _(i, m, s, a, f)_
Summary: They allow you to write bindings code manually as you like: full bindings for methods and attributes, certain functions for interfaces.
......
......@@ -43,6 +43,7 @@ Constructor
# FIXME: remove [ConstructorCallWith=Document], as can instead use
# [ConstructorCallWith=ExecutionContext] + toDocument(executionContext)
ConstructorCallWith=ExecutionContext|ScriptState|Document
ContextEnabled=*
CrossOrigin=|Getter|Setter
Custom=|Getter|Setter|LegacyCallAsFunction|PropertyGetter|PropertyEnumerator|PropertyQuery|CallPrologue|CallEpilogue
CustomConstructor
......
......@@ -8,6 +8,7 @@
#include "bindings/core/v8/V8HTMLLinkElement.h"
#include "bindings/core/v8/V8Navigator.h"
#include "bindings/core/v8/V8Window.h"
#include "core/context_features/ContextFeatureSettings.h"
#include "core/dom/ExecutionContext.h"
#include "core/frame/Frame.h"
#include "core/origin_trials/OriginTrials.h"
......@@ -38,7 +39,17 @@ void InstallConditionalFeaturesCore(const WrapperTypeInfo* wrapper_type_info,
return;
v8::Isolate* isolate = script_state->GetIsolate();
const DOMWrapperWorld& world = script_state->World();
if (wrapper_type_info == &V8HTMLLinkElement::wrapperTypeInfo) {
if (wrapper_type_info == &V8Window::wrapperTypeInfo) {
auto* settings = ContextFeatureSettings::From(
execution_context,
ContextFeatureSettings::CreationMode::kDontCreateIfNotExists);
if (settings && settings->isMojoJSEnabled()) {
v8::Local<v8::Object> instance_object =
script_state->GetContext()->Global();
V8Window::installMojoJS(isolate, world, instance_object, prototype_object,
interface_object);
}
} else if (wrapper_type_info == &V8HTMLLinkElement::wrapperTypeInfo) {
if (OriginTrials::linkServiceWorkerEnabled(execution_context)) {
V8HTMLLinkElement::installLinkServiceWorker(
isolate, world, v8::Local<v8::Object>(), prototype_object,
......
......@@ -105,9 +105,9 @@ def record_global_constructors(idl_filename):
def generate_global_constructors_list(interface_name, extended_attributes):
extended_attributes_list = [
name + '=' + extended_attributes[name]
for name in 'RuntimeEnabled', 'OriginTrialEnabled'
if name in extended_attributes]
name + '=' + extended_attributes[name]
for name in 'RuntimeEnabled', 'OriginTrialEnabled', 'ContextEnabled'
if name in extended_attributes]
if extended_attributes_list:
extended_string = '[%s] ' % ', '.join(extended_attributes_list)
else:
......
......@@ -124,6 +124,7 @@ def attribute_context(interface, attribute, interfaces):
'activity_logging_world_check': v8_utilities.activity_logging_world_check(attribute), # [ActivityLogging]
'cached_attribute_validation_method': cached_attribute_validation_method,
'constructor_type': constructor_type,
'context_enabled_feature_name': v8_utilities.context_enabled_feature_name(attribute),
'cpp_name': cpp_name(attribute),
'cpp_type': idl_type.cpp_type,
'cpp_type_initializer': idl_type.cpp_type_initializer,
......@@ -213,6 +214,7 @@ def filter_accessors(attributes):
return [attribute for attribute in attributes if
not (attribute['exposed_test'] or
attribute['secure_context_test'] or
attribute['context_enabled_feature_name'] or
attribute['origin_trial_enabled_function'] or
attribute['runtime_enabled_feature_name']) and
not attribute['is_data_type_property']]
......@@ -221,6 +223,7 @@ def filter_accessors(attributes):
def is_data_attribute(attribute):
return (not (attribute['exposed_test'] or
attribute['secure_context_test'] or
attribute['context_enabled_feature_name'] or
attribute['origin_trial_enabled_function'] or
attribute['runtime_enabled_feature_name']) and
attribute['is_data_type_property'])
......
......@@ -45,8 +45,9 @@ from v8_globals import includes
import v8_methods
import v8_types
import v8_utilities
from v8_utilities import (cpp_name_or_partial, cpp_name, has_extended_attribute_value,
runtime_enabled_feature_name, is_legacy_interface_type_checking)
from v8_utilities import (context_enabled_feature_name, cpp_name_or_partial, cpp_name,
has_extended_attribute_value, runtime_enabled_feature_name,
is_legacy_interface_type_checking)
INTERFACE_H_INCLUDES = frozenset([
......@@ -142,7 +143,33 @@ def origin_trial_features(interface, constants, attributes, methods):
if features:
includes.add('platform/bindings/ScriptState.h')
includes.add('core/origin_trials/OriginTrials.h')
return sorted(features)
return features
def context_enabled_features(attributes):
""" Returns a list of context-enabled features from a set of attributes.
Each element is a dictionary with the feature's |name| and lists of
|attributes| associated with the feature.
"""
KEY = 'context_enabled_feature_name' # pylint: disable=invalid-name
def member_filter(members):
return sorted([member for member in members if member.get(KEY) and not member.get('exposed_test')])
def member_filter_by_name(members, name):
return [member for member in members if member[KEY] == name]
# Collect all members visible on this interface with a defined origin trial
context_enabled_attributes = member_filter(attributes)
feature_names = set([member[KEY] for member in context_enabled_attributes])
features = [{'name': name,
'attributes': member_filter_by_name(context_enabled_attributes, name),
'needs_instance': False}
for name in feature_names]
if features:
includes.add('platform/bindings/ScriptState.h')
return features
def interface_context(interface, interfaces):
......@@ -233,6 +260,7 @@ def interface_context(interface, interfaces):
'V8Window', 'V8HTMLDocument', 'V8Document', 'V8Node', 'V8EventTarget']
context = {
'context_enabled_feature_name': context_enabled_feature_name(interface), # [ContextEnabled]
'cpp_class': cpp_class_name,
'cpp_class_or_partial': cpp_class_name_or_partial,
'is_gc_type': True,
......@@ -418,9 +446,11 @@ def interface_context(interface, interfaces):
'has_named_properties_object': is_global and context['named_property_getter'],
})
# Origin Trials
# Origin Trials and ContextEnabled features
context.update({
'origin_trial_features': origin_trial_features(interface, context['constants'], context['attributes'], context['methods']),
'optional_features':
sorted(origin_trial_features(interface, context['constants'], context['attributes'], context['methods']) +
context_enabled_features(context['attributes'])),
})
# Cross-origin interceptors
......
......@@ -437,6 +437,11 @@ def origin_trial_feature_name(definition_or_member):
return extended_attributes.get('OriginTrialEnabled') or extended_attributes.get('FeaturePolicy')
# [ContextEnabled]
def context_enabled_feature_name(definition_or_member):
return definition_or_member.extended_attributes.get('ContextEnabled')
# [RuntimeEnabled]
def runtime_enabled_feature_name(definition_or_member):
extended_attributes = definition_or_member.extended_attributes
......
......@@ -10,7 +10,7 @@
namespace blink {
{% if origin_trial_features %}
{% if optional_features %}
class ScriptState;
{% endif %}
{% if named_constructor %}
......@@ -138,7 +138,7 @@ class {{v8_class}} {
{% endfor %}
{% endif %}
{% for feature in origin_trial_features %}
{% for feature in optional_features %}
static void install{{feature.name}}(v8::Isolate*, const DOMWrapperWorld&, v8::Local<v8::Object> instance, v8::Local<v8::Object> prototype, v8::Local<v8::Function> interface);
static void install{{feature.name}}(ScriptState*, v8::Local<v8::Object> instance);
......
......@@ -701,7 +701,7 @@ void {{v8_class_or_partial}}::installRuntimeEnabledFeatures(v8::Isolate* isolate
with context %}
{% from 'constants.cpp.tmpl' import constant_configuration with context %}
{% from 'methods.cpp.tmpl' import method_configuration with context %}
{% for feature in origin_trial_features %}
{% for feature in optional_features %}
void {{v8_class_or_partial}}::install{{feature.name}}(v8::Isolate* isolate, const DOMWrapperWorld& world, v8::Local<v8::Object> instance, v8::Local<v8::Object> prototype, v8::Local<v8::Function> interface) {
{% if feature.attributes or feature.methods %}
v8::Local<v8::FunctionTemplate> interfaceTemplate = {{v8_class}}::wrapperTypeInfo.domTemplate(isolate, world);
......
......@@ -10,7 +10,7 @@
namespace blink {
{% if origin_trial_features %}
{% if optional_features %}
class ScriptState;
{% endif %}
......@@ -33,7 +33,7 @@ class {{v8_class_or_partial}} {
static void preparePrototypeAndInterfaceObject(v8::Local<v8::Context>, const DOMWrapperWorld&, v8::Local<v8::Object>, v8::Local<v8::Function>, v8::Local<v8::FunctionTemplate>);
{% endif %}
{% for feature in origin_trial_features %}
{% for feature in optional_features %}
static void install{{feature.name}}(ScriptState*, v8::Local<v8::Object> instance);
static void install{{feature.name}}(v8::Isolate*, const DOMWrapperWorld&, v8::Local<v8::Object> instance, v8::Local<v8::Object> prototype, v8::Local<v8::Function> interface);
{% if not feature.needs_instance %}
......
......@@ -38,6 +38,7 @@
DoNotCheckConstants,
ImplementedAs=TestInterfaceImplementation,
RuntimeEnabled=FeatureName,
ContextEnabled=FeatureName,
Exposed=(Worker,Window),
] interface TestInterface : TestInterfaceEmpty {
// members needed to test [ImplementedAs], as this affect attribute
......
......@@ -173,6 +173,7 @@ component("core") {
deps = [
"//third_party/WebKit/Source/core/animation",
"//third_party/WebKit/Source/core/clipboard",
"//third_party/WebKit/Source/core/context_features",
"//third_party/WebKit/Source/core/css",
"//third_party/WebKit/Source/core/dom",
"//third_party/WebKit/Source/core/editing",
......
# Copyright 2017 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import("//third_party/WebKit/Source/core/core.gni")
blink_core_sources("context_features") {
sources = [
"ContextFeatureSettings.cpp",
"ContextFeatureSettings.h",
]
}
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "core/context_features/ContextFeatureSettings.h"
#include "core/dom/ExecutionContext.h"
#include "public/platform/Platform.h"
namespace blink {
ContextFeatureSettings::ContextFeatureSettings(ExecutionContext& context)
: Supplement<ExecutionContext>(context) {}
// static
const char* ContextFeatureSettings::SupplementName() {
return "ContextFeatureSettings";
}
// static
ContextFeatureSettings* ContextFeatureSettings::From(
ExecutionContext* context,
CreationMode creation_mode) {
ContextFeatureSettings* settings = static_cast<ContextFeatureSettings*>(
Supplement<ExecutionContext>::From(context, SupplementName()));
if (!settings && creation_mode == CreationMode::kCreateIfNotExists) {
settings = new ContextFeatureSettings(*context);
Supplement<ExecutionContext>::ProvideTo(*context, SupplementName(),
settings);
}
return settings;
}
DEFINE_TRACE(ContextFeatureSettings) {
Supplement<ExecutionContext>::Trace(visitor);
}
} // namespace blink
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef ContextFeatureSettings_h
#define ContextFeatureSettings_h
#include "core/CoreExport.h"
#include "core/dom/ExecutionContext.h"
#include "platform/Supplementable.h"
namespace blink {
class ExecutionContext;
// ContextFeatureSettings attaches to an ExecutionContext some basic flags
// pertaining to the enabled/disabled state of any platform API features which
// are gated behind a ContextEnabled extended attribute in IDL.
class CORE_EXPORT ContextFeatureSettings final
: public GarbageCollectedFinalized<ContextFeatureSettings>,
public Supplement<ExecutionContext> {
USING_GARBAGE_COLLECTED_MIXIN(ContextFeatureSettings)
public:
enum class CreationMode { kCreateIfNotExists, kDontCreateIfNotExists };
explicit ContextFeatureSettings(ExecutionContext&);
static const char* SupplementName();
// Returns the ContextFeatureSettings for an ExecutionContext. If one does not
// already exist for the given context, one is created.
static ContextFeatureSettings* From(ExecutionContext*, CreationMode);
// ContextEnabled=MojoJS feature
void enableMojoJS(bool enable) { enable_mojo_js_ = enable; }
bool isMojoJSEnabled() const { return enable_mojo_js_; }
DECLARE_VIRTUAL_TRACE();
private:
bool enable_mojo_js_ = false;
};
} // namespace blink
#endif // ContextFeatureSettings_h
......@@ -7,6 +7,7 @@
typedef unsigned long MojoResult;
[
ContextEnabled=MojoJS,
RuntimeEnabled=MojoJS,
] interface Mojo {
const MojoResult RESULT_OK = 0;
......
......@@ -5,6 +5,7 @@
callback MojoWatchCallback = void (MojoResult result);
[
ContextEnabled=MojoJS,
RuntimeEnabled=MojoJS,
] interface MojoHandle {
void close();
......
......@@ -5,6 +5,7 @@
[
ActiveScriptWrappable,
DependentLifetime,
ContextEnabled=MojoJS,
RuntimeEnabled=MojoJS,
] interface MojoWatcher {
MojoResult cancel();
......
......@@ -117,6 +117,7 @@ component("web") {
"UserMediaClientImpl.cpp",
"UserMediaClientImpl.h",
"WebAXObject.cpp",
"WebContextFeatures.cpp",
"WebDOMMessageEvent.cpp",
"WebDevToolsAgentImpl.cpp",
"WebDevToolsAgentImpl.h",
......
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "public/web/WebContextFeatures.h"
#include "core/context_features/ContextFeatureSettings.h"
#include "platform/bindings/DOMWrapperWorld.h"
#include "platform/bindings/ScriptState.h"
namespace blink {
// static
void WebContextFeatures::EnableMojoJS(v8::Local<v8::Context> context,
bool enable) {
ScriptState* script_state = ScriptState::From(context);
DCHECK(script_state->World().IsMainWorld());
ContextFeatureSettings::From(
ExecutionContext::From(script_state),
ContextFeatureSettings::CreationMode::kCreateIfNotExists)
->enableMojoJS(enable);
}
} // namespace blink
......@@ -467,6 +467,7 @@ source_set("blink_headers") {
"web/WebColorSuggestion.h",
"web/WebCompositionUnderline.h",
"web/WebConsoleMessage.h",
"web/WebContextFeatures.h",
"web/WebContextMenuData.h",
"web/WebCryptoNormalize.h",
"web/WebCustomElement.h",
......
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef WebContextFeatures_h
#define WebContextFeatures_h
#include "public/platform/WebCommon.h"
#include "v8/include/v8.h"
namespace blink {
// WebContextFeatures is used in conjunction with IDL interface features which
// specify a [ContextEnabled] extended attribute. Such features may be enabled
// for arbitrary main-world V8 contexts by using these methods during
// WebFrameClient::DidCreateScriptContext. Enabling a given feature causes its
// binding(s) to be installed on the appropriate global object(s) during context
// initialization.
//
// See src/third_party/WebKit/Source/bindings/IDLExtendedAttributes.md for more
// information.
class WebContextFeatures {
public:
BLINK_EXPORT static void EnableMojoJS(v8::Local<v8::Context>, bool);
private:
WebContextFeatures();
};
} // namespace blink
#endif // WebContextFeatures_h
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment