Commit 745b6623 authored by kalman's avatar kalman Committed by Commit bot

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

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

Cr-Commit-Position: refs/heads/master@{#320971}
parent b42007a5
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#include "extensions/renderer/script_context.h" #include "extensions/renderer/script_context.h"
#include "third_party/WebKit/public/web/WebDocument.h" #include "third_party/WebKit/public/web/WebDocument.h"
#include "third_party/WebKit/public/web/WebElement.h" #include "third_party/WebKit/public/web/WebElement.h"
#include "third_party/WebKit/public/web/WebLocalFrame.h"
#include "third_party/WebKit/public/web/WebNode.h" #include "third_party/WebKit/public/web/WebNode.h"
#include "third_party/WebKit/public/web/WebNodeList.h" #include "third_party/WebKit/public/web/WebNodeList.h"
#include "third_party/WebKit/public/web/WebUserGestureIndicator.h" #include "third_party/WebKit/public/web/WebUserGestureIndicator.h"
...@@ -22,7 +23,6 @@ ...@@ -22,7 +23,6 @@
using blink::WebDocument; using blink::WebDocument;
using blink::WebElement; using blink::WebElement;
using blink::WebFrame;
using blink::WebNode; using blink::WebNode;
using blink::WebNodeList; using blink::WebNodeList;
using blink::WebUserGestureIndicator; using blink::WebUserGestureIndicator;
...@@ -79,7 +79,7 @@ void WebstoreBindings::Install( ...@@ -79,7 +79,7 @@ void WebstoreBindings::Install(
std::string webstore_item_id; std::string webstore_item_id;
std::string error; std::string error;
WebFrame* frame = context()->web_frame(); blink::WebLocalFrame* frame = context()->web_frame();
if (!GetWebstoreItemIdFromFrame( if (!GetWebstoreItemIdFromFrame(
frame, preferred_store_link_url, &webstore_item_id, &error)) { frame, preferred_store_link_url, &webstore_item_id, &error)) {
...@@ -102,8 +102,10 @@ void WebstoreBindings::Install( ...@@ -102,8 +102,10 @@ void WebstoreBindings::Install(
// static // static
bool WebstoreBindings::GetWebstoreItemIdFromFrame( bool WebstoreBindings::GetWebstoreItemIdFromFrame(
WebFrame* frame, const std::string& preferred_store_link_url, blink::WebLocalFrame* frame,
std::string* webstore_item_id, std::string* error) { const std::string& preferred_store_link_url,
std::string* webstore_item_id,
std::string* error) {
if (frame != frame->top()) { if (frame != frame->top()) {
*error = kNotInTopFrameError; *error = kNotInTopFrameError;
return false; return false;
......
...@@ -9,7 +9,10 @@ ...@@ -9,7 +9,10 @@
#include "chrome/common/extensions/webstore_install_result.h" #include "chrome/common/extensions/webstore_install_result.h"
#include "chrome/renderer/extensions/chrome_v8_extension_handler.h" #include "chrome/renderer/extensions/chrome_v8_extension_handler.h"
#include "extensions/renderer/object_backed_native_handler.h" #include "extensions/renderer/object_backed_native_handler.h"
#include "third_party/WebKit/public/web/WebFrame.h"
namespace blink {
class WebLocalFrame;
}
namespace extensions { namespace extensions {
...@@ -42,8 +45,10 @@ class WebstoreBindings : public ObjectBackedNativeHandler, ...@@ -42,8 +45,10 @@ class WebstoreBindings : public ObjectBackedNativeHandler,
// parameter will be populated with the ID. On failure, false will be returned // parameter will be populated with the ID. On failure, false will be returned
// and |error| will be populated with the error. // and |error| will be populated with the error.
static bool GetWebstoreItemIdFromFrame( static bool GetWebstoreItemIdFromFrame(
blink::WebFrame* frame, const std::string& preferred_store_link_url, blink::WebLocalFrame* frame,
std::string* webstore_item_id, std::string* error); const std::string& preferred_store_link_url,
std::string* webstore_item_id,
std::string* error);
DISALLOW_COPY_AND_ASSIGN(WebstoreBindings); DISALLOW_COPY_AND_ASSIGN(WebstoreBindings);
}; };
......
This diff is collapsed.
...@@ -11,10 +11,12 @@ ...@@ -11,10 +11,12 @@
#include <utility> #include <utility>
#include <vector> #include <vector>
#include "base/memory/scoped_ptr.h"
#include "base/scoped_observer.h" #include "base/scoped_observer.h"
#include "base/timer/timer.h" #include "base/timer/timer.h"
#include "content/public/renderer/render_process_observer.h" #include "content/public/renderer/render_process_observer.h"
#include "extensions/common/event_filter.h" #include "extensions/common/event_filter.h"
#include "extensions/common/extension.h"
#include "extensions/common/extension_set.h" #include "extensions/common/extension_set.h"
#include "extensions/common/extensions_client.h" #include "extensions/common/extensions_client.h"
#include "extensions/common/features/feature.h" #include "extensions/common/features/feature.h"
...@@ -53,7 +55,6 @@ class RenderThread; ...@@ -53,7 +55,6 @@ class RenderThread;
namespace extensions { namespace extensions {
class ContentWatcher; class ContentWatcher;
class DispatcherDelegate; class DispatcherDelegate;
class Extension;
class FilteredEventRouter; class FilteredEventRouter;
class ManifestPermissionSet; class ManifestPermissionSet;
class RequestSender; class RequestSender;
...@@ -76,7 +77,7 @@ class Dispatcher : public content::RenderProcessObserver, ...@@ -76,7 +77,7 @@ class Dispatcher : public content::RenderProcessObserver,
const ExtensionSet* extensions() const { return &extensions_; } const ExtensionSet* extensions() const { return &extensions_; }
const ScriptContextSet& script_context_set() const { const ScriptContextSet& script_context_set() const {
return script_context_set_; return *script_context_set_;
} }
V8SchemaRegistry* v8_schema_registry() { return v8_schema_registry_.get(); } V8SchemaRegistry* v8_schema_registry() { return v8_schema_registry_.get(); }
...@@ -89,14 +90,6 @@ class Dispatcher : public content::RenderProcessObserver, ...@@ -89,14 +90,6 @@ class Dispatcher : public content::RenderProcessObserver,
bool IsExtensionActive(const std::string& extension_id) const; 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, void DidCreateScriptContext(blink::WebLocalFrame* frame,
const v8::Handle<v8::Context>& context, const v8::Handle<v8::Context>& context,
int extension_group, int extension_group,
...@@ -242,15 +235,6 @@ class Dispatcher : public content::RenderProcessObserver, ...@@ -242,15 +235,6 @@ class Dispatcher : public content::RenderProcessObserver,
// Returns whether the current renderer hosts a platform app. // Returns whether the current renderer hosts a platform app.
bool IsWithinPlatformApp(); 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 // Gets |field| from |object| or creates it as an empty object if it doesn't
// exist. // exist.
v8::Handle<v8::Object> GetOrCreateObject(const v8::Handle<v8::Object>& object, v8::Handle<v8::Object> GetOrCreateObject(const v8::Handle<v8::Object>& object,
...@@ -284,7 +268,7 @@ class Dispatcher : public content::RenderProcessObserver, ...@@ -284,7 +268,7 @@ class Dispatcher : public content::RenderProcessObserver,
// All the bindings contexts that are currently loaded for this renderer. // All the bindings contexts that are currently loaded for this renderer.
// There is zero or one for each v8 context. // There is zero or one for each v8 context.
ScriptContextSet script_context_set_; scoped_ptr<ScriptContextSet> script_context_set_;
scoped_ptr<ContentWatcher> content_watcher_; scoped_ptr<ContentWatcher> content_watcher_;
...@@ -300,7 +284,7 @@ class Dispatcher : public content::RenderProcessObserver, ...@@ -300,7 +284,7 @@ class Dispatcher : public content::RenderProcessObserver,
std::set<std::string> function_names_; std::set<std::string> function_names_;
// The extensions and apps that are active in this process. // The extensions and apps that are active in this process.
std::set<std::string> active_extension_ids_; ExtensionIdSet active_extension_ids_;
ResourceBundleSourceMap source_map_; ResourceBundleSourceMap source_map_;
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
#include "base/bind.h" #include "base/bind.h"
#include "extensions/renderer/script_context.h" #include "extensions/renderer/script_context.h"
#include "third_party/WebKit/public/web/WebDocument.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" #include "v8/include/v8.h"
namespace extensions { namespace extensions {
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
#include "extensions/renderer/dispatcher.h" #include "extensions/renderer/dispatcher.h"
#include "extensions/renderer/script_context.h" #include "extensions/renderer/script_context.h"
#include "third_party/WebKit/public/web/WebDocument.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/WebScopedUserGesture.h"
#include "third_party/WebKit/public/web/WebUserGestureIndicator.h" #include "third_party/WebKit/public/web/WebUserGestureIndicator.h"
#include "third_party/WebKit/public/web/WebUserGestureToken.h" #include "third_party/WebKit/public/web/WebUserGestureToken.h"
...@@ -97,7 +97,7 @@ void RequestSender::StartRequest(Source* source, ...@@ -97,7 +97,7 @@ void RequestSender::StartRequest(Source* source,
return; return;
GURL source_url; GURL source_url;
if (blink::WebFrame* webframe = context->web_frame()) if (blink::WebLocalFrame* webframe = context->web_frame())
source_url = webframe->document().url(); source_url = webframe->document().url();
InsertRequest(request_id, new PendingRequest(name, source, InsertRequest(request_id, new PendingRequest(name, source,
......
...@@ -13,15 +13,19 @@ ...@@ -13,15 +13,19 @@
#include "content/public/common/url_constants.h" #include "content/public/common/url_constants.h"
#include "content/public/renderer/render_frame.h" #include "content/public/renderer/render_frame.h"
#include "content/public/renderer/render_view.h" #include "content/public/renderer/render_view.h"
#include "extensions/common/constants.h"
#include "extensions/common/extension.h" #include "extensions/common/extension.h"
#include "extensions/common/extension_api.h" #include "extensions/common/extension_api.h"
#include "extensions/common/extension_set.h"
#include "extensions/common/extension_urls.h" #include "extensions/common/extension_urls.h"
#include "extensions/common/features/base_feature_provider.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 "extensions/common/permissions/permissions_data.h"
#include "gin/per_context_data.h" #include "gin/per_context_data.h"
#include "third_party/WebKit/public/web/WebDataSource.h" #include "third_party/WebKit/public/web/WebDataSource.h"
#include "third_party/WebKit/public/web/WebDocument.h" #include "third_party/WebKit/public/web/WebDocument.h"
#include "third_party/WebKit/public/web/WebFrame.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/WebScopedMicrotaskSuppression.h"
#include "third_party/WebKit/public/web/WebSecurityOrigin.h" #include "third_party/WebKit/public/web/WebSecurityOrigin.h"
#include "third_party/WebKit/public/web/WebView.h" #include "third_party/WebKit/public/web/WebView.h"
...@@ -75,7 +79,7 @@ class ScriptContext::Runner : public gin::Runner { ...@@ -75,7 +79,7 @@ class ScriptContext::Runner : public gin::Runner {
}; };
ScriptContext::ScriptContext(const v8::Handle<v8::Context>& v8_context, ScriptContext::ScriptContext(const v8::Handle<v8::Context>& v8_context,
blink::WebFrame* web_frame, blink::WebLocalFrame* web_frame,
const Extension* extension, const Extension* extension,
Feature::Context context_type, Feature::Context context_type,
const Extension* effective_extension, const Extension* effective_extension,
...@@ -99,7 +103,9 @@ ScriptContext::ScriptContext(const v8::Handle<v8::Context>& v8_context, ...@@ -99,7 +103,9 @@ ScriptContext::ScriptContext(const v8::Handle<v8::Context>& v8_context,
<< (effective_extension_.get() ? effective_extension_->id() : "") << (effective_extension_.get() ? effective_extension_->id() : "")
<< " effective context type: " << " effective context type: "
<< GetEffectiveContextTypeDescription(); << GetEffectiveContextTypeDescription();
gin::PerContextData::From(v8_context)->set_runner(runner_.get()); gin::PerContextData* gin_data = gin::PerContextData::From(v8_context);
CHECK(gin_data); // may fail if the v8::Context hasn't been registered yet
gin_data->set_runner(runner_.get());
} }
ScriptContext::~ScriptContext() { ScriptContext::~ScriptContext() {
...@@ -110,6 +116,21 @@ ScriptContext::~ScriptContext() { ...@@ -110,6 +116,21 @@ ScriptContext::~ScriptContext() {
Invalidate(); 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() { void ScriptContext::Invalidate() {
if (!is_valid()) if (!is_valid())
return; return;
......
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
namespace blink { namespace blink {
class WebFrame; class WebFrame;
class WebLocalFrame;
} }
namespace content { namespace content {
...@@ -29,18 +30,27 @@ class RenderView; ...@@ -29,18 +30,27 @@ class RenderView;
namespace extensions { namespace extensions {
class Extension; class Extension;
class ExtensionSet;
// Extensions wrapper for a v8 context. // Extensions wrapper for a v8 context.
class ScriptContext : public RequestSender::Source { class ScriptContext : public RequestSender::Source {
public: public:
ScriptContext(const v8::Handle<v8::Context>& context, ScriptContext(const v8::Handle<v8::Context>& context,
blink::WebFrame* frame, blink::WebLocalFrame* frame,
const Extension* extension, const Extension* extension,
Feature::Context context_type, Feature::Context context_type,
const Extension* effective_extension, const Extension* effective_extension,
Feature::Context effective_context_type); Feature::Context effective_context_type);
~ScriptContext() override; ~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 // Clears the WebFrame for this contexts and invalidates the associated
// ModuleSystem. // ModuleSystem.
void Invalidate(); void Invalidate();
...@@ -59,7 +69,7 @@ class ScriptContext : public RequestSender::Source { ...@@ -59,7 +69,7 @@ class ScriptContext : public RequestSender::Source {
return effective_extension_.get(); 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_; } Feature::Context context_type() const { return context_type_; }
...@@ -114,6 +124,14 @@ class ScriptContext : public RequestSender::Source { ...@@ -114,6 +124,14 @@ class ScriptContext : public RequestSender::Source {
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.
//
// 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; GURL GetURL() const;
// Returns whether the API |api| or any part of the API could be // Returns whether the API |api| or any part of the API could be
...@@ -157,9 +175,9 @@ class ScriptContext : public RequestSender::Source { ...@@ -157,9 +175,9 @@ class ScriptContext : public RequestSender::Source {
private: private:
class Runner; class Runner;
// The WebFrame associated with this context. This can be NULL because this // The WebLocalFrame associated with this context. This can be NULL because
// object can outlive is destroyed asynchronously. // this object can outlive is destroyed asynchronously.
blink::WebFrame* web_frame_; blink::WebLocalFrame* web_frame_;
// The extension associated with this context, or NULL if there is none. This // 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. // might be a hosted app in the case that this context is hosting a web URL.
......
...@@ -5,34 +5,50 @@ ...@@ -5,34 +5,50 @@
#include "extensions/renderer/script_context_set.h" #include "extensions/renderer/script_context_set.h"
#include "base/message_loop/message_loop.h" #include "base/message_loop/message_loop.h"
#include "content/public/common/url_constants.h"
#include "content/public/renderer/render_view.h" #include "content/public/renderer/render_view.h"
#include "extensions/common/extension.h" #include "extensions/common/extension.h"
#include "extensions/renderer/extension_groups.h"
#include "extensions/renderer/script_context.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" #include "v8/include/v8.h"
namespace extensions { namespace extensions {
ScriptContextSet::ScriptContextSet() { ScriptContextSet::ScriptContextSet(ExtensionSet* extensions,
} ExtensionIdSet* active_extension_ids)
ScriptContextSet::~ScriptContextSet() { : extensions_(extensions), active_extension_ids_(active_extension_ids) {
} }
int ScriptContextSet::size() const { ScriptContextSet::~ScriptContextSet() {
return static_cast<int>(contexts_.size());
} }
void ScriptContextSet::Add(ScriptContext* context) { ScriptContext* ScriptContextSet::Register(
#if DCHECK_IS_ON() blink::WebLocalFrame* frame,
// It's OK to insert the same context twice, but we should only ever have const v8::Handle<v8::Context>& v8_context,
// one ScriptContext per v8::Context. int extension_group,
for (ContextSet::iterator iter = contexts_.begin(); iter != contexts_.end(); int world_id) {
++iter) { const Extension* extension =
ScriptContext* candidate = *iter; GetExtensionFromFrameAndWorld(frame, world_id, false);
if (candidate != context) const Extension* effective_extension =
DCHECK(candidate->v8_context() != context->v8_context()); GetExtensionFromFrameAndWorld(frame, world_id, true);
}
#endif GURL frame_url = ScriptContext::GetDataSourceURLForFrame(frame);
contexts_.insert(context); 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) { void ScriptContextSet::Remove(ScriptContext* context) {
...@@ -42,32 +58,25 @@ 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 { ScriptContext* ScriptContextSet::GetCurrent() const {
v8::Isolate* isolate = v8::Isolate::GetCurrent(); v8::Isolate* isolate = v8::Isolate::GetCurrent();
return isolate->InContext() ? GetByV8Context(isolate->GetCurrentContext()) return isolate->InContext() ? GetByV8Context(isolate->GetCurrentContext())
: NULL; : nullptr;
} }
ScriptContext* ScriptContextSet::GetCalling() const { ScriptContext* ScriptContextSet::GetCalling() const {
v8::Isolate* isolate = v8::Isolate::GetCurrent(); v8::Isolate* isolate = v8::Isolate::GetCurrent();
v8::Local<v8::Context> calling = isolate->GetCallingContext(); v8::Local<v8::Context> calling = isolate->GetCallingContext();
return calling.IsEmpty() ? NULL : GetByV8Context(calling); return calling.IsEmpty() ? nullptr : GetByV8Context(calling);
} }
ScriptContext* ScriptContextSet::GetByV8Context( ScriptContext* ScriptContextSet::GetByV8Context(
v8::Handle<v8::Context> v8_context) const { const v8::Handle<v8::Context>& v8_context) const {
for (ContextSet::const_iterator iter = contexts_.begin(); for (ScriptContext* script_context : contexts_) {
iter != contexts_.end(); if (script_context->v8_context() == v8_context)
++iter) { return script_context;
if ((*iter)->v8_context() == v8_context)
return *iter;
} }
return nullptr;
return NULL;
} }
void ScriptContextSet::ForEach( void ScriptContextSet::ForEach(
...@@ -76,11 +85,9 @@ void ScriptContextSet::ForEach( ...@@ -76,11 +85,9 @@ void ScriptContextSet::ForEach(
const base::Callback<void(ScriptContext*)>& callback) const { const base::Callback<void(ScriptContext*)>& callback) const {
// We copy the context list, because calling into javascript may modify it // We copy the context list, because calling into javascript may modify it
// out from under us. // out from under us.
ContextSet contexts = GetAll(); std::set<ScriptContext*> contexts_copy = contexts_;
for (ContextSet::iterator it = contexts.begin(); it != contexts.end(); ++it) {
ScriptContext* context = *it;
for (ScriptContext* context : contexts_copy) {
// For the same reason as above, contexts may become invalid while we run. // For the same reason as above, contexts may become invalid while we run.
if (!context->is_valid()) if (!context->is_valid())
continue; continue;
...@@ -102,23 +109,103 @@ void ScriptContextSet::ForEach( ...@@ -102,23 +109,103 @@ void ScriptContextSet::ForEach(
} }
} }
ScriptContextSet::ContextSet ScriptContextSet::OnExtensionUnloaded( std::set<ScriptContext*> ScriptContextSet::OnExtensionUnloaded(
const std::string& extension_id) { const std::string& extension_id) {
ContextSet contexts = GetAll(); std::set<ScriptContext*> removed;
ContextSet removed; ForEach(extension_id,
base::Bind(&ScriptContextSet::DispatchOnUnloadEventAndRemove,
// Clean up contexts belonging to the unloaded extension. This is done so base::Unretained(this), &removed));
// that content scripts (which remain injected into the page) don't continue return removed;
// receiving events and sending messages. }
for (ContextSet::iterator it = contexts.begin(); it != contexts.end(); ++it) {
if ((*it)->extension() && (*it)->extension()->id() == extension_id) { const Extension* ScriptContextSet::GetExtensionFromFrameAndWorld(
(*it)->DispatchOnUnloadEvent(); const blink::WebLocalFrame* frame,
removed.insert(*it); int world_id,
Remove(*it); 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 } // namespace extensions
...@@ -8,8 +8,12 @@ ...@@ -8,8 +8,12 @@
#include <set> #include <set>
#include <string> #include <string>
#include "base/basictypes.h" #include "base/macros.h"
#include "base/bind.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" #include "v8/include/v8.h"
class GURL; class GURL;
...@@ -18,39 +22,50 @@ namespace base { ...@@ -18,39 +22,50 @@ namespace base {
class ListValue; class ListValue;
} }
namespace content { namespace blink {
class RenderView; class WebLocalFrame;
class WebSecurityOrigin;
} }
namespace v8 { namespace content {
class Context; class RenderView;
} }
namespace extensions { namespace extensions {
class ScriptContext; class ScriptContext;
// A container of ExtensionBindingsContext. Since calling JavaScript within a // A container of ScriptContexts, responsible for both creating and managing
// context can cause any number of contexts to be created or destroyed, this // them.
// has additional smarts to help with the set changing underneath callers. //
// 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 { class ScriptContextSet {
public: 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(); ~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|. // Creates and starts managing a new ScriptContext. Ownership is held.
void Add(ScriptContext* context); // 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 // If the specified context is contained in this set, remove it, then delete
// it asynchronously. After this call returns the context object will still // it asynchronously. After this call returns the context object will still
// be valid, but its frame() pointer will be cleared. // be valid, but its frame() pointer will be cleared.
void Remove(ScriptContext* context); 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 // Gets the ScriptContext corresponding to v8::Context::GetCurrent(), or
// NULL if no such context exists. // NULL if no such context exists.
ScriptContext* GetCurrent() const; ScriptContext* GetCurrent() const;
...@@ -61,7 +76,7 @@ class ScriptContextSet { ...@@ -61,7 +76,7 @@ class ScriptContextSet {
// Gets the ScriptContext corresponding to the specified // Gets the ScriptContext corresponding to the specified
// v8::Context or NULL if no such context exists. // 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 // Synchronously runs |callback| with each ScriptContext that belongs to
// |extension_id| in |render_view|. // |extension_id| in |render_view|.
...@@ -88,10 +103,39 @@ class ScriptContextSet { ...@@ -88,10 +103,39 @@ class ScriptContextSet {
// Returns the set of ScriptContexts that were removed as a result. These // 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 // are safe to interact with until the end of the current event loop, since
// they're deleted asynchronously. // they're deleted asynchronously.
ContextSet OnExtensionUnloaded(const std::string& extension_id); std::set<ScriptContext*> OnExtensionUnloaded(const std::string& extension_id);
private: 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); DISALLOW_COPY_AND_ASSIGN(ScriptContextSet);
}; };
......
...@@ -2,61 +2,63 @@ ...@@ -2,61 +2,63 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
#include <vector>
#include "base/message_loop/message_loop.h" #include "base/message_loop/message_loop.h"
#include "extensions/common/extension.h" #include "extensions/common/extension.h"
#include "extensions/common/extension_set.h"
#include "extensions/common/features/feature.h" #include "extensions/common/features/feature.h"
#include "extensions/renderer/script_context.h" #include "extensions/renderer/script_context.h"
#include "extensions/renderer/script_context_set.h" #include "extensions/renderer/script_context_set.h"
#include "gin/public/context_holder.h" #include "gin/public/context_holder.h"
#include "testing/gtest/include/gtest/gtest.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/WebLocalFrame.h"
#include "third_party/WebKit/public/web/WebView.h" #include "third_party/WebKit/public/web/WebView.h"
#include "v8/include/v8.h" #include "v8/include/v8.h"
namespace extensions { namespace extensions {
TEST(ScriptContextSet, Lifecycle) { TEST(ScriptContextSetTest, Lifecycle) {
base::MessageLoop loop; base::MessageLoop loop;
ScriptContextSet context_set; blink::WebView* webview = blink::WebView::create(nullptr);
blink::WebLocalFrame* frame = blink::WebLocalFrame::create(nullptr);
webview->setMainFrame(frame);
// Do this after construction of the webview, since it may construct the
// Isolate.
v8::Isolate* isolate = v8::Isolate::GetCurrent(); v8::Isolate* isolate = v8::Isolate::GetCurrent();
v8::HandleScope handle_scope(isolate); v8::HandleScope handle_scope(isolate);
v8::Local<v8::Context> v8_context = v8::Context::New(isolate);
v8::Context::Scope context_scope(v8_context);
// ScriptContext relies on gin, it just doesn't look like it from here.
gin::ContextHolder context_holder(isolate); gin::ContextHolder context_holder(isolate);
context_holder.SetContext(v8::Context::New(isolate)); context_holder.SetContext(v8_context);
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); ExtensionSet extensions;
EXPECT_EQ(1u, context_set.GetAll().count(context)); ExtensionIdSet active_extensions;
EXPECT_EQ(context, context_set.GetByV8Context(context->v8_context())); ScriptContextSet context_set(&extensions, &active_extensions);
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 is valid and resembles correctness.
context_set.Add(context); EXPECT_TRUE(context->is_valid());
EXPECT_EQ(1u, context_set.GetAll().count(context)); 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. // Context has been correctly added.
ScriptContextSet::ContextSet set_copy = context_set.GetAll(); EXPECT_EQ(1u, context_set.size());
EXPECT_EQ(1u, set_copy.count(context)); EXPECT_EQ(context, context_set.GetByV8Context(v8_context));
// Test context is correctly removed.
context_set.Remove(context); context_set.Remove(context);
EXPECT_EQ(0, context_set.size()); EXPECT_EQ(0u, context_set.size());
EXPECT_FALSE(context_set.GetByV8Context(context->v8_context())); EXPECT_EQ(nullptr, context_set.GetByV8Context(v8_context));
EXPECT_EQ(1u, set_copy.size());
// After removal, the context should be marked for destruction. // After removal, the context should be invalid.
EXPECT_FALSE(context->web_frame()); EXPECT_FALSE(context->is_valid());
EXPECT_EQ(nullptr, context->web_frame());
// Run loop to do the actual deletion. // Run loop to do the actual deletion.
loop.RunUntilIdle(); loop.RunUntilIdle();
......
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