Index: extensions/renderer/script_context_set.cc |
diff --git a/extensions/renderer/script_context_set.cc b/extensions/renderer/script_context_set.cc |
index 5f7be6c156f84c6b031580b5ee4fcc486ae6171d..1e9e9e8ee3f2f852877909ebcff7a40ccad1fd0c 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 |