summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorabarth@chromium.org <abarth@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-06-25 03:20:05 +0000
committerabarth@chromium.org <abarth@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-06-25 03:20:05 +0000
commit0bceb6c00f1177923efc41903de625ccc33d4428 (patch)
treee7c709bb819843d00f11dd32ae85ed5172a0ee3e
parentd4cafafac04f16475b30346609c21dca6a8906d0 (diff)
downloadchromium_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--DEPS2
-rw-r--r--base/base_switches.cc4
-rw-r--r--base/base_switches.h1
-rw-r--r--chrome/browser/renderer_host/browser_render_process_host.cc1
-rw-r--r--webkit/glue/webframe_impl.cc7
-rw-r--r--webkit/port/bindings/v8/v8_proxy.cpp315
-rw-r--r--webkit/port/bindings/v8/v8_proxy.h11
-rw-r--r--webkit/webkit.gyp2
8 files changed, 212 insertions, 131 deletions
diff --git a/DEPS b/DEPS
index 04dee55..80b437a 100644
--- a/DEPS
+++ b/DEPS
@@ -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',