Commit 70527de5 authored by ericzeng@chromium.org's avatar ericzeng@chromium.org

Implement support for <extensionoptions> embedding in WebUI

Modify permissions and checks in GuestView, WebUI, and
extensions to allow <extensionoptions> to be used within WebUI.
In the GuestView infrastructure, allow guest views in WebUI to
be created without extension ids. In extensions, allow extension
options-related APIs to be used in WebUI contexts. In WebUI,
relax the CSP to allow internal browser plugins.  


BUG=386842

Review URL: https://codereview.chromium.org/453613002

Cr-Commit-Position: refs/heads/master@{#289233}
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@289233 0039d316-1c4b-4281-b951-d872f2087c98
parent 3da74d14
......@@ -40,9 +40,15 @@ bool GuestViewInternalCreateGuestFunction::RunAsync() {
error_ = "Guest views can only be embedded in web content";
return false;
}
// If the guest is an <extensionoptions> to be embedded in a WebUI, then
// there is no extension, and extension() will be null. Use an empty string
// instead.
std::string embedder_extension_id;
if (extension())
embedder_extension_id = extension_id();
guest_view_manager->CreateGuest(view_type,
extension_id(),
embedder_extension_id,
embedder_web_contents,
*create_params,
callback);
......
......@@ -18,6 +18,8 @@
#include "extensions/browser/event_router.h"
#include "extensions/common/api/test.h"
#include "extensions/common/extension.h"
#include "extensions/common/feature_switch.h"
#include "extensions/common/switches.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace extensions {
......@@ -108,6 +110,18 @@ class ExtensionWebUITest : public ExtensionApiTest {
base::Bind(&FindFrame, frame_url, &frame_host));
return frame_host;
}
virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
FeatureSwitch::ScopedOverride enable_options(
FeatureSwitch::embedded_extension_options(), true);
// Need to add a command line flag as well as a FeatureSwitch because the
// FeatureSwitch is not copied over to the renderer process from the
// browser process.
command_line->AppendSwitch(switches::kEnableEmbeddedExtensionOptions);
ExtensionApiTest::SetUpCommandLine(command_line);
}
scoped_ptr<FeatureSwitch::ScopedOverride> enable_options_;
};
IN_PROC_BROWSER_TEST_F(ExtensionWebUITest, SanityCheckAvailableAPIs) {
......@@ -167,6 +181,23 @@ IN_PROC_BROWSER_TEST_F(ExtensionWebUITest, RuntimeLastError) {
EXPECT_EQ("true", listener->message());
}
IN_PROC_BROWSER_TEST_F(ExtensionWebUITest, CanEmbedExtensionOptions) {
scoped_ptr<ExtensionTestMessageListener> listener(
new ExtensionTestMessageListener("ready", true));
const Extension* extension = InstallExtension(
test_data_dir_.AppendASCII("extension_options").AppendASCII("embed_self"),
1);
ASSERT_TRUE(extension);
ASSERT_TRUE(RunTestOnExtensions("can_embed_extension_options.js"));
ASSERT_TRUE(listener->WaitUntilSatisfied());
listener->Reply(extension->id());
listener.reset(new ExtensionTestMessageListener("guest loaded", false));
ASSERT_TRUE(listener->WaitUntilSatisfied());
}
} // namespace
} // namespace extensions
......@@ -64,7 +64,9 @@ void ExtensionOptionsGuest::CreateWebContents(
return;
}
if (extension_id != embedder_extension_id) {
if (extensions::Extension::IdIsValid(embedder_extension_id) &&
extension_id != embedder_extension_id) {
// Extensions cannot embed other extensions' options pages.
callback.Run(NULL);
return;
}
......
......@@ -84,6 +84,10 @@ ExtensionsUI::ExtensionsUI(content::WebUI* web_ui) : WebUIController(web_ui) {
web_ui->AddMessageHandler(new MetricsHandler());
// Need to allow <object> elements so that the <extensionoptions> browser
// plugin can be loaded within chrome://extensions.
source->OverrideContentSecurityPolicyObjectSrc("object-src 'self';");
content::WebUIDataSource::Add(profile, source);
}
......
......@@ -375,11 +375,16 @@
"extension.sendRequest": {
"contexts": ["blessed_extension", "unblessed_extension", "content_script"]
},
"extensionOptionsInternal": {
"extensionOptionsInternal": [{
"internal": true,
"contexts": ["blessed_extension"],
"dependencies": ["permission:embeddedExtensionOptions"]
},
}, {
"internal": true,
"channel": "trunk",
"contexts": ["webui"],
"matches": ["chrome://extensions-frame/*", "chrome://extensions/*"]
}],
// This is not a real API, only here for documentation purposes.
// See http://crbug.com/275944 for background.
"extensionsManifestTypes": {
......@@ -453,6 +458,11 @@
"internal": true,
"dependencies": ["permission:webview"],
"contexts": ["unblessed_extension"]
}, {
"internal": true,
"channel": "trunk",
"contexts": ["webui"],
"matches": ["chrome://extensions-frame/*", "chrome://extensions/*"]
}],
"hangoutsPrivate": {
"channel": "stable",
......
......@@ -245,6 +245,14 @@ bool ShouldUseJavaScriptSettingForPlugin(const WebPluginInfo& plugin) {
return false;
}
void IsGuestViewApiAvailableToScriptContext(
bool* api_is_available,
extensions::ScriptContext* context) {
if (context->GetAvailability("guestViewInternal").is_available()) {
*api_is_available = true;
}
}
} // namespace
ChromeContentRendererClient::ChromeContentRendererClient() {
......@@ -520,20 +528,13 @@ bool ChromeContentRendererClient::OverrideCreatePlugin(
WebPlugin** plugin) {
std::string orig_mime_type = params.mimeType.utf8();
if (orig_mime_type == content::kBrowserPluginMimeType) {
WebDocument document = frame->document();
const Extension* extension =
GetExtensionByOrigin(document.securityOrigin());
if (extension) {
const extensions::APIPermission::ID perms[] = {
extensions::APIPermission::kAppView,
extensions::APIPermission::kEmbeddedExtensionOptions,
extensions::APIPermission::kWebView,
};
for (size_t i = 0; i < arraysize(perms); ++i) {
if (extension->permissions_data()->HasAPIPermission(perms[i]))
return false;
}
}
bool guest_view_api_available = false;
extension_dispatcher_->script_context_set().ForEach(
render_frame->GetRenderView(),
base::Bind(&IsGuestViewApiAvailableToScriptContext,
&guest_view_api_available));
if (guest_view_api_available)
return false;
}
ChromeViewHostMsg_GetPluginInfo_Output output;
......
......@@ -289,6 +289,7 @@ void ChromeExtensionsDispatcherDelegate::RequireAdditionalModules(
// The API will be automatically set up when first used.
if (context_type == extensions::Feature::BLESSED_EXTENSION_CONTEXT ||
context_type == extensions::Feature::UNBLESSED_EXTENSION_CONTEXT) {
// TODO(fsamuel): Use context->GetAvailability("webViewInternal").
if (extension->permissions_data()->HasAPIPermission(
extensions::APIPermission::kWebView)) {
module_system->Require("webView");
......@@ -313,6 +314,7 @@ void ChromeExtensionsDispatcherDelegate::RequireAdditionalModules(
}
if (context_type == extensions::Feature::BLESSED_EXTENSION_CONTEXT) {
// TODO(fsamuel): Use context->GetAvailability("appViewInternal").
if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kEnableAppView) &&
extension->permissions_data()->HasAPIPermission(
extensions::APIPermission::kAppView)) {
......@@ -322,10 +324,8 @@ void ChromeExtensionsDispatcherDelegate::RequireAdditionalModules(
}
}
if (context_type == extensions::Feature::BLESSED_EXTENSION_CONTEXT &&
extensions::FeatureSwitch::embedded_extension_options()->IsEnabled() &&
extension->permissions_data()->HasAPIPermission(
extensions::APIPermission::kEmbeddedExtensionOptions)) {
if (extensions::FeatureSwitch::embedded_extension_options()->IsEnabled() &&
context->GetAvailability("extensionOptionsInternal").is_available()) {
module_system->Require("extensionOptions");
}
}
......
// Copyright 2014 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.
// out/Debug/browser_tests
// --gtest_filter=ExtensionWebUITest.CanEmbedExtensionOptions
if (!chrome || !chrome.test || !chrome.test.sendMessage) {
console.error('chrome.test.sendMessage is unavailable on ' +
document.location.href);
domAutomationController.send(false);
return;
}
chrome.test.sendMessage('ready', function(reply) {
var extensionoptions = document.createElement('extensionoptions');
extensionoptions.addEventListener('load', function() {
chrome.test.sendMessage('guest loaded');
});
extensionoptions.setAttribute('extension', reply);
document.body.appendChild(extensionoptions);
});
domAutomationController.send(true);
......@@ -397,10 +397,6 @@ GuestViewBase::~GuestViewBase() {
void GuestViewBase::DispatchEventToEmbedder(Event* event) {
scoped_ptr<Event> event_ptr(event);
if (!in_extension()) {
NOTREACHED();
return;
}
if (!attached()) {
pending_events_.push_back(linked_ptr<Event>(event_ptr.release()));
......
......@@ -210,10 +210,6 @@ void EventBindings::AttachFilteredEvent(
return;
std::string extension_id = context()->GetExtensionID();
if (extension_id.empty()) {
args.GetReturnValue().Set(static_cast<int32_t>(-1));
return;
}
scoped_ptr<base::DictionaryValue> filter;
scoped_ptr<content::V8ValueConverter> converter(
......@@ -256,8 +252,6 @@ void EventBindings::DetachFilteredEvent(
bool is_manual = args[1]->BooleanValue();
std::string extension_id = context()->GetExtensionID();
if (extension_id.empty())
return;
int matcher_id = args[0]->Int32Value();
EventFilter& event_filter = g_event_filter.Get();
......
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