Commit 7e1d7454 authored by mek's avatar mek Committed by Commit bot

Respect the clipboardRead and clipboardWrite permissions in content scripts.

Added an extra "effective extension" property to ScriptContext for this to still work correctly in about:blank iframes inside extension pages.

BUG=395376

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

Cr-Commit-Position: refs/heads/master@{#293818}
parent 1ce8e56e
...@@ -23,13 +23,11 @@ ...@@ -23,13 +23,11 @@
#include "content/public/browser/render_process_host.h" #include "content/public/browser/render_process_host.h"
#include "extensions/browser/extension_system.h" #include "extensions/browser/extension_system.h"
#include "extensions/common/api/messaging/message.h" #include "extensions/common/api/messaging/message.h"
#include "extensions/common/constants.h"
#include "extensions/common/extension_messages.h" #include "extensions/common/extension_messages.h"
#include "extensions/common/file_util.h" #include "extensions/common/file_util.h"
#include "extensions/common/message_bundle.h" #include "extensions/common/message_bundle.h"
using content::BrowserThread; using content::BrowserThread;
using extensions::APIPermission;
namespace { namespace {
...@@ -88,10 +86,6 @@ bool ChromeExtensionMessageFilter::OnMessageReceived( ...@@ -88,10 +86,6 @@ bool ChromeExtensionMessageFilter::OnMessageReceived(
const IPC::Message& message) { const IPC::Message& message) {
bool handled = true; bool handled = true;
IPC_BEGIN_MESSAGE_MAP(ChromeExtensionMessageFilter, message) IPC_BEGIN_MESSAGE_MAP(ChromeExtensionMessageFilter, message)
IPC_MESSAGE_HANDLER(ChromeViewHostMsg_CanTriggerClipboardRead,
OnCanTriggerClipboardRead)
IPC_MESSAGE_HANDLER(ChromeViewHostMsg_CanTriggerClipboardWrite,
OnCanTriggerClipboardWrite)
IPC_MESSAGE_HANDLER(ExtensionHostMsg_OpenChannelToExtension, IPC_MESSAGE_HANDLER(ExtensionHostMsg_OpenChannelToExtension,
OnOpenChannelToExtension) OnOpenChannelToExtension)
IPC_MESSAGE_HANDLER(ExtensionHostMsg_OpenChannelToTab, OnOpenChannelToTab) IPC_MESSAGE_HANDLER(ExtensionHostMsg_OpenChannelToTab, OnOpenChannelToTab)
...@@ -133,21 +127,6 @@ void ChromeExtensionMessageFilter::OnDestruct() const { ...@@ -133,21 +127,6 @@ void ChromeExtensionMessageFilter::OnDestruct() const {
} }
} }
void ChromeExtensionMessageFilter::OnCanTriggerClipboardRead(
const GURL& origin, bool* allowed) {
*allowed = extension_info_map_->SecurityOriginHasAPIPermission(
origin, render_process_id_, APIPermission::kClipboardRead);
}
void ChromeExtensionMessageFilter::OnCanTriggerClipboardWrite(
const GURL& origin, bool* allowed) {
// Since all extensions could historically write to the clipboard, preserve it
// for compatibility.
*allowed = (origin.SchemeIs(extensions::kExtensionScheme) ||
extension_info_map_->SecurityOriginHasAPIPermission(
origin, render_process_id_, APIPermission::kClipboardWrite));
}
void ChromeExtensionMessageFilter::OnOpenChannelToExtension( void ChromeExtensionMessageFilter::OnOpenChannelToExtension(
int routing_id, int routing_id,
const ExtensionMsg_ExternalConnectionInfo& info, const ExtensionMsg_ExternalConnectionInfo& info,
......
...@@ -47,9 +47,6 @@ class ChromeExtensionMessageFilter : public content::BrowserMessageFilter, ...@@ -47,9 +47,6 @@ class ChromeExtensionMessageFilter : public content::BrowserMessageFilter,
virtual ~ChromeExtensionMessageFilter(); virtual ~ChromeExtensionMessageFilter();
void OnCanTriggerClipboardRead(const GURL& origin, bool* allowed);
void OnCanTriggerClipboardWrite(const GURL& origin, bool* allowed);
// TODO(jamescook): Move these functions into the extensions module. Ideally // TODO(jamescook): Move these functions into the extensions module. Ideally
// this would be in extensions::ExtensionMessageFilter but that will require // this would be in extensions::ExtensionMessageFilter but that will require
// resolving the MessageService and ActivityLog dependencies on src/chrome. // resolving the MessageService and ActivityLog dependencies on src/chrome.
......
...@@ -71,14 +71,6 @@ IPC_MESSAGE_ROUTED4(ExtensionMsg_InlineWebstoreInstallResponse, ...@@ -71,14 +71,6 @@ IPC_MESSAGE_ROUTED4(ExtensionMsg_InlineWebstoreInstallResponse,
// Messages sent from the renderer to the browser. // Messages sent from the renderer to the browser.
// Sent by the renderer to check if a URL has permission to trigger a clipboard
// read/write operation from the DOM.
IPC_SYNC_MESSAGE_CONTROL1_1(ChromeViewHostMsg_CanTriggerClipboardRead,
GURL /* origin */,
bool /* allowed */)
IPC_SYNC_MESSAGE_CONTROL1_1(ChromeViewHostMsg_CanTriggerClipboardWrite,
GURL /* origin */,
bool /* allowed */)
// Sent by the renderer to implement chrome.webstore.install(). // Sent by the renderer to implement chrome.webstore.install().
IPC_MESSAGE_ROUTED5(ExtensionHostMsg_InlineWebstoreInstall, IPC_MESSAGE_ROUTED5(ExtensionHostMsg_InlineWebstoreInstall,
......
...@@ -26,6 +26,8 @@ ...@@ -26,6 +26,8 @@
#include "chrome/common/extensions/chrome_extension_messages.h" #include "chrome/common/extensions/chrome_extension_messages.h"
#include "extensions/common/constants.h" #include "extensions/common/constants.h"
#include "extensions/common/extension.h" #include "extensions/common/extension.h"
#include "extensions/common/permissions/api_permission.h"
#include "extensions/common/permissions/permissions_data.h"
#include "extensions/renderer/dispatcher.h" #include "extensions/renderer/dispatcher.h"
#endif #endif
...@@ -410,10 +412,15 @@ bool ContentSettingsObserver::allowStorage(bool local) { ...@@ -410,10 +412,15 @@ bool ContentSettingsObserver::allowStorage(bool local) {
bool ContentSettingsObserver::allowReadFromClipboard(bool default_value) { bool ContentSettingsObserver::allowReadFromClipboard(bool default_value) {
bool allowed = false; bool allowed = false;
#if defined(ENABLE_EXTENSIONS) #if defined(ENABLE_EXTENSIONS)
WebFrame* frame = render_frame()->GetWebFrame(); extensions::ScriptContext* calling_context =
// TODO(dcheng): Should we consider a toURL() method on WebSecurityOrigin? extension_dispatcher_->script_context_set().GetCalling();
Send(new ChromeViewHostMsg_CanTriggerClipboardRead( if (calling_context) {
GURL(frame->document().securityOrigin().toString()), &allowed)); const extensions::Extension* extension =
calling_context->effective_extension();
allowed = extension &&
extension->permissions_data()->HasAPIPermission(
extensions::APIPermission::kClipboardRead);
}
#endif #endif
return allowed; return allowed;
} }
...@@ -421,9 +428,22 @@ bool ContentSettingsObserver::allowReadFromClipboard(bool default_value) { ...@@ -421,9 +428,22 @@ bool ContentSettingsObserver::allowReadFromClipboard(bool default_value) {
bool ContentSettingsObserver::allowWriteToClipboard(bool default_value) { bool ContentSettingsObserver::allowWriteToClipboard(bool default_value) {
bool allowed = false; bool allowed = false;
#if defined(ENABLE_EXTENSIONS) #if defined(ENABLE_EXTENSIONS)
WebFrame* frame = render_frame()->GetWebFrame(); // All blessed extension pages could historically write to the clipboard, so
Send(new ChromeViewHostMsg_CanTriggerClipboardWrite( // preserve that for compatibility.
GURL(frame->document().securityOrigin().toString()), &allowed)); extensions::ScriptContext* calling_context =
extension_dispatcher_->script_context_set().GetCalling();
if (calling_context) {
if (calling_context->effective_context_type() ==
extensions::Feature::BLESSED_EXTENSION_CONTEXT) {
allowed = true;
} else {
const extensions::Extension* extension =
calling_context->effective_extension();
allowed = extension &&
extension->permissions_data()->HasAPIPermission(
extensions::APIPermission::kClipboardWrite);
}
}
#endif #endif
return allowed; return allowed;
} }
......
...@@ -111,7 +111,6 @@ class ContentSettingsObserver ...@@ -111,7 +111,6 @@ class ContentSettingsObserver
// Otherwise returns NULL. // Otherwise returns NULL.
const extensions::Extension* GetExtension( const extensions::Extension* GetExtension(
const blink::WebSecurityOrigin& origin) const; const blink::WebSecurityOrigin& origin) const;
#endif #endif
// Helpers. // Helpers.
......
...@@ -63,9 +63,16 @@ ChromeExtensionsDispatcherDelegate::CreateScriptContext( ...@@ -63,9 +63,16 @@ ChromeExtensionsDispatcherDelegate::CreateScriptContext(
const v8::Handle<v8::Context>& v8_context, const v8::Handle<v8::Context>& v8_context,
blink::WebFrame* frame, blink::WebFrame* frame,
const extensions::Extension* extension, const extensions::Extension* extension,
extensions::Feature::Context context_type) { extensions::Feature::Context context_type,
return scoped_ptr<extensions::ScriptContext>(new extensions::ChromeV8Context( const extensions::Extension* effective_extension,
v8_context, frame, extension, context_type)); extensions::Feature::Context effective_context_type) {
return scoped_ptr<extensions::ScriptContext>(
new extensions::ChromeV8Context(v8_context,
frame,
extension,
context_type,
effective_extension,
effective_context_type));
} }
void ChromeExtensionsDispatcherDelegate::InitOriginPermissions( void ChromeExtensionsDispatcherDelegate::InitOriginPermissions(
......
...@@ -21,7 +21,9 @@ class ChromeExtensionsDispatcherDelegate ...@@ -21,7 +21,9 @@ class ChromeExtensionsDispatcherDelegate
const v8::Handle<v8::Context>& v8_context, const v8::Handle<v8::Context>& v8_context,
blink::WebFrame* frame, blink::WebFrame* frame,
const extensions::Extension* extension, const extensions::Extension* extension,
extensions::Feature::Context context_type) OVERRIDE; extensions::Feature::Context context_type,
const extensions::Extension* effective_extension,
extensions::Feature::Context effective_context_type) OVERRIDE;
virtual void InitOriginPermissions(const extensions::Extension* extension, virtual void InitOriginPermissions(const extensions::Extension* extension,
bool is_extension_active) OVERRIDE; bool is_extension_active) OVERRIDE;
virtual void RegisterNativeHandlers( virtual void RegisterNativeHandlers(
......
...@@ -9,8 +9,15 @@ namespace extensions { ...@@ -9,8 +9,15 @@ namespace extensions {
ChromeV8Context::ChromeV8Context(const v8::Handle<v8::Context>& v8_context, ChromeV8Context::ChromeV8Context(const v8::Handle<v8::Context>& v8_context,
blink::WebFrame* web_frame, blink::WebFrame* web_frame,
const Extension* extension, const Extension* extension,
Feature::Context context_type) Feature::Context context_type,
: ScriptContext(v8_context, web_frame, extension, context_type) { const Extension* effective_extension,
Feature::Context effective_context_type)
: ScriptContext(v8_context,
web_frame,
extension,
context_type,
effective_extension,
effective_context_type) {
} }
} // namespace extensions } // namespace extensions
...@@ -34,7 +34,9 @@ class ChromeV8Context : public ScriptContext { ...@@ -34,7 +34,9 @@ class ChromeV8Context : public ScriptContext {
ChromeV8Context(const v8::Handle<v8::Context>& context, ChromeV8Context(const v8::Handle<v8::Context>& context,
blink::WebFrame* frame, blink::WebFrame* frame,
const Extension* extension, const Extension* extension,
Feature::Context context_type); Feature::Context context_type,
const Extension* effective_extension,
Feature::Context effective_context_type);
private: private:
DISALLOW_COPY_AND_ASSIGN(ChromeV8Context); DISALLOW_COPY_AND_ASSIGN(ChromeV8Context);
......
// 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.
// TODO(kalman): Consolidate this test script with the other clipboard tests.
function appendTextarea() {
return document.body.appendChild(document.createElement('textarea'));
}
function run() {
var textIn = appendTextarea();
textIn.focus();
textIn.value = 'foobar';
textIn.selectionStart = 0;
textIn.selectionEnd = 'foobar'.length;
if (!document.execCommand('copy'))
return 'Failed to copy';
var textOut = appendTextarea();
textOut.focus();
if (!document.execCommand('paste'))
return 'Failed to paste';
if (textOut.value != 'foobar')
return 'Expected "foobar", got ' + textOut.value;
return '';
}
chrome.runtime.onMessage.addListener(function(message, sender, sendResponse) {
sendResponse(run());
});
...@@ -6,5 +6,11 @@ ...@@ -6,5 +6,11 @@
"background": { "background": {
"page": "test.html" "page": "test.html"
}, },
"permissions": ["clipboardRead"] "permissions": ["clipboardRead", "clipboardWrite", "http://localhost/*"],
"content_scripts": [
{
"matches": ["http://*/*test_file_with_body.html"],
"js": ["content_script.js"]
}
]
} }
...@@ -5,33 +5,35 @@ ...@@ -5,33 +5,35 @@
// Clipboard permission test for Chrome. // Clipboard permission test for Chrome.
// browser_tests.exe --gtest_filter=ClipboardApiTest.Extension // browser_tests.exe --gtest_filter=ClipboardApiTest.Extension
chrome.test.runTests([ // TODO(kalman): Consolidate this test script with the other clipboard tests.
function domCopy() {
if (document.execCommand('copy')) function testDomCopy() {
chrome.test.succeed(); if (document.execCommand('copy'))
else chrome.test.succeed();
chrome.test.fail('execCommand("copy") failed'); else
}, chrome.test.fail('execCommand("copy") failed');
function domPaste() { }
if (document.execCommand('paste'))
chrome.test.succeed(); function testDomPaste() {
else if (document.execCommand('paste'))
chrome.test.fail('execCommand("paste") failed'); chrome.test.succeed();
}, else
function copyInIframe() { chrome.test.fail('execCommand("paste") failed');
var ifr = document.createElement('iframe'); }
document.body.appendChild(ifr);
window.command = 'copy';
ifr.contentDocument.write('<script src="iframe.js"></script>');
},
function pasteInIframe() {
var ifr = document.createElement('iframe');
document.body.appendChild(ifr);
window.command = 'paste';
ifr.contentDocument.write('<script src="iframe.js"></script>');
}
]);
function testCopyInIframe() {
var ifr = document.createElement('iframe');
document.body.appendChild(ifr);
window.command = 'copy';
ifr.contentDocument.write('<script src="iframe.js"></script>');
}
function testPasteInIframe() {
var ifr = document.createElement('iframe');
document.body.appendChild(ifr);
window.command = 'paste';
ifr.contentDocument.write('<script src="iframe.js"></script>');
}
function testDone(result) { function testDone(result) {
if (result) if (result)
...@@ -39,3 +41,66 @@ function testDone(result) { ...@@ -39,3 +41,66 @@ function testDone(result) {
else else
chrome.test.fail(); chrome.test.fail();
} }
function testExecuteScriptCopyPaste(baseUrl) {
var tabUrl = baseUrl + '/test_file.html';
function runScript(tabId) {
chrome.tabs.executeScript(tabId, {file: 'content_script.js'},
chrome.test.callbackPass(function() {
chrome.tabs.sendMessage(tabId, "run",
chrome.test.callbackPass(function(result) {
chrome.tabs.remove(tabId);
chrome.test.assertEq('', result);
}));
}));
}
chrome.tabs.create({url: tabUrl}, chrome.test.callbackPass(function(newTab) {
var done = chrome.test.listenForever(chrome.tabs.onUpdated,
function(_, info, updatedTab) {
if (updatedTab.id == newTab.id && info.status == 'complete') {
runScript(newTab.id);
done();
}
});
}));
}
function testContentScriptCopyPaste(baseUrl) {
var tabUrl = baseUrl + '/test_file_with_body.html';
function runScript(tabId) {
chrome.tabs.sendMessage(tabId, "run",
chrome.test.callbackPass(function(result) {
chrome.tabs.remove(tabId);
chrome.test.assertEq('', result);
}));
}
chrome.tabs.create({url: tabUrl}, chrome.test.callbackPass(function(newTab) {
var done = chrome.test.listenForever(chrome.tabs.onUpdated,
function(_, info, updatedTab) {
if (updatedTab.id == newTab.id && info.status == 'complete') {
runScript(newTab.id);
done();
}
});
}));
}
function bindTest(test, param) {
var result = test.bind(null, param);
result.generatedName = test.name;
return result;
}
chrome.test.getConfig(function(config) {
var baseUrl = 'http://localhost:' + config.testServer.port + '/extensions';
chrome.test.runTests([
testDomCopy,
testDomPaste,
testCopyInIframe,
testPasteInIframe,
bindTest(testExecuteScriptCopyPaste, baseUrl),
bindTest(testContentScriptCopyPaste, baseUrl)
]);
})
// 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.
// TODO(kalman): Consolidate this test script with the other clipboard tests.
function appendTextarea() {
return document.body.appendChild(document.createElement('textarea'));
}
function run() {
var textIn = appendTextarea();
textIn.focus();
textIn.value = 'foobar';
textIn.selectionStart = 0;
textIn.selectionEnd = 'foobar'.length;
if (document.execCommand('copy'))
return 'Succeeded to copy';
var textOut = appendTextarea();
textOut.focus();
if (document.execCommand('paste'))
return 'Succeeded to paste';
if (textOut.value == 'foobar')
return 'Successfully copied/pasted despite execCommand failures';
return '';
}
chrome.runtime.onMessage.addListener(function(message, sender, sendResponse) {
sendResponse(run());
});
...@@ -5,5 +5,12 @@ ...@@ -5,5 +5,12 @@
"description": "end-to-end browser test for clipboard permissions", "description": "end-to-end browser test for clipboard permissions",
"background": { "background": {
"page": "test.html" "page": "test.html"
} },
"permissions": ["http://localhost/*"],
"content_scripts": [
{
"matches": ["http://*/*test_file_with_body.html"],
"js": ["content_script.js"]
}
]
} }
...@@ -5,32 +5,37 @@ ...@@ -5,32 +5,37 @@
// Clipboard permission test for Chrome. // Clipboard permission test for Chrome.
// browser_tests.exe --gtest_filter=ClipboardApiTest.ExtensionNoPermission // browser_tests.exe --gtest_filter=ClipboardApiTest.ExtensionNoPermission
chrome.test.runTests([ // TODO(kalman): Consolidate this test script with the other clipboard tests.
function domCopy() {
if (document.execCommand('copy')) var pass = chrome.test.callbackPass;
chrome.test.succeed();
else function testDomCopy() {
chrome.test.fail('execCommand("copy") failed'); if (document.execCommand('copy'))
}, chrome.test.succeed();
function domPaste() { else
if (!document.execCommand('paste')) chrome.test.fail('execCommand("copy") failed');
chrome.test.succeed(); }
else
chrome.test.fail('execCommand("paste") succeeded'); function testDomPaste() {
}, if (document.execCommand('paste'))
function copyInIframe() { chrome.test.fail('execCommand("paste") succeeded');
var ifr = document.createElement('iframe'); else
document.body.appendChild(ifr); chrome.test.succeed();
window.command = 'copy'; }
ifr.contentDocument.write('<script src="iframe.js"></script>');
}, function testCopyInIframe() {
function pasteInIframe() { var ifr = document.createElement('iframe');
var ifr = document.createElement('iframe'); document.body.appendChild(ifr);
document.body.appendChild(ifr); window.command = 'copy';
window.command = 'paste'; ifr.contentDocument.write('<script src="iframe.js"></script>');
ifr.contentDocument.write('<script src="iframe.js"></script>'); }
}
]); function testPasteInIframe() {
var ifr = document.createElement('iframe');
document.body.appendChild(ifr);
window.command = 'paste';
ifr.contentDocument.write('<script src="iframe.js"></script>');
}
function testDone(result) { function testDone(result) {
// 'copy' should always succeed regardless of the clipboardWrite permission, // 'copy' should always succeed regardless of the clipboardWrite permission,
...@@ -42,3 +47,66 @@ function testDone(result) { ...@@ -42,3 +47,66 @@ function testDone(result) {
else else
chrome.test.fail(); chrome.test.fail();
} }
function testExecuteScriptCopyPaste(baseUrl) {
var tabUrl = baseUrl + '/test_file.html';
function runScript(tabId) {
chrome.tabs.executeScript(tabId, {file: 'content_script.js'},
chrome.test.callbackPass(function() {
chrome.tabs.sendMessage(tabId, "run",
chrome.test.callbackPass(function(result) {
chrome.tabs.remove(tabId);
chrome.test.assertEq('', result);
}));
}));
}
chrome.tabs.create({url: tabUrl}, pass(function(newTab) {
var done = chrome.test.listenForever(chrome.tabs.onUpdated,
function(_, info, updatedTab) {
if (updatedTab.id == newTab.id && info.status == 'complete') {
runScript(newTab.id);
done();
}
});
}));
}
function testContentScriptCopyPaste(baseUrl) {
var tabUrl = baseUrl + '/test_file_with_body.html';
function runScript(tabId) {
chrome.tabs.sendMessage(tabId, "run",
chrome.test.callbackPass(function(result) {
chrome.tabs.remove(tabId);
chrome.test.assertEq('', result);
}));
}
chrome.tabs.create({url: tabUrl}, chrome.test.callbackPass(function(newTab) {
var done = chrome.test.listenForever(chrome.tabs.onUpdated,
function(_, info, updatedTab) {
if (updatedTab.id == newTab.id && info.status == 'complete') {
runScript(newTab.id);
done();
}
});
}));
}
function bindTest(test, param) {
var result = test.bind(null, param);
result.generatedName = test.name;
return result;
}
chrome.test.getConfig(function(config) {
var baseUrl = 'http://localhost:' + config.testServer.port + '/extensions';
chrome.test.runTests([
testDomCopy,
testDomPaste,
testCopyInIframe,
testPasteInIframe,
bindTest(testExecuteScriptCopyPaste, baseUrl),
bindTest(testContentScriptCopyPaste, baseUrl)
]);
});
...@@ -19,9 +19,15 @@ scoped_ptr<ScriptContext> DefaultDispatcherDelegate::CreateScriptContext( ...@@ -19,9 +19,15 @@ scoped_ptr<ScriptContext> DefaultDispatcherDelegate::CreateScriptContext(
const v8::Handle<v8::Context>& v8_context, const v8::Handle<v8::Context>& v8_context,
blink::WebFrame* frame, blink::WebFrame* frame,
const Extension* extension, const Extension* extension,
Feature::Context context_type) { Feature::Context context_type,
return make_scoped_ptr( const Extension* effective_extension,
new ScriptContext(v8_context, frame, extension, context_type)); Feature::Context effective_context_type) {
return make_scoped_ptr(new ScriptContext(v8_context,
frame,
extension,
context_type,
effective_extension,
effective_context_type));
} }
} // namespace extensions } // namespace extensions
...@@ -19,7 +19,9 @@ class DefaultDispatcherDelegate : public DispatcherDelegate { ...@@ -19,7 +19,9 @@ class DefaultDispatcherDelegate : public DispatcherDelegate {
const v8::Handle<v8::Context>& v8_context, const v8::Handle<v8::Context>& v8_context,
blink::WebFrame* frame, blink::WebFrame* frame,
const Extension* extension, const Extension* extension,
Feature::Context context_type) OVERRIDE; Feature::Context context_type,
const Extension* effective_extension,
Feature::Context effective_context_type) OVERRIDE;
}; };
} // namespace extensions } // namespace extensions
......
...@@ -220,32 +220,24 @@ bool Dispatcher::IsExtensionActive(const std::string& extension_id) const { ...@@ -220,32 +220,24 @@ bool Dispatcher::IsExtensionActive(const std::string& extension_id) const {
return is_active; return is_active;
} }
std::string Dispatcher::GetExtensionID(const WebFrame* frame, int world_id) { const Extension* Dispatcher::GetExtensionFromFrameAndWorld(
const WebFrame* frame,
int world_id,
bool use_effective_url) {
std::string extension_id;
if (world_id != 0) { if (world_id != 0) {
// Isolated worlds (content script). // Isolated worlds (content script).
return ScriptInjection::GetExtensionIdForIsolatedWorld(world_id); extension_id = ScriptInjection::GetExtensionIdForIsolatedWorld(world_id);
} else if (!frame->document().securityOrigin().isUnique()) {
// TODO(kalman): Delete the above check.
// Extension pages (chrome-extension:// URLs).
GURL frame_url = ScriptContext::GetDataSourceURLForFrame(frame);
frame_url = ScriptContext::GetEffectiveDocumentURL(
frame, frame_url, use_effective_url);
extension_id = extensions_.GetExtensionOrAppIDByURL(frame_url);
} }
// TODO(kalman): Delete this check.
if (frame->document().securityOrigin().isUnique())
return std::string();
// Extension pages (chrome-extension:// URLs).
GURL frame_url = ScriptContext::GetDataSourceURLForFrame(frame);
return extensions_.GetExtensionOrAppIDByURL(frame_url);
}
void Dispatcher::DidCreateScriptContext(
WebFrame* frame,
const v8::Handle<v8::Context>& v8_context,
int extension_group,
int world_id) {
#if !defined(ENABLE_EXTENSIONS)
return;
#endif
std::string extension_id = GetExtensionID(frame, world_id);
const Extension* extension = extensions_.GetByID(extension_id); const Extension* extension = extensions_.GetByID(extension_id);
if (!extension && !extension_id.empty()) { if (!extension && !extension_id.empty()) {
// There are conditions where despite a context being associated with an // There are conditions where despite a context being associated with an
...@@ -257,19 +249,43 @@ void Dispatcher::DidCreateScriptContext( ...@@ -257,19 +249,43 @@ void Dispatcher::DidCreateScriptContext(
RenderThread::Get()->RecordAction( RenderThread::Get()->RecordAction(
UserMetricsAction("ExtensionNotFound_ED")); UserMetricsAction("ExtensionNotFound_ED"));
} }
extension_id = "";
} }
return extension;
}
void Dispatcher::DidCreateScriptContext(
WebFrame* frame,
const v8::Handle<v8::Context>& v8_context,
int extension_group,
int world_id) {
#if !defined(ENABLE_EXTENSIONS)
return;
#endif
const Extension* extension =
GetExtensionFromFrameAndWorld(frame, world_id, false);
const Extension* effective_extension =
GetExtensionFromFrameAndWorld(frame, world_id, true);
GURL frame_url = ScriptContext::GetDataSourceURLForFrame(frame);
Feature::Context context_type = Feature::Context context_type =
ClassifyJavaScriptContext(extension, ClassifyJavaScriptContext(extension,
extension_group, extension_group,
ScriptContext::GetDataSourceURLForFrame(frame), frame_url,
frame->document().securityOrigin()); frame->document().securityOrigin());
Feature::Context effective_context_type = ClassifyJavaScriptContext(
effective_extension,
extension_group,
ScriptContext::GetEffectiveDocumentURL(frame, frame_url, true),
frame->document().securityOrigin());
ScriptContext* context = ScriptContext* context =
delegate_->CreateScriptContext(v8_context, frame, extension, context_type) delegate_->CreateScriptContext(v8_context,
.release(); frame,
extension,
context_type,
effective_extension,
effective_context_type).release();
script_context_set_.Add(context); script_context_set_.Add(context);
// Initialize origin permissions for content scripts, which can't be // Initialize origin permissions for content scripts, which can't be
......
...@@ -90,11 +90,13 @@ class Dispatcher : public content::RenderProcessObserver, ...@@ -90,11 +90,13 @@ class Dispatcher : public content::RenderProcessObserver,
bool IsExtensionActive(const std::string& extension_id) const; bool IsExtensionActive(const std::string& extension_id) const;
// Finds the extension ID for the JavaScript context associated with the // Finds the extension for the JavaScript context associated with the
// specified |frame| and isolated world. If |world_id| is zero, finds the // specified |frame| and isolated world. If |world_id| is zero, finds the
// extension ID associated with the main world's JavaScript context. If the // extension ID associated with the main world's JavaScript context. If the
// JavaScript context isn't from an extension, returns empty string. // JavaScript context isn't from an extension, returns empty string.
std::string GetExtensionID(const blink::WebFrame* frame, int world_id); const Extension* GetExtensionFromFrameAndWorld(const blink::WebFrame* frame,
int world_id,
bool use_effective_url);
void DidCreateScriptContext(blink::WebFrame* frame, void DidCreateScriptContext(blink::WebFrame* frame,
const v8::Handle<v8::Context>& context, const v8::Handle<v8::Context>& context,
......
...@@ -36,7 +36,9 @@ class DispatcherDelegate { ...@@ -36,7 +36,9 @@ class DispatcherDelegate {
const v8::Handle<v8::Context>& v8_context, const v8::Handle<v8::Context>& v8_context,
blink::WebFrame* frame, blink::WebFrame* frame,
const Extension* extension, const Extension* extension,
Feature::Context context_type) = 0; Feature::Context context_type,
const Extension* effective_extension,
Feature::Context effective_context_type) = 0;
// Initializes origin permissions for a newly created extension context. // Initializes origin permissions for a newly created extension context.
virtual void InitOriginPermissions(const Extension* extension, virtual void InitOriginPermissions(const Extension* extension,
......
...@@ -131,6 +131,8 @@ ModuleSystemTestEnvironment::ModuleSystemTestEnvironment(v8::Isolate* isolate) ...@@ -131,6 +131,8 @@ ModuleSystemTestEnvironment::ModuleSystemTestEnvironment(v8::Isolate* isolate)
context_.reset(new ScriptContext(context_holder_->context(), context_.reset(new ScriptContext(context_holder_->context(),
NULL, // WebFrame NULL, // WebFrame
NULL, // Extension NULL, // Extension
Feature::BLESSED_EXTENSION_CONTEXT,
NULL, // Effective Extension
Feature::BLESSED_EXTENSION_CONTEXT)); Feature::BLESSED_EXTENSION_CONTEXT));
context_->v8_context()->Enter(); context_->v8_context()->Enter();
assert_natives_ = new AssertNatives(context_.get()); assert_natives_ = new AssertNatives(context_.get());
......
...@@ -30,27 +30,62 @@ using content::V8ValueConverter; ...@@ -30,27 +30,62 @@ using content::V8ValueConverter;
namespace extensions { namespace extensions {
namespace {
std::string GetContextTypeDescriptionString(Feature::Context context_type) {
switch (context_type) {
case Feature::UNSPECIFIED_CONTEXT:
return "UNSPECIFIED";
case Feature::BLESSED_EXTENSION_CONTEXT:
return "BLESSED_EXTENSION";
case Feature::UNBLESSED_EXTENSION_CONTEXT:
return "UNBLESSED_EXTENSION";
case Feature::CONTENT_SCRIPT_CONTEXT:
return "CONTENT_SCRIPT";
case Feature::WEB_PAGE_CONTEXT:
return "WEB_PAGE";
case Feature::BLESSED_WEB_PAGE_CONTEXT:
return "BLESSED_WEB_PAGE";
case Feature::WEBUI_CONTEXT:
return "WEBUI";
}
NOTREACHED();
return std::string();
}
} // namespace
ScriptContext::ScriptContext(const v8::Handle<v8::Context>& v8_context, ScriptContext::ScriptContext(const v8::Handle<v8::Context>& v8_context,
blink::WebFrame* web_frame, blink::WebFrame* web_frame,
const Extension* extension, const Extension* extension,
Feature::Context context_type) Feature::Context context_type,
const Extension* effective_extension,
Feature::Context effective_context_type)
: v8_context_(v8_context), : v8_context_(v8_context),
web_frame_(web_frame), web_frame_(web_frame),
extension_(extension), extension_(extension),
context_type_(context_type), context_type_(context_type),
effective_extension_(effective_extension),
effective_context_type_(effective_context_type),
safe_builtins_(this), safe_builtins_(this),
isolate_(v8_context->GetIsolate()) { isolate_(v8_context->GetIsolate()) {
VLOG(1) << "Created context:\n" VLOG(1) << "Created context:\n"
<< " extension id: " << GetExtensionID() << "\n" << " extension id: " << GetExtensionID() << "\n"
<< " frame: " << web_frame_ << "\n" << " frame: " << web_frame_ << "\n"
<< " URL: " << GetURL() << "\n" << " URL: " << GetURL() << "\n"
<< " context type: " << GetContextTypeDescription(); << " context type: " << GetContextTypeDescription() << "\n"
<< " effective extension id: "
<< (effective_extension_.get() ? effective_extension_->id() : "")
<< " effective context type: "
<< GetEffectiveContextTypeDescription();
gin::PerContextData::From(v8_context)->set_runner(this); gin::PerContextData::From(v8_context)->set_runner(this);
} }
ScriptContext::~ScriptContext() { ScriptContext::~ScriptContext() {
VLOG(1) << "Destroyed context for extension\n" VLOG(1) << "Destroyed context for extension\n"
<< " extension id: " << GetExtensionID(); << " extension id: " << GetExtensionID() << "\n"
<< " effective extension id: "
<< (effective_extension_.get() ? effective_extension_->id() : "");
Invalidate(); Invalidate();
} }
...@@ -133,24 +168,11 @@ void ScriptContext::DispatchOnUnloadEvent() { ...@@ -133,24 +168,11 @@ void ScriptContext::DispatchOnUnloadEvent() {
} }
std::string ScriptContext::GetContextTypeDescription() { std::string ScriptContext::GetContextTypeDescription() {
switch (context_type_) { return GetContextTypeDescriptionString(context_type_);
case Feature::UNSPECIFIED_CONTEXT: }
return "UNSPECIFIED";
case Feature::BLESSED_EXTENSION_CONTEXT: std::string ScriptContext::GetEffectiveContextTypeDescription() {
return "BLESSED_EXTENSION"; return GetContextTypeDescriptionString(effective_context_type_);
case Feature::UNBLESSED_EXTENSION_CONTEXT:
return "UNBLESSED_EXTENSION";
case Feature::CONTENT_SCRIPT_CONTEXT:
return "CONTENT_SCRIPT";
case Feature::WEB_PAGE_CONTEXT:
return "WEB_PAGE";
case Feature::BLESSED_WEB_PAGE_CONTEXT:
return "BLESSED_WEB_PAGE";
case Feature::WEBUI_CONTEXT:
return "WEBUI";
}
NOTREACHED();
return std::string();
} }
GURL ScriptContext::GetURL() const { GURL ScriptContext::GetURL() const {
......
...@@ -35,7 +35,9 @@ class ScriptContext : public RequestSender::Source, public gin::Runner { ...@@ -35,7 +35,9 @@ class ScriptContext : public RequestSender::Source, public gin::Runner {
ScriptContext(const v8::Handle<v8::Context>& context, ScriptContext(const v8::Handle<v8::Context>& context,
blink::WebFrame* frame, blink::WebFrame* frame,
const Extension* extension, const Extension* extension,
Feature::Context context_type); Feature::Context context_type,
const Extension* effective_extension,
Feature::Context effective_context_type);
virtual ~ScriptContext(); virtual ~ScriptContext();
// Clears the WebFrame for this contexts and invalidates the associated // Clears the WebFrame for this contexts and invalidates the associated
...@@ -52,10 +54,18 @@ class ScriptContext : public RequestSender::Source, public gin::Runner { ...@@ -52,10 +54,18 @@ class ScriptContext : public RequestSender::Source, public gin::Runner {
const Extension* extension() const { return extension_.get(); } const Extension* extension() const { return extension_.get(); }
const Extension* effective_extension() const {
return effective_extension_.get();
}
blink::WebFrame* web_frame() const { return web_frame_; } blink::WebFrame* web_frame() const { return web_frame_; }
Feature::Context context_type() const { return context_type_; } Feature::Context context_type() const { return context_type_; }
Feature::Context effective_context_type() const {
return effective_context_type_;
}
void set_module_system(scoped_ptr<ModuleSystem> module_system) { void set_module_system(scoped_ptr<ModuleSystem> module_system) {
module_system_ = module_system.Pass(); module_system_ = module_system.Pass();
} }
...@@ -97,6 +107,9 @@ class ScriptContext : public RequestSender::Source, public gin::Runner { ...@@ -97,6 +107,9 @@ class ScriptContext : public RequestSender::Source, public gin::Runner {
// Returns a string description of the type of context this is. // Returns a string description of the type of context this is.
std::string GetContextTypeDescription(); std::string GetContextTypeDescription();
// Returns a string description of the effective type of context this is.
std::string GetEffectiveContextTypeDescription();
v8::Isolate* isolate() const { return isolate_; } v8::Isolate* isolate() const { return isolate_; }
// Get the URL of this context's web frame. // Get the URL of this context's web frame.
...@@ -152,6 +165,14 @@ class ScriptContext : public RequestSender::Source, public gin::Runner { ...@@ -152,6 +165,14 @@ class ScriptContext : public RequestSender::Source, public gin::Runner {
// The type of context. // The type of context.
Feature::Context context_type_; Feature::Context context_type_;
// The effective extension associated with this context, or NULL if there is
// none. This is different from the above extension if this context is in an
// about:blank iframe for example.
scoped_refptr<const Extension> effective_extension_;
// The type of context.
Feature::Context effective_context_type_;
// Owns and structures the JS that is injected to set up extension bindings. // Owns and structures the JS that is injected to set up extension bindings.
scoped_ptr<ModuleSystem> module_system_; scoped_ptr<ModuleSystem> module_system_;
......
...@@ -32,6 +32,8 @@ TEST(ScriptContextSet, Lifecycle) { ...@@ -32,6 +32,8 @@ TEST(ScriptContextSet, Lifecycle) {
new ScriptContext(context_holder.context(), new ScriptContext(context_holder.context(),
frame, frame,
extension, extension,
Feature::BLESSED_EXTENSION_CONTEXT,
extension,
Feature::BLESSED_EXTENSION_CONTEXT); Feature::BLESSED_EXTENSION_CONTEXT);
context_set.Add(context); context_set.Add(context);
......
...@@ -52,6 +52,8 @@ scoped_ptr<NativeHandler> V8SchemaRegistry::AsNativeHandler() { ...@@ -52,6 +52,8 @@ scoped_ptr<NativeHandler> V8SchemaRegistry::AsNativeHandler() {
new ScriptContext(GetOrCreateContext(v8::Isolate::GetCurrent()), new ScriptContext(GetOrCreateContext(v8::Isolate::GetCurrent()),
NULL, // no frame NULL, // no frame
NULL, // no extension NULL, // no extension
Feature::UNSPECIFIED_CONTEXT,
NULL, // no effective extension
Feature::UNSPECIFIED_CONTEXT)); Feature::UNSPECIFIED_CONTEXT));
return scoped_ptr<NativeHandler>( return scoped_ptr<NativeHandler>(
new SchemaRegistryNativeHandler(this, context.Pass())); new SchemaRegistryNativeHandler(this, context.Pass()));
......
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