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
Esse commit está contido em:
abarth@chromium.org
2009-06-25 03:20:05 +00:00
commit 5e8a73bd7a
8 arquivos alterados com 217 adições e 136 exclusões
+1 -1
Ver Arquivo
@@ -1,7 +1,7 @@
vars = {
"webkit_trunk":
"http://svn.webkit.org/repository/webkit/trunk",
"webkit_revision": "45111",
"webkit_revision": "45134",
}
+4
Ver Arquivo
@@ -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
+1
Ver Arquivo
@@ -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
@@ -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) {
+6 -1
Ver Arquivo
@@ -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() {
+192 -133
Ver Arquivo
@@ -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)
}
}
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 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);
}
}
};
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_; }
DOMNodeMap node_map = getDOMNodeMap().impl();
GrouperList grouper;
grouper.reserveCapacity(node_map.size());
private:
uintptr_t group_id_;
Node* node_;
v8::Persistent<v8::Object> wrapper_;
};
for (DOMNodeMap::iterator it = node_map.begin(), end = node_map.end();
it != end; ++it) {
Node* node = it->first;
bool operator<(const GrouperItem& a, const GrouperItem& b) {
return a.group_id() < b.group_id();
}
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());
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].first != grouper[j].first) {
next_key_index = j;
break;
}
}
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;
}
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);
// 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);
// 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].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());
ASSERT(i == next_key_index);
}
if (group.size() > 1)
v8::V8::AddObjectGroup(&group[0], group.size());
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();
+10 -1
Ver Arquivo
@@ -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();
+2
Ver Arquivo
@@ -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',