diff options
author | abarth@chromium.org <abarth@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-06-25 03:20:05 +0000 |
---|---|---|
committer | abarth@chromium.org <abarth@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-06-25 03:20:05 +0000 |
commit | 0bceb6c00f1177923efc41903de625ccc33d4428 (patch) | |
tree | e7c709bb819843d00f11dd32ae85ed5172a0ee3e | |
parent | d4cafafac04f16475b30346609c21dca6a8906d0 (diff) | |
download | chromium_src-0bceb6c00f1177923efc41903de625ccc33d4428.zip chromium_src-0bceb6c00f1177923efc41903de625ccc33d4428.tar.gz chromium_src-0bceb6c00f1177923efc41903de625ccc33d4428.tar.bz2 |
Run content scripts in their own isolated world. Hidden behind the --isolated-world command line argument to let us iterate on this feature.R=aaBUG=12218TEST=None :( (I have a testing plan we can put in place once the upstream half of this CL lands.)
Review URL: http://codereview.chromium.org/118188
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@19225 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | DEPS | 2 | ||||
-rw-r--r-- | base/base_switches.cc | 4 | ||||
-rw-r--r-- | base/base_switches.h | 1 | ||||
-rw-r--r-- | chrome/browser/renderer_host/browser_render_process_host.cc | 1 | ||||
-rw-r--r-- | webkit/glue/webframe_impl.cc | 7 | ||||
-rw-r--r-- | webkit/port/bindings/v8/v8_proxy.cpp | 315 | ||||
-rw-r--r-- | webkit/port/bindings/v8/v8_proxy.h | 11 | ||||
-rw-r--r-- | webkit/webkit.gyp | 2 |
8 files changed, 212 insertions, 131 deletions
@@ -1,7 +1,7 @@ vars = { "webkit_trunk": "http://svn.webkit.org/repository/webkit/trunk", - "webkit_revision": "45111", + "webkit_revision": "45134", } diff --git a/base/base_switches.cc b/base/base_switches.cc index 77d263c..46b7676 100644 --- a/base/base_switches.cc +++ b/base/base_switches.cc @@ -40,4 +40,8 @@ const wchar_t kEnableDCHECK[] = L"enable-dcheck"; // Available at http://www.adambarth.com/papers/2008/jackson-barth.pdf const wchar_t kForceHTTPS[] = L"force-https"; +// Run content scripts in their own isolated world instead of just in a new +// context. +const wchar_t kIsolatedWorld[] = L"isolated-world"; + } // namespace switches diff --git a/base/base_switches.h b/base/base_switches.h index 97a3691..7b66ad5 100644 --- a/base/base_switches.h +++ b/base/base_switches.h @@ -17,6 +17,7 @@ extern const wchar_t kNoErrorDialogs[]; extern const wchar_t kProcessType[]; extern const wchar_t kEnableDCHECK[]; extern const wchar_t kForceHTTPS[]; +extern const wchar_t kIsolatedWorld[]; } // namespace switches diff --git a/chrome/browser/renderer_host/browser_render_process_host.cc b/chrome/browser/renderer_host/browser_render_process_host.cc index 1c6fe9b..2630790 100644 --- a/chrome/browser/renderer_host/browser_render_process_host.cc +++ b/chrome/browser/renderer_host/browser_render_process_host.cc @@ -278,6 +278,7 @@ bool BrowserRenderProcessHost::Init() { switches::kDisableAudio, switches::kSimpleDataSource, switches::kEnableBenchmarking, + switches::kIsolatedWorld, }; for (size_t i = 0; i < arraysize(switch_names); ++i) { diff --git a/webkit/glue/webframe_impl.cc b/webkit/glue/webframe_impl.cc index 98e0afb..98c9b16 100644 --- a/webkit/glue/webframe_impl.cc +++ b/webkit/glue/webframe_impl.cc @@ -131,7 +131,9 @@ MSVC_POP_WARNING(); #undef LOG +#include "base/base_switches.h" #include "base/basictypes.h" +#include "base/command_line.h" #include "base/gfx/rect.h" #include "base/logging.h" #include "base/message_loop.h" @@ -1692,7 +1694,10 @@ void WebFrameImpl::ExecuteScriptInNewContext( sources_in[i].startLine)); } - frame_->script()->evaluateInNewContext(sources); + if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kIsolatedWorld)) + frame_->script()->evaluateInNewWorld(sources); + else + frame_->script()->evaluateInNewContext(sources); } std::wstring WebFrameImpl::GetName() { diff --git a/webkit/port/bindings/v8/v8_proxy.cpp b/webkit/port/bindings/v8/v8_proxy.cpp index 5f80a2d..f7ab5c2 100644 --- a/webkit/port/bindings/v8/v8_proxy.cpp +++ b/webkit/port/bindings/v8/v8_proxy.cpp @@ -40,6 +40,7 @@ #include "v8_binding.h" #include "V8Collection.h" #include "V8DOMWindow.h" +#include "V8IsolatedWorld.h" #include "ChromiumBridge.h" #include "CSSMutableStyleDeclaration.h" @@ -167,6 +168,7 @@ typedef HashMap<Node*, v8::Object*> DOMNodeMap; typedef HashMap<void*, v8::Object*> DOMObjectMap; #ifndef NDEBUG + static void EnumerateDOMObjectMap(DOMObjectMap& wrapper_map) { for (DOMObjectMap::iterator it = wrapper_map.begin(), end = wrapper_map.end(); @@ -179,16 +181,23 @@ static void EnumerateDOMObjectMap(DOMObjectMap& wrapper_map) } } +class DOMObjectVisitor : public DOMWrapperMap<void>::Visitor { + public: + void visitDOMWrapper(void* object, v8::Persistent<v8::Object> wrapper) { + V8ClassIndex::V8WrapperType type = V8Proxy::GetDOMWrapperType(wrapper); + USE_VAR(type); + USE_VAR(object); + } +}; -static void EnumerateDOMNodeMap(DOMNodeMap& node_map) -{ - for (DOMNodeMap::iterator it = node_map.begin(), end = node_map.end(); - it != end; ++it) { - Node* node = it->first; - USE_VAR(node); - ASSERT(v8::Persistent<v8::Object>(it->second).IsWeak()); +class EnsureWeakDOMNodeVisitor : public DOMWrapperMap<Node>::Visitor { + public: + void visitDOMWrapper(Node* object, v8::Persistent<v8::Object> wrapper) { + USE_VAR(object); + ASSERT(wrapper.IsWeak()); } -} +}; + #endif // NDEBUG #if ENABLE(SVG) @@ -324,32 +333,18 @@ void V8Proxy::GCUnprotect(void* dom_object) wrapper.Dispose(); } - -// Create object groups for DOM tree nodes. -static void GCPrologue() -{ - v8::HandleScope scope; - -#ifndef NDEBUG - EnumerateDOMObjectMap(getDOMObjectMap().impl()); -#endif - - // Run through all objects with possible pending activity making their - // wrappers non weak if there is pending activity. - DOMObjectMap active_map = getActiveDOMObjectMap().impl(); - for (DOMObjectMap::iterator it = active_map.begin(), end = active_map.end(); - it != end; ++it) { - void* obj = it->first; - v8::Persistent<v8::Object> wrapper = v8::Persistent<v8::Object>(it->second); +class GCPrologueVisitor : public DOMWrapperMap<void>::Visitor { + public: + void visitDOMWrapper(void* object, v8::Persistent<v8::Object> wrapper) { ASSERT(wrapper.IsWeak()); V8ClassIndex::V8WrapperType type = V8Proxy::GetDOMWrapperType(wrapper); switch (type) { -#define MAKE_CASE(TYPE, NAME) \ - case V8ClassIndex::TYPE: { \ - NAME* impl = static_cast<NAME*>(obj); \ - if (impl->hasPendingActivity()) \ - wrapper.ClearWeak(); \ - break; \ +#define MAKE_CASE(TYPE, NAME) \ + case V8ClassIndex::TYPE: { \ + NAME* impl = static_cast<NAME*>(object); \ + if (impl->hasPendingActivity()) \ + wrapper.ClearWeak(); \ + break; \ } ACTIVE_DOM_OBJECT_TYPES(MAKE_CASE) default: @@ -365,7 +360,7 @@ ACTIVE_DOM_OBJECT_TYPES(MAKE_CASE) // GC even though their entanglement most likely is still the same. if (type == V8ClassIndex::MESSAGEPORT) { // Get the port and its entangled port. - MessagePort* port1 = static_cast<MessagePort*>(obj); + MessagePort* port1 = static_cast<MessagePort*>(object); MessagePort* port2 = port1->locallyEntangledPort(); // If we are remotely entangled, then mark this object as reachable @@ -401,19 +396,36 @@ ACTIVE_DOM_OBJECT_TYPES(MAKE_CASE) } } } +}; - // Create object groups. - typedef std::pair<uintptr_t, Node*> GrouperPair; - typedef Vector<GrouperPair> GrouperList; +class GrouperItem { + public: + GrouperItem(uintptr_t group_id, Node* node, v8::Persistent<v8::Object> wrapper) + : group_id_(group_id), node_(node), wrapper_(wrapper) { } + + uintptr_t group_id() const { return group_id_; } + Node* node() const { return node_; } + v8::Persistent<v8::Object> wrapper() const { return wrapper_; } + + private: + uintptr_t group_id_; + Node* node_; + v8::Persistent<v8::Object> wrapper_; +}; - DOMNodeMap node_map = getDOMNodeMap().impl(); - GrouperList grouper; - grouper.reserveCapacity(node_map.size()); +bool operator<(const GrouperItem& a, const GrouperItem& b) { + return a.group_id() < b.group_id(); +} - for (DOMNodeMap::iterator it = node_map.begin(), end = node_map.end(); - it != end; ++it) { - Node* node = it->first; +typedef Vector<GrouperItem> GrouperList; +class ObjectGrouperVisitor : public DOMWrapperMap<Node>::Visitor { + public: + ObjectGrouperVisitor() { + // TODO(abarth): grouper_.reserveCapacity(node_map.size()); ? + } + + void visitDOMWrapper(Node* node, v8::Persistent<v8::Object> wrapper) { // If the node is in document, put it in the ownerDocument's object group. // // If an image element was created by JavaScript "new Image", @@ -435,85 +447,106 @@ ACTIVE_DOM_OBJECT_TYPES(MAKE_CASE) // If the node is alone in its DOM tree (doesn't have a parent or any // children) then the group will be filtered out later anyway. if (root == node && !node->hasChildNodes()) - continue; + return; group_id = reinterpret_cast<uintptr_t>(root); } - grouper.append(GrouperPair(group_id, node)); + grouper_.append(GrouperItem(group_id, node, wrapper)); } - // Group by sorting by the group id. This will use the std::pair operator<, - // which will really sort by both the group id and the Node*. However the - // Node* is only involved to sort within a group id, so it will be fine. - std::sort(grouper.begin(), grouper.end()); - - // TODO(deanm): Should probably work in iterators here, but indexes were - // easier for my simple mind. - for (size_t i = 0; i < grouper.size(); ) { - // Seek to the next key (or the end of the list). - size_t next_key_index = grouper.size(); - for (size_t j = i; j < grouper.size(); ++j) { - if (grouper[i].first != grouper[j].first) { - next_key_index = j; - break; + void ApplyGrouping() { + // Group by sorting by the group id. + std::sort(grouper_.begin(), grouper_.end()); + + // TODO(deanm): Should probably work in iterators here, but indexes were + // easier for my simple mind. + for (size_t i = 0; i < grouper_.size(); ) { + // Seek to the next key (or the end of the list). + size_t next_key_index = grouper_.size(); + for (size_t j = i; j < grouper_.size(); ++j) { + if (grouper_[i].group_id() != grouper_[j].group_id()) { + next_key_index = j; + break; + } } - } - ASSERT(next_key_index > i); + ASSERT(next_key_index > i); - // We only care about a group if it has more than one object. If it only - // has one object, it has nothing else that needs to be kept alive. - if (next_key_index - i <= 1) { - i = next_key_index; - continue; - } + // We only care about a group if it has more than one object. If it only + // has one object, it has nothing else that needs to be kept alive. + if (next_key_index - i <= 1) { + i = next_key_index; + continue; + } - Vector<v8::Persistent<v8::Value> > group; - group.reserveCapacity(next_key_index - i); - for (; i < next_key_index; ++i) { - Node* node = grouper[i].second; - v8::Persistent<v8::Value> wrapper = - getDOMNodeMap().get(node); - if (!wrapper.IsEmpty()) - group.append(wrapper); - // If the node is styled and there is a wrapper for the inline - // style declaration, we need to keep that style declaration - // wrapper alive as well, so we add it to the object group. - if (node->isStyledElement()) { - StyledElement* element = reinterpret_cast<StyledElement*>(node); - CSSStyleDeclaration* style = element->inlineStyleDecl(); - if (style != NULL) { - wrapper = getDOMObjectMap().get(style); - if (!wrapper.IsEmpty()) - group.append(wrapper); + Vector<v8::Persistent<v8::Value> > group; + group.reserveCapacity(next_key_index - i); + for (; i < next_key_index; ++i) { + Node* node = grouper_[i].node(); + v8::Persistent<v8::Value> wrapper = grouper_[i].wrapper(); + if (!wrapper.IsEmpty()) + group.append(wrapper); + /* TODO(abarth): Re-enabled this code to avoid GCing these wrappers! + Currently this depends on looking up the wrapper + during a GC, but we don't know which isolated world + we're in, so it's unclear which map to look in... + + // If the node is styled and there is a wrapper for the inline + // style declaration, we need to keep that style declaration + // wrapper alive as well, so we add it to the object group. + if (node->isStyledElement()) { + StyledElement* element = reinterpret_cast<StyledElement*>(node); + CSSStyleDeclaration* style = element->inlineStyleDecl(); + if (style != NULL) { + wrapper = getDOMObjectMap().get(style); + if (!wrapper.IsEmpty()) + group.append(wrapper); + } } + */ } - } - if (group.size() > 1) - v8::V8::AddObjectGroup(&group[0], group.size()); + if (group.size() > 1) + v8::V8::AddObjectGroup(&group[0], group.size()); - ASSERT(i == next_key_index); + ASSERT(i == next_key_index); + } } -} - + + private: + GrouperList grouper_; +}; -static void GCEpilogue() +// Create object groups for DOM tree nodes. +static void GCPrologue() { v8::HandleScope scope; - // Run through all objects with pending activity making their wrappers weak - // again. - DOMObjectMap active_map = getActiveDOMObjectMap().impl(); - for (DOMObjectMap::iterator it = active_map.begin(), end = active_map.end(); - it != end; ++it) { - void* obj = it->first; - v8::Persistent<v8::Object> wrapper = v8::Persistent<v8::Object>(it->second); +#ifndef NDEBUG + DOMObjectVisitor domObjectVisitor; + visitDOMObjectsInCurrentThread(&domObjectVisitor); +#endif + + // Run through all objects with possible pending activity making their + // wrappers non weak if there is pending activity. + GCPrologueVisitor prologueVisitor; + visitActiveDOMObjectsInCurrentThread(&prologueVisitor); + + // Create object groups. + ObjectGrouperVisitor objectGrouperVisitor; + visitDOMNodesInCurrentThread(&objectGrouperVisitor); + objectGrouperVisitor.ApplyGrouping(); +} + +class GCEpilogueVisitor : public DOMWrapperMap<void>::Visitor { + public: + void visitDOMWrapper(void* object, v8::Persistent<v8::Object> wrapper) + { V8ClassIndex::V8WrapperType type = V8Proxy::GetDOMWrapperType(wrapper); switch (type) { #define MAKE_CASE(TYPE, NAME) \ case V8ClassIndex::TYPE: { \ - NAME* impl = static_cast<NAME*>(obj); \ + NAME* impl = static_cast<NAME*>(object); \ if (impl->hasPendingActivity()) { \ ASSERT(!wrapper.IsWeak()); \ wrapper.MakeWeak(impl, &weakActiveDOMObjectCallback); \ @@ -526,11 +559,25 @@ ACTIVE_DOM_OBJECT_TYPES(MAKE_CASE) #undef MAKE_CASE } } +}; + +static void GCEpilogue() +{ + v8::HandleScope scope; + + // Run through all objects with pending activity making their wrappers weak + // again. + GCEpilogueVisitor epilogueVisitor; + visitActiveDOMObjectsInCurrentThread(&epilogueVisitor); #ifndef NDEBUG // Check all survivals are weak. - EnumerateDOMObjectMap(getDOMObjectMap().impl()); - EnumerateDOMNodeMap(getDOMNodeMap().impl()); + DOMObjectVisitor domObjectVisitor; + visitDOMObjectsInCurrentThread(&domObjectVisitor); + + EnsureWeakDOMNodeVisitor weakDOMNodeVisitor; + visitDOMNodesInCurrentThread(&weakDOMNodeVisitor); + EnumerateDOMObjectMap(gc_protected_map()); EnumerateGlobalHandles(); #undef USE_VAR @@ -1005,6 +1052,12 @@ bool V8Proxy::HandleOutOfMemory() return true; } +void V8Proxy::evaluateInNewWorld(const Vector<ScriptSourceCode>& sources) +{ + InitContextIfNeeded(); + V8IsolatedWorld::evaluate(sources, this); +} + void V8Proxy::evaluateInNewContext(const Vector<ScriptSourceCode>& sources) { InitContextIfNeeded(); @@ -2131,6 +2184,38 @@ v8::Persistent<v8::Context> V8Proxy::createNewContext( return result; } +bool V8Proxy::installDOMWindow(v8::Handle<v8::Context> context, + DOMWindow* window) +{ + v8::Handle<v8::String> implicit_proto_string = v8::String::New("__proto__"); + if (implicit_proto_string.IsEmpty()) + return false; + + // Create a new JS window object and use it as the prototype for the + // shadow global object. + v8::Handle<v8::Function> window_constructor = + GetConstructor(V8ClassIndex::DOMWINDOW); + v8::Local<v8::Object> js_window = + SafeAllocation::NewInstance(window_constructor); + // Bail out if allocation failed. + if (js_window.IsEmpty()) + return false; + + // Wrap the window. + SetDOMWrapper(js_window, + V8ClassIndex::ToInt(V8ClassIndex::DOMWINDOW), + window); + + window->ref(); + V8Proxy::SetJSWrapperForDOMObject(window, + v8::Persistent<v8::Object>::New(js_window)); + + // Insert the window instance as the prototype of the shadow object. + v8::Handle<v8::Object> v8_global = context->Global(); + v8_global->Set(implicit_proto_string, js_window); + return true; +} + // Create a new environment and setup the global object. // // The global object corresponds to a DOMWindow instance. However, to @@ -2218,11 +2303,9 @@ void V8Proxy::InitContextIfNeeded() // Allocate strings used during initialization. v8::Handle<v8::String> object_string = v8::String::New("Object"); v8::Handle<v8::String> prototype_string = v8::String::New("prototype"); - v8::Handle<v8::String> implicit_proto_string = v8::String::New("__proto__"); // Bail out if allocation failed. if (object_string.IsEmpty() || - prototype_string.IsEmpty() || - implicit_proto_string.IsEmpty()) { + prototype_string.IsEmpty()) { DisposeContextHandles(); return; } @@ -2244,32 +2327,8 @@ void V8Proxy::InitContextIfNeeded() RegisterGlobalHandle(PROXY, this, m_wrapper_boilerplates); #endif - // Create a new JS window object and use it as the prototype for the - // shadow global object. - v8::Handle<v8::Function> window_constructor = - GetConstructor(V8ClassIndex::DOMWINDOW); - v8::Local<v8::Object> js_window = - SafeAllocation::NewInstance(window_constructor); - // Bail out if allocation failed. - if (js_window.IsEmpty()) { + if (!installDOMWindow(context, m_frame->domWindow())) DisposeContextHandles(); - return; - } - - DOMWindow* window = m_frame->domWindow(); - - // Wrap the window. - SetDOMWrapper(js_window, - V8ClassIndex::ToInt(V8ClassIndex::DOMWINDOW), - window); - - window->ref(); - V8Proxy::SetJSWrapperForDOMObject(window, - v8::Persistent<v8::Object>::New(js_window)); - - // Insert the window instance as the prototype of the shadow object. - v8::Handle<v8::Object> v8_global = context->Global(); - v8_global->Set(implicit_proto_string, js_window); updateDocument(); diff --git a/webkit/port/bindings/v8/v8_proxy.h b/webkit/port/bindings/v8/v8_proxy.h index 6bfff64..2353420 100644 --- a/webkit/port/bindings/v8/v8_proxy.h +++ b/webkit/port/bindings/v8/v8_proxy.h @@ -242,6 +242,12 @@ class V8Proxy { void setEventHandlerLineno(int lineno) { m_handlerLineno = lineno; } void finishedWithEvent(Event* event) { } + // Evaluate JavaScript in a new isolated world. The script gets its own + // global scope, its own prototypes for intrinsic JavaScript objects (String, + // Array, and so-on), and its own wrappers for all DOM nodes and DOM + // constructors. + void evaluateInNewWorld(const Vector<ScriptSourceCode>& sources); + // Evaluate JavaScript in a new context. The script gets its own global scope // and its own prototypes for intrinsic JavaScript objects (String, Array, // and so-on). It shares the wrappers for all DOM nodes and DOM constructors. @@ -503,11 +509,14 @@ class V8Proxy { static void* ToSVGPODTypeImpl(V8ClassIndex::V8WrapperType type, v8::Handle<v8::Value> object); + // TODO(abarth): Separate these concerns from V8Proxy? + v8::Persistent<v8::Context> createNewContext(v8::Handle<v8::Object> global); + bool installDOMWindow(v8::Handle<v8::Context> context, DOMWindow* window); + private: static const char* kContextDebugDataType; static const char* kContextDebugDataValue; - v8::Persistent<v8::Context> createNewContext(v8::Handle<v8::Object> global); void InitContextIfNeeded(); void DisconnectEventListeners(); void SetSecurityToken(); diff --git a/webkit/webkit.gyp b/webkit/webkit.gyp index 7da230e..e7b3dfe 100644 --- a/webkit/webkit.gyp +++ b/webkit/webkit.gyp @@ -1120,6 +1120,8 @@ '../third_party/WebKit/WebCore/bindings/v8/V8EventListenerList.h', '../third_party/WebKit/WebCore/bindings/v8/V8Helpers.cpp', '../third_party/WebKit/WebCore/bindings/v8/V8Helpers.h', + '../third_party/WebKit/WebCore/bindings/v8/V8IsolatedWorld.cpp', + '../third_party/WebKit/WebCore/bindings/v8/V8IsolatedWorld.h', '../third_party/WebKit/WebCore/bindings/v8/V8LazyEventListener.cpp', '../third_party/WebKit/WebCore/bindings/v8/V8LazyEventListener.h', '../third_party/WebKit/WebCore/bindings/v8/V8NodeFilterCondition.cpp', |