summaryrefslogtreecommitdiffstats
path: root/extensions
diff options
context:
space:
mode:
authorkalman <kalman@chromium.org>2015-03-16 12:16:26 -0700
committerCommit bot <commit-bot@chromium.org>2015-03-16 19:17:26 +0000
commit52f4caf6d01302ebf42136b42fa77d1c67d93ae9 (patch)
treed4c6f0324759cb3463fd752a368bd3392b157b7d /extensions
parent67ad64c997cd2207782a965a7aaf26a4f5109653 (diff)
downloadchromium_src-52f4caf6d01302ebf42136b42fa77d1c67d93ae9.zip
chromium_src-52f4caf6d01302ebf42136b42fa77d1c67d93ae9.tar.gz
chromium_src-52f4caf6d01302ebf42136b42fa77d1c67d93ae9.tar.bz2
Move Extension ScriptContext creation into ScriptContextSet.
Previously extensions::Dispatcher was responsible for creating ScriptContexts then Adding them to its ScriptContextSet. Now, ScriptContextSet creates them itself. This prepares the Extension system for later changing to lazily or asynchronously creating ScriptContexts. BUG=466373 R=rockot@chromium.org Committed: https://crrev.com/75605c7fca774b95984ba67f70ba86bb5750c0f6 Cr-Commit-Position: refs/heads/master@{#320602} Review URL: https://codereview.chromium.org/995283004 Cr-Commit-Position: refs/heads/master@{#320770}
Diffstat (limited to 'extensions')
-rw-r--r--extensions/renderer/dispatcher.cc183
-rw-r--r--extensions/renderer/dispatcher.h26
-rw-r--r--extensions/renderer/document_custom_bindings.cc2
-rw-r--r--extensions/renderer/request_sender.cc4
-rw-r--r--extensions/renderer/script_context.cc21
-rw-r--r--extensions/renderer/script_context.h28
-rw-r--r--extensions/renderer/script_context_set.cc185
-rw-r--r--extensions/renderer/script_context_set.h84
-rw-r--r--extensions/renderer/script_context_set_unittest.cc60
9 files changed, 306 insertions, 287 deletions
diff --git a/extensions/renderer/dispatcher.cc b/extensions/renderer/dispatcher.cc
index e7b1267..8f37d82 100644
--- a/extensions/renderer/dispatcher.cc
+++ b/extensions/renderer/dispatcher.cc
@@ -24,7 +24,6 @@
#include "content/public/renderer/render_view.h"
#include "extensions/common/api/messaging/message.h"
#include "extensions/common/constants.h"
-#include "extensions/common/extension.h"
#include "extensions/common/extension_api.h"
#include "extensions/common/extension_messages.h"
#include "extensions/common/extension_urls.h"
@@ -37,7 +36,6 @@
#include "extensions/common/manifest_handlers/content_capabilities_handler.h"
#include "extensions/common/manifest_handlers/externally_connectable.h"
#include "extensions/common/manifest_handlers/options_page_info.h"
-#include "extensions/common/manifest_handlers/sandboxed_page_info.h"
#include "extensions/common/message_bundle.h"
#include "extensions/common/permissions/permission_set.h"
#include "extensions/common/permissions/permissions_data.h"
@@ -56,7 +54,6 @@
#include "extensions/renderer/document_custom_bindings.h"
#include "extensions/renderer/dom_activity_logger.h"
#include "extensions/renderer/event_bindings.h"
-#include "extensions/renderer/extension_groups.h"
#include "extensions/renderer/extension_helper.h"
#include "extensions/renderer/extensions_renderer_client.h"
#include "extensions/renderer/file_system_natives.h"
@@ -205,6 +202,8 @@ Dispatcher::Dispatcher(DispatcherDelegate* delegate)
RenderThread::Get()->RegisterExtension(SafeBuiltins::CreateV8Extension());
+ script_context_set_.reset(
+ new ScriptContextSet(&extensions_, &active_extension_ids_));
user_script_set_manager_.reset(new UserScriptSetManager(&extensions_));
script_injection_manager_.reset(
new ScriptInjectionManager(&extensions_, user_script_set_manager_.get()));
@@ -228,39 +227,6 @@ bool Dispatcher::IsExtensionActive(const std::string& extension_id) const {
return is_active;
}
-const Extension* Dispatcher::GetExtensionFromFrameAndWorld(
- const blink::WebFrame* frame,
- int world_id,
- bool use_effective_url) {
- std::string extension_id;
- if (world_id != 0) {
- // Isolated worlds (content script).
- extension_id = ScriptInjection::GetHostIdForIsolatedWorld(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);
- }
-
- const Extension* extension = extensions_.GetByID(extension_id);
- if (!extension && !extension_id.empty()) {
- // There are conditions where despite a context being associated with an
- // extension, no extension actually gets found. Ignore "invalid" because
- // CSP blocks extension page loading by switching the extension ID to
- // "invalid". This isn't interesting.
- if (extension_id != "invalid") {
- LOG(ERROR) << "Extension \"" << extension_id << "\" not found";
- RenderThread::Get()->RecordAction(
- UserMetricsAction("ExtensionNotFound_ED"));
- }
- }
- return extension;
-}
-
void Dispatcher::DidCreateScriptContext(
blink::WebLocalFrame* frame,
const v8::Handle<v8::Context>& v8_context,
@@ -268,32 +234,13 @@ void Dispatcher::DidCreateScriptContext(
int world_id) {
const base::TimeTicks start_time = base::TimeTicks::Now();
- 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 =
- ClassifyJavaScriptContext(extension,
- extension_group,
- frame_url,
- frame->document().securityOrigin());
- Feature::Context effective_context_type = ClassifyJavaScriptContext(
- effective_extension,
- extension_group,
- ScriptContext::GetEffectiveDocumentURL(frame, frame_url, true),
- frame->document().securityOrigin());
-
- ScriptContext* context =
- new ScriptContext(v8_context, frame, extension, context_type,
- effective_extension, effective_context_type);
- script_context_set_.Add(context);
+ ScriptContext* context = script_context_set_->Register(
+ frame, v8_context, extension_group, world_id);
// Initialize origin permissions for content scripts, which can't be
// initialized in |OnActivateExtension|.
- if (context_type == Feature::CONTENT_SCRIPT_CONTEXT)
- InitOriginPermissions(extension);
+ if (context->context_type() == Feature::CONTENT_SCRIPT_CONTEXT)
+ InitOriginPermissions(context->extension());
{
scoped_ptr<ModuleSystem> module_system(
@@ -328,7 +275,7 @@ void Dispatcher::DidCreateScriptContext(
delegate_->RequireAdditionalModules(context, is_within_platform_app);
const base::TimeDelta elapsed = base::TimeTicks::Now() - start_time;
- switch (context_type) {
+ switch (context->context_type()) {
case Feature::UNSPECIFIED_CONTEXT:
UMA_HISTOGRAM_TIMES("Extensions.DidCreateScriptContext_Unspecified",
elapsed);
@@ -356,14 +303,14 @@ void Dispatcher::DidCreateScriptContext(
break;
}
- VLOG(1) << "Num tracked contexts: " << script_context_set_.size();
+ VLOG(1) << "Num tracked contexts: " << script_context_set_->size();
}
void Dispatcher::WillReleaseScriptContext(
blink::WebLocalFrame* frame,
const v8::Handle<v8::Context>& v8_context,
int world_id) {
- ScriptContext* context = script_context_set_.GetByV8Context(v8_context);
+ ScriptContext* context = script_context_set_->GetByV8Context(v8_context);
if (!context)
return;
@@ -371,8 +318,8 @@ void Dispatcher::WillReleaseScriptContext(
// TODO(kalman): add an invalidation observer interface to ScriptContext.
request_sender_->InvalidateSource(context);
- script_context_set_.Remove(context);
- VLOG(1) << "Num tracked contexts: " << script_context_set_.size();
+ script_context_set_->Remove(context);
+ VLOG(1) << "Num tracked contexts: " << script_context_set_->size();
}
void Dispatcher::DidCreateDocumentElement(blink::WebFrame* frame) {
@@ -443,7 +390,8 @@ bool Dispatcher::CheckContextAccessToExtensionAPI(
// Theoretically we could end up with bindings being injected into sandboxed
// frames, for example content scripts. Don't let them execute API functions.
blink::WebFrame* frame = context->web_frame();
- if (IsSandboxedPage(ScriptContext::GetDataSourceURLForFrame(frame))) {
+ if (ScriptContext::IsSandboxedPage(
+ extensions_, ScriptContext::GetDataSourceURLForFrame(frame))) {
static const char kMessage[] =
"%s cannot be used within a sandboxed frame.";
std::string error_msg = base::StringPrintf(kMessage, function_name.c_str());
@@ -470,11 +418,9 @@ void Dispatcher::DispatchEvent(const std::string& extension_id,
// Needed for Windows compilation, since kEventBindings is declared extern.
const char* local_event_bindings = kEventBindings;
- script_context_set_.ForEach(extension_id,
- base::Bind(&CallModuleMethod,
- local_event_bindings,
- kEventDispatchFunction,
- &args));
+ script_context_set_->ForEach(
+ extension_id, base::Bind(&CallModuleMethod, local_event_bindings,
+ kEventDispatchFunction, &args));
}
void Dispatcher::InvokeModuleSystemMethod(content::RenderView* render_view,
@@ -487,9 +433,8 @@ void Dispatcher::InvokeModuleSystemMethod(content::RenderView* render_view,
if (user_gesture)
web_user_gesture.reset(new WebScopedUserGesture);
- script_context_set_.ForEach(
- extension_id,
- render_view,
+ script_context_set_->ForEach(
+ extension_id, render_view,
base::Bind(&CallModuleMethod, module_name, function_name, &args));
// Reset the idle handler each time there's any activity like event or message
@@ -894,8 +839,7 @@ void Dispatcher::OnDeliverMessage(int target_port_id, const Message& message) {
new RequestSender::ScopedTabID(request_sender(), it->second));
}
- MessagingBindings::DeliverMessage(script_context_set_,
- target_port_id,
+ MessagingBindings::DeliverMessage(*script_context_set_, target_port_id,
message,
NULL); // All render frames.
}
@@ -912,19 +856,15 @@ void Dispatcher::OnDispatchOnConnect(
source.tab.GetInteger("id", &sender_tab_id);
port_to_tab_id_map_[target_port_id] = sender_tab_id;
- MessagingBindings::DispatchOnConnect(script_context_set_,
- target_port_id,
- channel_name,
- source,
- info,
+ MessagingBindings::DispatchOnConnect(*script_context_set_, target_port_id,
+ channel_name, source, info,
tls_channel_id,
NULL); // All render frames.
}
void Dispatcher::OnDispatchOnDisconnect(int port_id,
const std::string& error_message) {
- MessagingBindings::DispatchOnDisconnect(script_context_set_,
- port_id,
+ MessagingBindings::DispatchOnDisconnect(*script_context_set_, port_id,
error_message,
NULL); // All render frames.
}
@@ -1014,12 +954,10 @@ void Dispatcher::OnUnloaded(const std::string& id) {
// Invalidate all of the contexts that were removed.
// TODO(kalman): add an invalidation observer interface to ScriptContext.
- ScriptContextSet::ContextSet removed_contexts =
- script_context_set_.OnExtensionUnloaded(id);
- for (ScriptContextSet::ContextSet::iterator it = removed_contexts.begin();
- it != removed_contexts.end();
- ++it) {
- request_sender_->InvalidateSource(*it);
+ std::set<ScriptContext*> removed_contexts =
+ script_context_set_->OnExtensionUnloaded(id);
+ for (ScriptContext* context : removed_contexts) {
+ request_sender_->InvalidateSource(context);
}
// Update the available bindings for the remaining contexts. These may have
@@ -1384,75 +1322,6 @@ bool Dispatcher::IsWithinPlatformApp() {
return false;
}
-// TODO(kalman): This is checking for the wrong thing, it should be checking if
-// the frame's security origin is unique. The extension sandbox directive is
-// checked for in extensions/common/manifest_handlers/csp_info.cc.
-bool Dispatcher::IsSandboxedPage(const GURL& url) const {
- if (url.SchemeIs(kExtensionScheme)) {
- const Extension* extension = extensions_.GetByID(url.host());
- if (extension) {
- return SandboxedPageInfo::IsSandboxedPage(extension, url.path());
- }
- }
- return false;
-}
-
-Feature::Context Dispatcher::ClassifyJavaScriptContext(
- const Extension* extension,
- int extension_group,
- const GURL& url,
- const blink::WebSecurityOrigin& origin) {
- // WARNING: This logic must match ProcessMap::GetContextType, as much as
- // possible.
-
- DCHECK_GE(extension_group, 0);
- if (extension_group == EXTENSION_GROUP_CONTENT_SCRIPTS) {
- return extension ? // TODO(kalman): when does this happen?
- Feature::CONTENT_SCRIPT_CONTEXT
- : Feature::UNSPECIFIED_CONTEXT;
- }
-
- // We have an explicit check for sandboxed pages before checking whether the
- // extension is active in this process because:
- // 1. Sandboxed pages run in the same process as regular extension pages, so
- // the extension is considered active.
- // 2. ScriptContext creation (which triggers bindings injection) happens
- // before the SecurityContext is updated with the sandbox flags (after
- // reading the CSP header), so the caller can't check if the context's
- // security origin is unique yet.
- if (IsSandboxedPage(url))
- return Feature::WEB_PAGE_CONTEXT;
-
- if (extension && IsExtensionActive(extension->id())) {
- // |extension| is active in this process, but it could be either a true
- // extension process or within the extent of a hosted app. In the latter
- // case this would usually be considered a (blessed) web page context,
- // unless the extension in question is a component extension, in which case
- // we cheat and call it blessed.
- return (extension->is_hosted_app() &&
- extension->location() != Manifest::COMPONENT)
- ? Feature::BLESSED_WEB_PAGE_CONTEXT
- : Feature::BLESSED_EXTENSION_CONTEXT;
- }
-
- // TODO(kalman): This isUnique() check is wrong, it should be performed as
- // part of IsSandboxedPage().
- if (!origin.isUnique() && extensions_.ExtensionBindingsAllowed(url)) {
- if (!extension) // TODO(kalman): when does this happen?
- return Feature::UNSPECIFIED_CONTEXT;
- return extension->is_hosted_app() ? Feature::BLESSED_WEB_PAGE_CONTEXT
- : Feature::UNBLESSED_EXTENSION_CONTEXT;
- }
-
- if (!url.is_valid())
- return Feature::UNSPECIFIED_CONTEXT;
-
- if (url.SchemeIs(content::kChromeUIScheme))
- return Feature::WEBUI_CONTEXT;
-
- return Feature::WEB_PAGE_CONTEXT;
-}
-
v8::Handle<v8::Object> Dispatcher::GetOrCreateObject(
const v8::Handle<v8::Object>& object,
const std::string& field,
diff --git a/extensions/renderer/dispatcher.h b/extensions/renderer/dispatcher.h
index c5ac365..c71cb64 100644
--- a/extensions/renderer/dispatcher.h
+++ b/extensions/renderer/dispatcher.h
@@ -11,10 +11,12 @@
#include <utility>
#include <vector>
+#include "base/memory/scoped_ptr.h"
#include "base/scoped_observer.h"
#include "base/timer/timer.h"
#include "content/public/renderer/render_process_observer.h"
#include "extensions/common/event_filter.h"
+#include "extensions/common/extension.h"
#include "extensions/common/extension_set.h"
#include "extensions/common/extensions_client.h"
#include "extensions/common/features/feature.h"
@@ -53,7 +55,6 @@ class RenderThread;
namespace extensions {
class ContentWatcher;
class DispatcherDelegate;
-class Extension;
class FilteredEventRouter;
class ManifestPermissionSet;
class RequestSender;
@@ -76,7 +77,7 @@ class Dispatcher : public content::RenderProcessObserver,
const ExtensionSet* extensions() const { return &extensions_; }
const ScriptContextSet& script_context_set() const {
- return script_context_set_;
+ return *script_context_set_;
}
V8SchemaRegistry* v8_schema_registry() { return v8_schema_registry_.get(); }
@@ -89,14 +90,6 @@ class Dispatcher : public content::RenderProcessObserver,
bool IsExtensionActive(const std::string& extension_id) const;
- // Finds the extension for the JavaScript context associated with 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
- // JavaScript context isn't from an extension, returns empty string.
- const Extension* GetExtensionFromFrameAndWorld(const blink::WebFrame* frame,
- int world_id,
- bool use_effective_url);
-
void DidCreateScriptContext(blink::WebLocalFrame* frame,
const v8::Handle<v8::Context>& context,
int extension_group,
@@ -242,15 +235,6 @@ class Dispatcher : public content::RenderProcessObserver,
// Returns whether the current renderer hosts a platform app.
bool IsWithinPlatformApp();
- bool IsSandboxedPage(const GURL& url) const;
-
- // Returns the Feature::Context type of context for a JavaScript context.
- Feature::Context ClassifyJavaScriptContext(
- const Extension* extension,
- int extension_group,
- const GURL& url,
- const blink::WebSecurityOrigin& origin);
-
// Gets |field| from |object| or creates it as an empty object if it doesn't
// exist.
v8::Handle<v8::Object> GetOrCreateObject(const v8::Handle<v8::Object>& object,
@@ -284,7 +268,7 @@ class Dispatcher : public content::RenderProcessObserver,
// All the bindings contexts that are currently loaded for this renderer.
// There is zero or one for each v8 context.
- ScriptContextSet script_context_set_;
+ scoped_ptr<ScriptContextSet> script_context_set_;
scoped_ptr<ContentWatcher> content_watcher_;
@@ -300,7 +284,7 @@ class Dispatcher : public content::RenderProcessObserver,
std::set<std::string> function_names_;
// The extensions and apps that are active in this process.
- std::set<std::string> active_extension_ids_;
+ ExtensionIdSet active_extension_ids_;
ResourceBundleSourceMap source_map_;
diff --git a/extensions/renderer/document_custom_bindings.cc b/extensions/renderer/document_custom_bindings.cc
index bb9d7f7..f9f35d2 100644
--- a/extensions/renderer/document_custom_bindings.cc
+++ b/extensions/renderer/document_custom_bindings.cc
@@ -9,7 +9,7 @@
#include "base/bind.h"
#include "extensions/renderer/script_context.h"
#include "third_party/WebKit/public/web/WebDocument.h"
-#include "third_party/WebKit/public/web/WebFrame.h"
+#include "third_party/WebKit/public/web/WebLocalFrame.h"
#include "v8/include/v8.h"
namespace extensions {
diff --git a/extensions/renderer/request_sender.cc b/extensions/renderer/request_sender.cc
index 60c77c6..5797953 100644
--- a/extensions/renderer/request_sender.cc
+++ b/extensions/renderer/request_sender.cc
@@ -10,7 +10,7 @@
#include "extensions/renderer/dispatcher.h"
#include "extensions/renderer/script_context.h"
#include "third_party/WebKit/public/web/WebDocument.h"
-#include "third_party/WebKit/public/web/WebFrame.h"
+#include "third_party/WebKit/public/web/WebLocalFrame.h"
#include "third_party/WebKit/public/web/WebScopedUserGesture.h"
#include "third_party/WebKit/public/web/WebUserGestureIndicator.h"
#include "third_party/WebKit/public/web/WebUserGestureToken.h"
@@ -97,7 +97,7 @@ void RequestSender::StartRequest(Source* source,
return;
GURL source_url;
- if (blink::WebFrame* webframe = context->web_frame())
+ if (blink::WebLocalFrame* webframe = context->web_frame())
source_url = webframe->document().url();
InsertRequest(request_id, new PendingRequest(name, source,
diff --git a/extensions/renderer/script_context.cc b/extensions/renderer/script_context.cc
index e5677aa..47a3c2a 100644
--- a/extensions/renderer/script_context.cc
+++ b/extensions/renderer/script_context.cc
@@ -13,15 +13,19 @@
#include "content/public/common/url_constants.h"
#include "content/public/renderer/render_frame.h"
#include "content/public/renderer/render_view.h"
+#include "extensions/common/constants.h"
#include "extensions/common/extension.h"
#include "extensions/common/extension_api.h"
+#include "extensions/common/extension_set.h"
#include "extensions/common/extension_urls.h"
#include "extensions/common/features/base_feature_provider.h"
+#include "extensions/common/manifest_handlers/sandboxed_page_info.h"
#include "extensions/common/permissions/permissions_data.h"
#include "gin/per_context_data.h"
#include "third_party/WebKit/public/web/WebDataSource.h"
#include "third_party/WebKit/public/web/WebDocument.h"
#include "third_party/WebKit/public/web/WebFrame.h"
+#include "third_party/WebKit/public/web/WebLocalFrame.h"
#include "third_party/WebKit/public/web/WebScopedMicrotaskSuppression.h"
#include "third_party/WebKit/public/web/WebSecurityOrigin.h"
#include "third_party/WebKit/public/web/WebView.h"
@@ -75,7 +79,7 @@ class ScriptContext::Runner : public gin::Runner {
};
ScriptContext::ScriptContext(const v8::Handle<v8::Context>& v8_context,
- blink::WebFrame* web_frame,
+ blink::WebLocalFrame* web_frame,
const Extension* extension,
Feature::Context context_type,
const Extension* effective_extension,
@@ -110,6 +114,21 @@ ScriptContext::~ScriptContext() {
Invalidate();
}
+// static
+bool ScriptContext::IsSandboxedPage(const ExtensionSet& extensions,
+ const GURL& url) {
+ // TODO(kalman): This is checking for the wrong thing, it should be checking
+ // if the frame's security origin is unique. The extension sandbox directive
+ // is checked for in extensions/common/manifest_handlers/csp_info.cc.
+ if (url.SchemeIs(kExtensionScheme)) {
+ const Extension* extension = extensions.GetByID(url.host());
+ if (extension) {
+ return SandboxedPageInfo::IsSandboxedPage(extension, url.path());
+ }
+ }
+ return false;
+}
+
void ScriptContext::Invalidate() {
if (!is_valid())
return;
diff --git a/extensions/renderer/script_context.h b/extensions/renderer/script_context.h
index b6c90f2..745f522 100644
--- a/extensions/renderer/script_context.h
+++ b/extensions/renderer/script_context.h
@@ -20,6 +20,7 @@
namespace blink {
class WebFrame;
+class WebLocalFrame;
}
namespace content {
@@ -29,18 +30,27 @@ class RenderView;
namespace extensions {
class Extension;
+class ExtensionSet;
// Extensions wrapper for a v8 context.
class ScriptContext : public RequestSender::Source {
public:
ScriptContext(const v8::Handle<v8::Context>& context,
- blink::WebFrame* frame,
+ blink::WebLocalFrame* frame,
const Extension* extension,
Feature::Context context_type,
const Extension* effective_extension,
Feature::Context effective_context_type);
~ScriptContext() override;
+ // Returns whether |url| is sandboxed (as declared in any Extension in
+ // |extension_set| as sandboxed).
+ //
+ // Declared in ScriptContext for lack of a better place, but this should
+ // become unnecessary at some point as crbug.com/466373 is worked on.
+ static bool IsSandboxedPage(const ExtensionSet& extension_set,
+ const GURL& url);
+
// Clears the WebFrame for this contexts and invalidates the associated
// ModuleSystem.
void Invalidate();
@@ -59,7 +69,7 @@ class ScriptContext : public RequestSender::Source {
return effective_extension_.get();
}
- blink::WebFrame* web_frame() const { return web_frame_; }
+ blink::WebLocalFrame* web_frame() const { return web_frame_; }
Feature::Context context_type() const { return context_type_; }
@@ -114,6 +124,14 @@ class ScriptContext : public RequestSender::Source {
v8::Isolate* isolate() const { return isolate_; }
// Get the URL of this context's web frame.
+ //
+ // TODO(kalman): Remove this and replace with a GetOrigin() call which reads
+ // of WebDocument::securityOrigin():
+ // - The URL can change (e.g. pushState) but the origin cannot. Luckily it
+ // appears as though callers don't make security decisions based on the
+ // result of GetURL() so it's not a problem... yet.
+ // - Origin is the correct check to be making.
+ // - It might let us remove the about:blank resolving?
GURL GetURL() const;
// Returns whether the API |api| or any part of the API could be
@@ -157,9 +175,9 @@ class ScriptContext : public RequestSender::Source {
private:
class Runner;
- // The WebFrame associated with this context. This can be NULL because this
- // object can outlive is destroyed asynchronously.
- blink::WebFrame* web_frame_;
+ // The WebLocalFrame associated with this context. This can be NULL because
+ // this object can outlive is destroyed asynchronously.
+ blink::WebLocalFrame* web_frame_;
// The extension associated with this context, or NULL if there is none. This
// might be a hosted app in the case that this context is hosting a web URL.
diff --git a/extensions/renderer/script_context_set.cc b/extensions/renderer/script_context_set.cc
index 5f7be6c1..1e9e9e8 100644
--- a/extensions/renderer/script_context_set.cc
+++ b/extensions/renderer/script_context_set.cc
@@ -5,34 +5,50 @@
#include "extensions/renderer/script_context_set.h"
#include "base/message_loop/message_loop.h"
+#include "content/public/common/url_constants.h"
#include "content/public/renderer/render_view.h"
#include "extensions/common/extension.h"
+#include "extensions/renderer/extension_groups.h"
#include "extensions/renderer/script_context.h"
+#include "extensions/renderer/script_injection.h"
+#include "third_party/WebKit/public/web/WebDocument.h"
+#include "third_party/WebKit/public/web/WebLocalFrame.h"
#include "v8/include/v8.h"
namespace extensions {
-ScriptContextSet::ScriptContextSet() {
-}
-ScriptContextSet::~ScriptContextSet() {
+ScriptContextSet::ScriptContextSet(ExtensionSet* extensions,
+ ExtensionIdSet* active_extension_ids)
+ : extensions_(extensions), active_extension_ids_(active_extension_ids) {
}
-int ScriptContextSet::size() const {
- return static_cast<int>(contexts_.size());
+ScriptContextSet::~ScriptContextSet() {
}
-void ScriptContextSet::Add(ScriptContext* context) {
-#if DCHECK_IS_ON()
- // It's OK to insert the same context twice, but we should only ever have
- // one ScriptContext per v8::Context.
- for (ContextSet::iterator iter = contexts_.begin(); iter != contexts_.end();
- ++iter) {
- ScriptContext* candidate = *iter;
- if (candidate != context)
- DCHECK(candidate->v8_context() != context->v8_context());
- }
-#endif
- contexts_.insert(context);
+ScriptContext* ScriptContextSet::Register(
+ blink::WebLocalFrame* frame,
+ const v8::Handle<v8::Context>& v8_context,
+ int extension_group,
+ int world_id) {
+ 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 =
+ ClassifyJavaScriptContext(extension, extension_group, frame_url,
+ frame->document().securityOrigin());
+ Feature::Context effective_context_type = ClassifyJavaScriptContext(
+ effective_extension, extension_group,
+ ScriptContext::GetEffectiveDocumentURL(frame, frame_url, true),
+ frame->document().securityOrigin());
+
+ ScriptContext* context =
+ new ScriptContext(v8_context, frame, extension, context_type,
+ effective_extension, effective_context_type);
+ contexts_.insert(context); // takes ownership
+ return context;
}
void ScriptContextSet::Remove(ScriptContext* context) {
@@ -42,32 +58,25 @@ void ScriptContextSet::Remove(ScriptContext* context) {
}
}
-ScriptContextSet::ContextSet ScriptContextSet::GetAll() const {
- return contexts_;
-}
-
ScriptContext* ScriptContextSet::GetCurrent() const {
v8::Isolate* isolate = v8::Isolate::GetCurrent();
return isolate->InContext() ? GetByV8Context(isolate->GetCurrentContext())
- : NULL;
+ : nullptr;
}
ScriptContext* ScriptContextSet::GetCalling() const {
v8::Isolate* isolate = v8::Isolate::GetCurrent();
v8::Local<v8::Context> calling = isolate->GetCallingContext();
- return calling.IsEmpty() ? NULL : GetByV8Context(calling);
+ return calling.IsEmpty() ? nullptr : GetByV8Context(calling);
}
ScriptContext* ScriptContextSet::GetByV8Context(
- v8::Handle<v8::Context> v8_context) const {
- for (ContextSet::const_iterator iter = contexts_.begin();
- iter != contexts_.end();
- ++iter) {
- if ((*iter)->v8_context() == v8_context)
- return *iter;
+ const v8::Handle<v8::Context>& v8_context) const {
+ for (ScriptContext* script_context : contexts_) {
+ if (script_context->v8_context() == v8_context)
+ return script_context;
}
-
- return NULL;
+ return nullptr;
}
void ScriptContextSet::ForEach(
@@ -76,11 +85,9 @@ void ScriptContextSet::ForEach(
const base::Callback<void(ScriptContext*)>& callback) const {
// We copy the context list, because calling into javascript may modify it
// out from under us.
- ContextSet contexts = GetAll();
-
- for (ContextSet::iterator it = contexts.begin(); it != contexts.end(); ++it) {
- ScriptContext* context = *it;
+ std::set<ScriptContext*> contexts_copy = contexts_;
+ for (ScriptContext* context : contexts_copy) {
// For the same reason as above, contexts may become invalid while we run.
if (!context->is_valid())
continue;
@@ -102,23 +109,103 @@ void ScriptContextSet::ForEach(
}
}
-ScriptContextSet::ContextSet ScriptContextSet::OnExtensionUnloaded(
+std::set<ScriptContext*> ScriptContextSet::OnExtensionUnloaded(
const std::string& extension_id) {
- ContextSet contexts = GetAll();
- ContextSet removed;
-
- // Clean up contexts belonging to the unloaded extension. This is done so
- // that content scripts (which remain injected into the page) don't continue
- // receiving events and sending messages.
- for (ContextSet::iterator it = contexts.begin(); it != contexts.end(); ++it) {
- if ((*it)->extension() && (*it)->extension()->id() == extension_id) {
- (*it)->DispatchOnUnloadEvent();
- removed.insert(*it);
- Remove(*it);
- }
+ std::set<ScriptContext*> removed;
+ ForEach(extension_id,
+ base::Bind(&ScriptContextSet::DispatchOnUnloadEventAndRemove,
+ base::Unretained(this), &removed));
+ return removed;
+}
+
+const Extension* ScriptContextSet::GetExtensionFromFrameAndWorld(
+ const blink::WebLocalFrame* frame,
+ int world_id,
+ bool use_effective_url) {
+ std::string extension_id;
+ if (world_id != 0) {
+ // Isolated worlds (content script).
+ extension_id = ScriptInjection::GetHostIdForIsolatedWorld(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);
}
- return removed;
+ // There are conditions where despite a context being associated with an
+ // extension, no extension actually gets found. Ignore "invalid" because CSP
+ // blocks extension page loading by switching the extension ID to "invalid".
+ const Extension* extension = extensions_->GetByID(extension_id);
+ if (!extension && !extension_id.empty() && extension_id != "invalid") {
+ // TODO(kalman): Do something here?
+ }
+ return extension;
+}
+
+Feature::Context ScriptContextSet::ClassifyJavaScriptContext(
+ const Extension* extension,
+ int extension_group,
+ const GURL& url,
+ const blink::WebSecurityOrigin& origin) {
+ // WARNING: This logic must match ProcessMap::GetContextType, as much as
+ // possible.
+
+ DCHECK_GE(extension_group, 0);
+ if (extension_group == EXTENSION_GROUP_CONTENT_SCRIPTS) {
+ return extension ? // TODO(kalman): when does this happen?
+ Feature::CONTENT_SCRIPT_CONTEXT
+ : Feature::UNSPECIFIED_CONTEXT;
+ }
+
+ // We have an explicit check for sandboxed pages before checking whether the
+ // extension is active in this process because:
+ // 1. Sandboxed pages run in the same process as regular extension pages, so
+ // the extension is considered active.
+ // 2. ScriptContext creation (which triggers bindings injection) happens
+ // before the SecurityContext is updated with the sandbox flags (after
+ // reading the CSP header), so the caller can't check if the context's
+ // security origin is unique yet.
+ if (ScriptContext::IsSandboxedPage(*extensions_, url))
+ return Feature::WEB_PAGE_CONTEXT;
+
+ if (extension && active_extension_ids_->count(extension->id()) > 0) {
+ // |extension| is active in this process, but it could be either a true
+ // extension process or within the extent of a hosted app. In the latter
+ // case this would usually be considered a (blessed) web page context,
+ // unless the extension in question is a component extension, in which case
+ // we cheat and call it blessed.
+ return (extension->is_hosted_app() &&
+ extension->location() != Manifest::COMPONENT)
+ ? Feature::BLESSED_WEB_PAGE_CONTEXT
+ : Feature::BLESSED_EXTENSION_CONTEXT;
+ }
+
+ // TODO(kalman): This isUnique() check is wrong, it should be performed as
+ // part of ScriptContext::IsSandboxedPage().
+ if (!origin.isUnique() && extensions_->ExtensionBindingsAllowed(url)) {
+ if (!extension) // TODO(kalman): when does this happen?
+ return Feature::UNSPECIFIED_CONTEXT;
+ return extension->is_hosted_app() ? Feature::BLESSED_WEB_PAGE_CONTEXT
+ : Feature::UNBLESSED_EXTENSION_CONTEXT;
+ }
+
+ if (!url.is_valid())
+ return Feature::UNSPECIFIED_CONTEXT;
+
+ if (url.SchemeIs(content::kChromeUIScheme))
+ return Feature::WEBUI_CONTEXT;
+
+ return Feature::WEB_PAGE_CONTEXT;
+}
+
+void ScriptContextSet::DispatchOnUnloadEventAndRemove(
+ std::set<ScriptContext*>* out,
+ ScriptContext* context) {
+ Remove(context); // deleted asynchronously
+ out->insert(context);
}
} // namespace extensions
diff --git a/extensions/renderer/script_context_set.h b/extensions/renderer/script_context_set.h
index aa7b253..ac3cb17 100644
--- a/extensions/renderer/script_context_set.h
+++ b/extensions/renderer/script_context_set.h
@@ -8,8 +8,12 @@
#include <set>
#include <string>
-#include "base/basictypes.h"
-#include "base/bind.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "extensions/common/extension.h"
+#include "extensions/common/extension_set.h"
+#include "extensions/common/features/feature.h"
+#include "url/gurl.h"
#include "v8/include/v8.h"
class GURL;
@@ -18,39 +22,50 @@ namespace base {
class ListValue;
}
-namespace content {
-class RenderView;
+namespace blink {
+class WebLocalFrame;
+class WebSecurityOrigin;
}
-namespace v8 {
-class Context;
+namespace content {
+class RenderView;
}
namespace extensions {
class ScriptContext;
-// A container of ExtensionBindingsContext. Since calling JavaScript within a
-// context can cause any number of contexts to be created or destroyed, this
-// has additional smarts to help with the set changing underneath callers.
+// A container of ScriptContexts, responsible for both creating and managing
+// them.
+//
+// Since calling JavaScript within a context can cause any number of contexts
+// to be created or destroyed, this has additional smarts to help with the set
+// changing underneath callers.
class ScriptContextSet {
public:
- ScriptContextSet();
+ ScriptContextSet(
+ ExtensionSet* extensions,
+ // Set of the IDs of extensions that are active in this process.
+ // Must outlive this. TODO(kalman): Combine this and |extensions|.
+ ExtensionIdSet* active_extension_ids);
+
~ScriptContextSet();
- int size() const;
+ // Returns the number of contexts being tracked by this set.
+ // This may also include invalid contexts. TODO(kalman): Useful?
+ size_t size() const { return contexts_.size(); }
- // Takes ownership of |context|.
- void Add(ScriptContext* context);
+ // Creates and starts managing a new ScriptContext. Ownership is held.
+ // Returns a weak reference to the new ScriptContext.
+ ScriptContext* Register(blink::WebLocalFrame* frame,
+ const v8::Handle<v8::Context>& v8_context,
+ int extension_group,
+ int world_id);
// If the specified context is contained in this set, remove it, then delete
// it asynchronously. After this call returns the context object will still
// be valid, but its frame() pointer will be cleared.
void Remove(ScriptContext* context);
- // Returns a copy to protect against changes.
- typedef std::set<ScriptContext*> ContextSet;
- ContextSet GetAll() const;
-
// Gets the ScriptContext corresponding to v8::Context::GetCurrent(), or
// NULL if no such context exists.
ScriptContext* GetCurrent() const;
@@ -61,7 +76,7 @@ class ScriptContextSet {
// Gets the ScriptContext corresponding to the specified
// v8::Context or NULL if no such context exists.
- ScriptContext* GetByV8Context(v8::Handle<v8::Context> context) const;
+ ScriptContext* GetByV8Context(const v8::Handle<v8::Context>& context) const;
// Synchronously runs |callback| with each ScriptContext that belongs to
// |extension_id| in |render_view|.
@@ -88,10 +103,39 @@ class ScriptContextSet {
// Returns the set of ScriptContexts that were removed as a result. These
// are safe to interact with until the end of the current event loop, since
// they're deleted asynchronously.
- ContextSet OnExtensionUnloaded(const std::string& extension_id);
+ std::set<ScriptContext*> OnExtensionUnloaded(const std::string& extension_id);
private:
- ContextSet contexts_;
+ // Finds the extension for the JavaScript context associated with 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
+ // JavaScript context isn't from an extension, returns empty string.
+ const Extension* GetExtensionFromFrameAndWorld(
+ const blink::WebLocalFrame* frame,
+ int world_id,
+ bool use_effective_url);
+
+ // Returns the Feature::Context type of context for a JavaScript context.
+ Feature::Context ClassifyJavaScriptContext(
+ const Extension* extension,
+ int extension_group,
+ const GURL& url,
+ const blink::WebSecurityOrigin& origin);
+
+ // Calls Remove on |context| then appends |context| to |out|.
+ // This is a helper designed to be used by OnExtensionUnloaded with ForEach.
+ void DispatchOnUnloadEventAndRemove(std::set<ScriptContext*>* out,
+ ScriptContext* context);
+
+ // Weak reference to all installed Extensions.
+ ExtensionSet* extensions_;
+
+ // Weak reference to all installed Extensions that are also active in this
+ // process.
+ ExtensionIdSet* active_extension_ids_;
+
+ // The set of all ScriptContexts we own.
+ std::set<ScriptContext*> contexts_;
DISALLOW_COPY_AND_ASSIGN(ScriptContextSet);
};
diff --git a/extensions/renderer/script_context_set_unittest.cc b/extensions/renderer/script_context_set_unittest.cc
index e795dd9..089caca 100644
--- a/extensions/renderer/script_context_set_unittest.cc
+++ b/extensions/renderer/script_context_set_unittest.cc
@@ -2,61 +2,59 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include <vector>
+
#include "base/message_loop/message_loop.h"
#include "extensions/common/extension.h"
+#include "extensions/common/extension_set.h"
#include "extensions/common/features/feature.h"
#include "extensions/renderer/script_context.h"
#include "extensions/renderer/script_context_set.h"
#include "gin/public/context_holder.h"
#include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/WebKit/public/web/WebFrame.h"
#include "third_party/WebKit/public/web/WebLocalFrame.h"
#include "third_party/WebKit/public/web/WebView.h"
#include "v8/include/v8.h"
namespace extensions {
-TEST(ScriptContextSet, Lifecycle) {
+TEST(ScriptContextSetTest, Lifecycle) {
base::MessageLoop loop;
- ScriptContextSet context_set;
+ ExtensionSet extensions;
+ ExtensionIdSet active_extensions;
+ ScriptContextSet context_set(&extensions, &active_extensions);
+ blink::WebView* webview = blink::WebView::create(nullptr);
+ blink::WebLocalFrame* frame = blink::WebLocalFrame::create(nullptr);
+ webview->setMainFrame(frame);
v8::Isolate* isolate = v8::Isolate::GetCurrent();
v8::HandleScope handle_scope(isolate);
+ // Note: ScriptContext has gin stuff in it, and without a ContextHolder tests
+ // will leak memory.
gin::ContextHolder context_holder(isolate);
- context_holder.SetContext(v8::Context::New(isolate));
-
- blink::WebView* webview = blink::WebView::create(nullptr);
- blink::WebFrame* frame = blink::WebLocalFrame::create(nullptr);
- webview->setMainFrame(frame);
- const Extension* extension = NULL;
- ScriptContext* context =
- new ScriptContext(context_holder.context(),
- frame,
- extension,
- Feature::BLESSED_EXTENSION_CONTEXT,
- extension,
- Feature::BLESSED_EXTENSION_CONTEXT);
-
- context_set.Add(context);
- EXPECT_EQ(1u, context_set.GetAll().count(context));
- EXPECT_EQ(context, context_set.GetByV8Context(context->v8_context()));
+ v8::Handle<v8::Context> v8_context = v8::Context::New(isolate);
+ v8::Context::Scope context_scope(v8_context);
+ ScriptContext* context = context_set.Register(
+ frame, v8_context, 0, 0); // no extension group or world ID
- // Adding the same item multiple times should be OK and deduped.
- context_set.Add(context);
- EXPECT_EQ(1u, context_set.GetAll().count(context));
+ // Context is valid and resembles correctness.
+ EXPECT_TRUE(context->is_valid());
+ EXPECT_EQ(frame, context->web_frame());
+ EXPECT_EQ(v8_context, context->v8_context());
- // GetAll() returns a copy so removing from one should not remove from others.
- ScriptContextSet::ContextSet set_copy = context_set.GetAll();
- EXPECT_EQ(1u, set_copy.count(context));
+ // Context has been correctly added.
+ EXPECT_EQ(1u, context_set.size());
+ EXPECT_EQ(context, context_set.GetByV8Context(v8_context));
+ // Test context is correctly removed.
context_set.Remove(context);
- EXPECT_EQ(0, context_set.size());
- EXPECT_FALSE(context_set.GetByV8Context(context->v8_context()));
- EXPECT_EQ(1u, set_copy.size());
+ EXPECT_EQ(0u, context_set.size());
+ EXPECT_EQ(nullptr, context_set.GetByV8Context(v8_context));
- // After removal, the context should be marked for destruction.
- EXPECT_FALSE(context->web_frame());
+ // After removal, the context should be invalid.
+ EXPECT_FALSE(context->is_valid());
+ EXPECT_EQ(nullptr, context->web_frame());
// Run loop to do the actual deletion.
loop.RunUntilIdle();