Commit 0ad18647 authored by aa@chromium.org's avatar aa@chromium.org
Browse files

Update isolated worlds under v8 to support world reuse.

https://bugs.webkit.org/show_bug.cgi?id=30145

Patch by Aaron Boodman <aa@chromium.org> on 2009-10-07
Reviewed by Adam Barth.

WebCore: 

* bindings/v8/ScriptController.cpp:
Replace evaluateInNewIsolatedWorld() withe evaluateInIsolatedWorld(id).
(WebCore::ScriptController::evaluateInIsolatedWorld):
* bindings/v8/ScriptController.h: Ditto.
* bindings/v8/V8Proxy.cpp: Ditto.
(WebCore::V8Proxy::~V8Proxy): Dittio.
(WebCore::V8Proxy::evaluateInIsolatedWorld): Ditto.
* bindings/v8/V8Proxy.h: Ditto.
* bindings/v8/V8IsolatedWorld.cpp:
Add support for controlling lifetime of a world, rather than relying on GC.
(WebCore::V8IsolatedWorld::contextWeakReferenceCallback): Ditto.
(WebCore::V8IsolatedWorld::V8IsolatedWorld): Ditto.
(WebCore::V8IsolatedWorld::destroy): Ditto.
* bindings/v8/V8IsolatedWorld.h: Ditto.

LayoutTests: 

* http/tests/security/isolatedWorld/iframe.html: Added.
* http/tests/security/isolatedWorld/world-reuse-expected.txt: Added.
* http/tests/security/isolatedWorld/world-reuse.html: Added.



git-svn-id: http://svn.webkit.org/repository/webkit/trunk@49278 268f45cc-cd09-0410-ab3c-d52691b4dbfc
parent 2a943cc0
2009-10-07 Aaron Boodman <aa@chromium.org>
Reviewed by Adam Barth.
Update isolated worlds under v8 to support world reuse.
https://bugs.webkit.org/show_bug.cgi?id=30145
* http/tests/security/isolatedWorld/iframe.html: Added.
* http/tests/security/isolatedWorld/world-reuse-expected.txt: Added.
* http/tests/security/isolatedWorld/world-reuse.html: Added.
2009-10-07 Brady Eidson <beidson@apple.com>
 
Reviewed by Darin Adler.
......
<!DOCTYPE html>
<html>
<body>
<div id="output"></div>
<script>
window.focus();
// This should not pick up the 'bar' defined in the isolated world from the parent
// document, or the 'hotdog' defined in previous runs of this frame.
layoutTestController.evaluateScriptInIsolatedWorld(1,
"document.getElementById('output').appendChild(document.createTextNode(window.bar + ',' + window.hotdog));");
// Next time we run, we also shouldn't see this 'hotdog'.
layoutTestController.evaluateScriptInIsolatedWorld(1, "var hotdog = 'hotdog';");
parent.iframeComplete(document.getElementById("output").textContent);
</script>
</body>
</html>
Expecting undefined: undefined
Expecting bar: bar
Expecting undefined: undefined
Expecting undefined,undefined: undefined,undefined
Expecting undefined,undefined: undefined,undefined
<!DOCTYPE html>
<html>
<body>
<script>
if (window.layoutTestController) {
layoutTestController.dumpAsText();
layoutTestController.waitUntilDone();
// World id 0 is never saved, so this should print 'undefined'.
layoutTestController.evaluateScriptInIsolatedWorld(0, "var foo = 'foo';");
layoutTestController.evaluateScriptInIsolatedWorld(0,
"document.body.appendChild(document.createTextNode('Expecting undefined: ' + window.foo));" +
"document.body.appendChild(document.createElement('br'));");
// Since these two run in the same world, this should print 'bar'.
layoutTestController.evaluateScriptInIsolatedWorld(1, "var bar = 'bar';");
layoutTestController.evaluateScriptInIsolatedWorld(1,
"document.body.appendChild(document.createTextNode('Expecting bar: ' + window.bar));" +
"document.body.appendChild(document.createElement('br'));");
// Since these two are in different worlds, this should print 'undefined'.
layoutTestController.evaluateScriptInIsolatedWorld(2, "var baz = 'baz';");
layoutTestController.evaluateScriptInIsolatedWorld(3,
"document.body.appendChild(document.createTextNode('Expecting undefined: ' + window.baz));" +
"document.body.appendChild(document.createElement('br'));");
// Worlds should not be shared across documents, so another frame that uses the same
// world id should not see our variables.
var iframe = document.createElement("iframe");
document.body.insertBefore(iframe, document.body.firstChild);
document.body.insertBefore(document.createElement("br"), iframe.nextSibling);
var iframeComplete = function(result) {
document.body.appendChild(document.createTextNode('Expecting undefined,undefined: ' + result));
document.body.appendChild(document.createElement('br'));
reloadFrame();
}
iframe.src = "iframe.html";
// Also, navigating a single frame should not result in sharing variables.
function reloadFrame() {
iframeComplete = function(result) {
document.body.appendChild(document.createTextNode('Expecting undefined,undefined: ' + result));
document.body.appendChild(document.createElement('br'));
layoutTestController.notifyDone();
}
iframe.contentWindow.location.reload(true);
}
}
</script>
</body>
</html>
......@@ -5389,6 +5389,7 @@ http/tests/security/isolatedWorld/number-prototype.html
http/tests/security/isolatedWorld/object-prototype.html
http/tests/security/isolatedWorld/string-prototype.html
http/tests/security/isolatedWorld/window-properties.html
http/tests/security/isolatedWorld/world-reuse.html
# Missing DRT ability return a null request for willSendRequest redirect calls
http/tests/misc/will-send-request-returns-null-on-redirect.html
......
......@@ -89,6 +89,7 @@ http/tests/security/isolatedWorld/number-prototype.html
http/tests/security/isolatedWorld/object-prototype.html
http/tests/security/isolatedWorld/string-prototype.html
http/tests/security/isolatedWorld/window-properties.html
http/tests/security/isolatedWorld/world-reuse.html
# New test with questionable results
# See https://bugs.webkit.org/show_bug.cgi?id=28079 for explanation.
......
......@@ -80,6 +80,7 @@ http/tests/security/isolatedWorld/number-prototype.html
http/tests/security/isolatedWorld/object-prototype.html
http/tests/security/isolatedWorld/string-prototype.html
http/tests/security/isolatedWorld/window-properties.html
http/tests/security/isolatedWorld/world-reuse.html
# Missing DRT ability return a null request for willSendRequest redirect calls
http/tests/misc/will-send-request-returns-null-on-redirect.html
......
......@@ -618,6 +618,7 @@ http/tests/security/isolatedWorld/number-prototype.html
http/tests/security/isolatedWorld/object-prototype.html
http/tests/security/isolatedWorld/string-prototype.html
http/tests/security/isolatedWorld/window-properties.html
http/tests/security/isolatedWorld/world-reuse.html
# New test with questionable results
# See https://bugs.webkit.org/show_bug.cgi?id=28079 for explanation.
......
2009-10-07 Aaron Boodman <aa@chromium.org>
Reviewed by Adam Barth.
Update isolated worlds under v8 to support world reuse.
https://bugs.webkit.org/show_bug.cgi?id=30145
* bindings/v8/ScriptController.cpp:
Replace evaluateInNewIsolatedWorld() withe evaluateInIsolatedWorld(id).
(WebCore::ScriptController::evaluateInIsolatedWorld):
* bindings/v8/ScriptController.h: Ditto.
* bindings/v8/V8Proxy.cpp: Ditto.
(WebCore::V8Proxy::~V8Proxy): Dittio.
(WebCore::V8Proxy::evaluateInIsolatedWorld): Ditto.
* bindings/v8/V8Proxy.h: Ditto.
* bindings/v8/V8IsolatedWorld.cpp:
Add support for controlling lifetime of a world, rather than relying on GC.
(WebCore::V8IsolatedWorld::contextWeakReferenceCallback): Ditto.
(WebCore::V8IsolatedWorld::V8IsolatedWorld): Ditto.
(WebCore::V8IsolatedWorld::destroy): Ditto.
* bindings/v8/V8IsolatedWorld.h: Ditto.
2009-10-07 Jeremy Orlow <jorlow@chromium.org>
 
Build fix for http://trac.webkit.org/changeset/49272 on Windows
......@@ -181,13 +181,12 @@ bool ScriptController::processingUserGesture() const
void ScriptController::evaluateInIsolatedWorld(unsigned worldID, const Vector<ScriptSourceCode>& sources)
{
// FIXME: Get rid of extensionGroup here.
m_proxy->evaluateInNewWorld(sources, 1);
m_proxy->evaluateInIsolatedWorld(worldID, sources, 0);
}
void ScriptController::evaluateInNewWorld(const Vector<ScriptSourceCode>& sources, int extensionGroup)
void ScriptController::evaluateInIsolatedWorld(unsigned worldID, const Vector<ScriptSourceCode>& sources, int extensionGroup)
{
m_proxy->evaluateInNewWorld(sources, extensionGroup);
m_proxy->evaluateInIsolatedWorld(worldID, sources, extensionGroup);
}
void ScriptController::evaluateInNewContext(const Vector<ScriptSourceCode>& sources, int extensionGroup)
......
......@@ -65,13 +65,18 @@ namespace WebCore {
ScriptValue evaluate(const ScriptSourceCode&);
void evaluateInIsolatedWorld(unsigned worldID, const Vector<ScriptSourceCode>&);
// Executes JavaScript in a new world associated with the web frame. 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.
// FIXME: Move to using evaluateInIsolatedWorld instead.
void evaluateInNewWorld(const Vector<ScriptSourceCode>&, int extensionGroup);
// Executes JavaScript in an 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.
//
// If an isolated world with the specified ID already exists, it is reused.
// Otherwise, a new world is created.
//
// If the worldID is 0, a new world is always created.
//
// FIXME: Get rid of extensionGroup here.
void evaluateInIsolatedWorld(unsigned worldID, const Vector<ScriptSourceCode>&, int extensionGroup);
// Executes JavaScript in a new context associated with the web frame. The
// script gets its own global scope and its own prototypes for intrinsic
......
......@@ -45,32 +45,27 @@ namespace WebCore {
int V8IsolatedWorld::isolatedWorldCount = 0;
static void contextWeakReferenceCallback(v8::Persistent<v8::Value> object, void* isolated_world)
void V8IsolatedWorld::contextWeakReferenceCallback(v8::Persistent<v8::Value> object, void* isolated_world)
{
// Our context is going away. Time to clean up the world.
V8IsolatedWorld* world = static_cast<V8IsolatedWorld*>(isolated_world);
delete world;
}
void V8IsolatedWorld::evaluate(const Vector<ScriptSourceCode>& sources, V8Proxy* proxy, int extensionGroup)
V8IsolatedWorld::V8IsolatedWorld(V8Proxy* proxy, int extensionGroup)
{
++isolatedWorldCount;
v8::HandleScope scope;
v8::Persistent<v8::Context> context = proxy->createNewContext(v8::Handle<v8::Object>(), extensionGroup);
m_context = SharedPersistent<v8::Context>::create(proxy->createNewContext(v8::Handle<v8::Object>(), extensionGroup));
// Run code in the new context.
v8::Context::Scope context_scope(context);
v8::Context::Scope context_scope(m_context->get());
// The lifetime of this object is controlled by the V8 GC.
// We need to create the world before touching DOM wrappers.
V8IsolatedWorld* world = new V8IsolatedWorld(context);
m_context->get()->Global()->SetHiddenValue(V8HiddenPropertyName::isolatedWorld(), v8::External::Wrap(this));
V8Proxy::installHiddenObjectPrototype(context);
proxy->installDOMWindow(context, proxy->frame()->domWindow());
proxy->frame()->loader()->client()->didCreateIsolatedScriptContext();
for (size_t i = 0; i < sources.size(); ++i)
proxy->evaluate(sources[i], 0);
V8Proxy::installHiddenObjectPrototype(m_context->get());
proxy->installDOMWindow(m_context->get(), proxy->frame()->domWindow());
// Using the default security token means that the canAccess is always
// called, which is slow.
......@@ -78,20 +73,14 @@ void V8IsolatedWorld::evaluate(const Vector<ScriptSourceCode>& sources, V8Proxy*
// created contexts so that they can all be updated when the
// document domain
// changes.
// FIXME: Move this statement above proxy->evaluate? It seems like we
// should set up the token before running the script.
context->UseDefaultSecurityToken();
m_context->get()->UseDefaultSecurityToken();
context.Dispose();
// WARNING! This might well delete |world|.
proxy->frame()->loader()->client()->didCreateIsolatedScriptContext();
}
V8IsolatedWorld::V8IsolatedWorld(v8::Handle<v8::Context> context)
: m_context(SharedPersistent<v8::Context>::create(v8::Persistent<v8::Context>::New(context)))
void V8IsolatedWorld::destroy()
{
++isolatedWorldCount;
m_context->get().MakeWeak(this, &contextWeakReferenceCallback);
context->Global()->SetHiddenValue(V8HiddenPropertyName::isolatedWorld(), v8::External::Wrap(this));
}
V8IsolatedWorld::~V8IsolatedWorld()
......
......@@ -58,11 +58,14 @@ namespace WebCore {
//
class V8IsolatedWorld {
public:
~V8IsolatedWorld();
// Creates an isolated world. To destroy it, call destroy().
// This will delete the isolated world when the context it owns is GC'd.
V8IsolatedWorld(V8Proxy* proxy, int extensionGroup);
// Evaluate JavaScript in a new isolated world. The script has access
// to the DOM of the document associated with |proxy|.
static void evaluate(const Vector<ScriptSourceCode>& sources, V8Proxy* proxy, int extensionGroup);
// Call this to destroy the isolated world. It will be deleted sometime
// after this call, once all script references to the world's context
// have been dropped.
void destroy();
// Returns the isolated world associated with
// v8::Context::GetEntered(). Because worlds are isolated, the entire
......@@ -90,12 +93,13 @@ namespace WebCore {
DOMDataStore* getDOMDataStore() const { return m_domDataStore.getStore(); }
private:
~V8IsolatedWorld();
static V8IsolatedWorld* getEnteredImpl();
// The lifetime of an isolated world is managed by the V8 garbage
// collector. In particular, the object created by this constructor is
// freed when |context| is garbage collected.
explicit V8IsolatedWorld(v8::Handle<v8::Context> context);
// Called by the garbage collector when our JavaScript context is about
// to be destroyed.
static void contextWeakReferenceCallback(v8::Persistent<v8::Value> object, void* isolated_world);
// The v8::Context for the isolated world. This object is keep on the
// heap as long as |m_context| has not been garbage collected.
......
......@@ -274,10 +274,32 @@ bool V8Proxy::handleOutOfMemory()
return true;
}
void V8Proxy::evaluateInNewWorld(const Vector<ScriptSourceCode>& sources, int extensionGroup)
void V8Proxy::evaluateInIsolatedWorld(int worldID, const Vector<ScriptSourceCode>& sources, int extensionGroup)
{
initContextIfNeeded();
V8IsolatedWorld::evaluate(sources, this, extensionGroup);
v8::HandleScope handleScope;
V8IsolatedWorld* world = 0;
if (worldID > 0) {
IsolatedWorldMap::iterator iter = m_isolatedWorlds.find(worldID);
if (iter != m_isolatedWorlds.end()) {
world = iter->second;
} else {
world = new V8IsolatedWorld(this, extensionGroup);
m_isolatedWorlds.set(worldID, world);
}
} else {
world = new V8IsolatedWorld(this, extensionGroup);
}
v8::Local<v8::Context> context = v8::Local<v8::Context>::New(world->context());
v8::Context::Scope context_scope(context);
for (size_t i = 0; i < sources.size(); ++i)
evaluate(sources[i], 0);
if (worldID == 0)
world->destroy();
}
void V8Proxy::evaluateInNewContext(const Vector<ScriptSourceCode>& sources, int extensionGroup)
......@@ -708,9 +730,20 @@ void V8Proxy::disconnectEventListeners()
m_listenerGuard->disconnectListeners();
m_listenerGuard = V8ListenerGuard::create();
}
void V8Proxy::resetIsolatedWorlds()
{
for (IsolatedWorldMap::iterator iter = m_isolatedWorlds.begin();
iter != m_isolatedWorlds.end(); ++iter) {
iter->second->destroy();
}
m_isolatedWorlds.clear();
}
void V8Proxy::clearForClose()
{
resetIsolatedWorlds();
if (!context().IsEmpty()) {
v8::HandleScope handleScope;
......@@ -722,6 +755,8 @@ void V8Proxy::clearForClose()
void V8Proxy::clearForNavigation()
{
disconnectEventListeners();
resetIsolatedWorlds();
if (!context().IsEmpty()) {
v8::HandleScope handle;
clearDocumentWrapper();
......
......@@ -58,6 +58,7 @@ namespace WebCore {
class ScriptExecutionContext;
class String;
class V8EventListener;
class V8IsolatedWorld;
// FIXME: use standard logging facilities in WebCore.
void logInfo(Frame*, const String& message, const String& url);
......@@ -166,7 +167,7 @@ namespace WebCore {
// 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, int extensionGroup);
void evaluateInIsolatedWorld(int worldId, const Vector<ScriptSourceCode>& sources, int extensionGroup);
// Evaluate JavaScript in a new context. The script gets its own global scope
// and its own prototypes for intrinsic JavaScript objects (String, Array,
......@@ -348,6 +349,8 @@ namespace WebCore {
void releaseStorageMutex();
void disconnectEventListeners();
void resetIsolatedWorlds();
static bool canAccessPrivate(DOMWindow*);
......@@ -414,6 +417,16 @@ namespace WebCore {
// All of the extensions registered with the context.
static V8Extensions m_extensions;
// The isolated worlds we are tracking for this frame. We hold them alive
// here so that they can be used again by future calls to
// evaluateInIsolatedWorld().
//
// Note: although the pointer is raw, the instance is kept alive by a strong
// reference to the v8 context it contains, which is not made weak until we
// call world->destroy().
typedef HashMap<int, V8IsolatedWorld*> IsolatedWorldMap;
IsolatedWorldMap m_isolatedWorlds;
};
template <int tag, typename T>
......
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