Fix for bug 637099 (Remove JS_ClearScope calls on windows). r=mrbkap.

--HG--
extra : rebase_source : 8bdadc72a0b28c78e21758fab8f1035ee1dd9f3d
This commit is contained in:
Peter Van der Beken 2011-03-15 12:06:39 +01:00
parent 28f90dc67b
commit b3213dcf89
12 changed files with 178 additions and 205 deletions

View File

@ -1227,48 +1227,8 @@ nsGlobalWindow::ClearControllers()
}
}
// static
void
nsGlobalWindow::TryClearWindowScope(nsISupports *aWindow)
{
nsGlobalWindow *window =
static_cast<nsGlobalWindow *>(static_cast<nsIDOMWindow*>(aWindow));
// This termination function might be called when any script evaluation in our
// context terminated, even if there are other scripts in the stack. Thus, we
// have to check again if a script is executing and post a new termination
// function if necessary.
window->ClearScopeWhenAllScriptsStop();
}
void
nsGlobalWindow::ClearScopeWhenAllScriptsStop()
{
NS_ASSERTION(IsInnerWindow(), "Must be an inner window");
// We cannot clear scope safely until all the scripts in our script context
// stopped. This might be a long wait, for example if one script is busy
// because it started a nested event loop for a modal dialog.
nsIScriptContext *jsscx = GetContextInternal();
if (jsscx && jsscx->GetExecutingScript()) {
// We ignore the return value because the only reason that we clear scope
// here is to try to prevent leaks. Failing to clear scope might mean that
// we'll leak more but if we don't have enough memory to allocate a
// termination function we probably don't have to worry about this anyway.
jsscx->SetTerminationFunction(TryClearWindowScope,
static_cast<nsIDOMWindow *>(this));
return;
}
NotifyWindowIDDestroyed("inner-window-destroyed");
nsIScriptContext *scx = GetContextInternal();
if (scx) {
scx->ClearScope(mJSObject, true);
}
}
void
nsGlobalWindow::FreeInnerObjects(bool aClearScope)
nsGlobalWindow::FreeInnerObjects()
{
NS_ASSERTION(IsInnerWindow(), "Don't free inner objects on an outer window");
@ -1330,9 +1290,7 @@ nsGlobalWindow::FreeInnerObjects(bool aClearScope)
mIndexedDB = nsnull;
if (aClearScope) {
ClearScopeWhenAllScriptsStop();
}
NotifyWindowIDDestroyed("inner-window-destroyed");
if (mDummyJavaPluginOwner) {
// Tear down the dummy java plugin.
@ -1666,7 +1624,6 @@ nsGlobalWindow::WouldReuseInnerWindow(nsIDocument *aNewDocument)
// Great, we're the original document, check for one of the other
// conditions.
if (mDoc == aNewDocument) {
// aClearScopeHint is false.
return true;
}
@ -1840,10 +1797,8 @@ WindowStateHolder::~WindowStateHolder()
// free it up.
// Note that FreeInnerObjects may already have been called on the
// inner window if its outer has already had SetDocShell(null)
// called. In this case the contexts will all be null and the
// true for aClearScope won't do anything; this is OK since
// SetDocShell(null) already did it.
mInnerWindow->FreeInnerObjects(true);
// called.
mInnerWindow->FreeInnerObjects();
}
}
@ -2006,12 +1961,6 @@ nsGlobalWindow::SetNewDocument(nsIDocument* aDocument,
nsCOMPtr<WindowStateHolder> wsh = do_QueryInterface(aState);
NS_ASSERTION(!aState || wsh, "What kind of weird state are you giving me here?");
// Make sure to clear scope on the outer window *before* we
// initialize the new inner window. If we don't, things
// (Object.prototype etc) could leak from the old outer to the new
// inner scope.
mContext->ClearScope(mJSObject, false);
if (reUseInnerWindow) {
// We're reusing the current inner window.
NS_ASSERTION(!currentInner->IsFrozen(),
@ -2081,8 +2030,6 @@ nsGlobalWindow::SetNewDocument(nsIDocument* aDocument,
}
if (currentInner && currentInner->mJSObject) {
bool termFuncSet = false;
if (oldDoc == aDocument) {
// Move the navigator from the old inner window to the new one since
// this is a document.write. This is safe from a same-origin point of
@ -2092,45 +2039,12 @@ nsGlobalWindow::SetNewDocument(nsIDocument* aDocument,
if (newInnerWindow->mNavigator) {
newInnerWindow->mNavigator->SetWindow(newInnerWindow);
}
// Suspend the current context's request before Pop() resumes the old
// context's request.
JSAutoSuspendRequest asr(cx);
// Pop our context here so that we get the correct one for the
// termination function.
cxPusher.Pop();
JSContext *oldCx = nsContentUtils::GetCurrentJSContext();
nsIScriptContext *callerScx;
if (oldCx && (callerScx = GetScriptContextFromJSContext(oldCx))) {
// We're called from document.open() (and document.open() is
// called from JS), clear the scope etc in a termination
// function on the calling context to prevent clearing the
// calling scope.
NS_ASSERTION(!currentInner->IsFrozen(),
"How does this opened window get into session history");
JSAutoRequest ar(oldCx);
callerScx->SetTerminationFunction(ClearWindowScope,
static_cast<nsIDOMWindow *>
(currentInner));
termFuncSet = true;
}
// Re-push our context.
cxPusher.Push(cx);
}
// Don't clear scope on our current inner window if it's going to be
// Don't free objects on our current inner window if it's going to be
// held in the bfcache.
if (!currentInner->IsFrozen()) {
// Skip the ClearScope if we set a termination function to do
// it ourselves, later.
currentInner->FreeInnerObjects(!termFuncSet);
currentInner->FreeInnerObjects();
}
}
@ -2433,7 +2347,7 @@ nsGlobalWindow::SetDocShell(nsIDocShell* aDocShell)
inner = (nsGlobalWindow*)PR_NEXT_LINK(inner)) {
NS_ASSERTION(!inner->mOuterWindow || inner->mOuterWindow == this,
"bad outer window pointer");
inner->FreeInnerObjects(true);
inner->FreeInnerObjects();
}
// Make sure that this is called before we null out the document.
@ -2455,10 +2369,6 @@ nsGlobalWindow::SetDocShell(nsIDocShell* aDocShell)
mFocusedNode = nsnull;
}
if (mContext) {
mContext->ClearScope(mJSObject, true);
}
ClearControllers();
mChromeEventHandler = nsnull; // force release now
@ -8985,17 +8895,6 @@ nsGlobalWindow::CloseWindow(nsISupports *aWindow)
// else if OOM, better not to close. That might cause a crash.
}
// static
void
nsGlobalWindow::ClearWindowScope(nsISupports *aWindow)
{
nsCOMPtr<nsIScriptGlobalObject> sgo(do_QueryInterface(aWindow));
nsIScriptContext *scx = sgo->GetContext();
if (scx) {
scx->ClearScope(sgo->GetGlobalJSObject(), true);
}
}
//*****************************************************************************
// nsGlobalWindow: Timeout Functions
//*****************************************************************************

View File

@ -595,11 +595,9 @@ protected:
virtual ~nsGlobalWindow();
void CleanUp(bool aIgnoreModalDialog);
void ClearControllers();
static void TryClearWindowScope(nsISupports* aWindow);
void ClearScopeWhenAllScriptsStop();
nsresult FinalClose();
void FreeInnerObjects(bool aClearScope);
void FreeInnerObjects();
nsGlobalWindow *CallerInnerWindow();
nsresult InnerSetNewDocument(nsIDocument* aDocument);
@ -677,7 +675,6 @@ protected:
nsIDOMWindow **aReturn);
static void CloseWindow(nsISupports* aWindow);
static void ClearWindowScope(nsISupports* aWindow);
// Timeout Functions
// Language agnostic timeout function (all args passed).

View File

@ -75,8 +75,8 @@ NS_DEFINE_STATIC_IID_ACCESSOR(nsIScriptContextPrincipal,
NS_ISCRIPTCONTEXTPRINCIPAL_IID)
#define NS_ISCRIPTCONTEXT_IID \
{ 0xf3840057, 0x4fe5, 0x4f92, \
{ 0xa3, 0xb8, 0x27, 0xd7, 0x44, 0x6f, 0x72, 0x4d } }
{ 0x6d69fbee, 0x0723, 0x48f5, \
{ 0x82, 0x48, 0xcd, 0xcf, 0x88, 0xac, 0x25, 0x74 } }
/* This MUST match JSVERSION_DEFAULT. This version stuff if we don't
know what language we have is a little silly... */
@ -427,20 +427,6 @@ public:
*/
virtual nsresult InitClasses(JSObject* aGlobalObj) = 0;
/**
* Clear the scope object - may be called either as we are being torn down,
* or before we are attached to a different document.
*
* aClearFromProtoChain is probably somewhat JavaScript specific. It
* indicates that the global scope polluter should be removed from the
* prototype chain and that the objects in the prototype chain should
* also have their scopes cleared. We don't do this all the time
* because the prototype chain is shared between inner and outer
* windows, and needs to stay with inner windows that we're keeping
* around.
*/
virtual void ClearScope(void* aGlobalObj, bool aClearFromProtoChain) = 0;
/**
* Tell the context we're about to be reinitialize it.
*/

View File

@ -3015,82 +3015,6 @@ nsJSContext::InitClasses(JSObject* aGlobalObj)
return rv;
}
void
nsJSContext::ClearScope(void *aGlobalObj, bool aClearFromProtoChain)
{
// Push our JSContext on our thread's context stack.
nsCOMPtr<nsIJSContextStack> stack =
do_GetService("@mozilla.org/js/xpc/ContextStack;1");
if (stack && NS_FAILED(stack->Push(mContext))) {
stack = nsnull;
}
if (aGlobalObj) {
JSObject *obj = (JSObject *)aGlobalObj;
JSAutoRequest ar(mContext);
JSAutoEnterCompartment ac;
ac.enterAndIgnoreErrors(mContext, obj);
// Grab a reference to the window property, which is the outer
// window, so that we can re-define it once we've cleared
// scope. This is what keeps the outer window alive in cases where
// nothing else does.
jsval window;
if (!JS_GetProperty(mContext, obj, "window", &window)) {
window = JSVAL_VOID;
JS_ClearPendingException(mContext);
}
JS_ClearScope(mContext, obj);
NS_ABORT_IF_FALSE(!xpc::WrapperFactory::IsXrayWrapper(obj), "unexpected wrapper");
if (window != JSVAL_VOID) {
if (!JS_DefineProperty(mContext, obj, "window", window,
JS_PropertyStub, JS_StrictPropertyStub,
JSPROP_ENUMERATE | JSPROP_READONLY |
JSPROP_PERMANENT)) {
JS_ClearPendingException(mContext);
}
}
if (!js::GetObjectParent(obj)) {
JS_ClearRegExpStatics(mContext, obj);
}
// Always clear watchpoints, to deal with two cases:
// 1. The first document for this window is loading, and a miscreant has
// preset watchpoints on the window object in order to attack the new
// document's privileged information.
// 2. A document loaded and used watchpoints on its own window, leaving
// them set until the next document loads. We must clean up window
// watchpoints here.
// Watchpoints set on document and subordinate objects are all cleared
// when those sub-window objects are finalized, after JS_ClearScope and
// a GC run that finds them to be garbage.
::JS_ClearWatchPointsForObject(mContext, obj);
// Since the prototype chain is shared between inner and outer (and
// stays with the inner), we don't clear things from the prototype
// chain when we're clearing an outer window whose current inner we
// still want.
if (aClearFromProtoChain) {
nsWindowSH::InvalidateGlobalScopePolluter(mContext, obj);
// Clear up obj's prototype chain, but not Object.prototype.
for (JSObject *o = ::JS_GetPrototype(obj), *next;
o && (next = ::JS_GetPrototype(o)); o = next)
::JS_ClearScope(mContext, o);
}
}
if (stack) {
stack->Pop(nsnull);
}
}
void
nsJSContext::WillInitializeContext()
{

View File

@ -161,7 +161,6 @@ public:
virtual void SetGCOnDestruction(bool aGCOnDestruction);
virtual nsresult InitClasses(JSObject* aGlobalObj);
virtual void ClearScope(void* aGlobalObj, bool bClearPolluters);
virtual void WillInitializeContext();
virtual void DidInitializeContext();

View File

@ -156,6 +156,12 @@ _TEST_FILES = \
test_bug698061.html \
test_bug707749.html \
test_bug691707.html \
test_bug304459.html \
iframe_bug304459-1.html \
iframe_bug304459-2.html \
test_bug38959.html \
iframe_bug38959-1.html \
iframe_bug38959-2.html \
$(NULL)
libs:: $(_TEST_FILES)

View File

@ -0,0 +1,12 @@
<html>
<head>
<title>Iframe test for bug 304459</title>
</head>
<body">
<script>
Object.prototype.x = "oops";
</script>
</body>
</html>

View File

@ -0,0 +1,18 @@
<html>
<head>
<title>Iframe test for bug 304459</title>
</head>
<body">
<script>
var result;
try {
x == undefined;
} catch (e) {
result = true;
}
window.parent.postMessage(result, "http://mochi.test:8888");
</script>
</body>
</html>

View File

@ -0,0 +1,14 @@
<html>
<head>
<title>Iframe test for bug 38959</title>
</head>
<body">
<script>
x = false;
window.opener.postMessage(1, "http://mochi.test:8888");
window.close();
</script>
</body>
</html>

View File

@ -0,0 +1,14 @@
<html>
<head>
<title>Iframe test for bug 38959</title>
</head>
<body">
<script>
x = true;
window.opener.postMessage(2, "http://mochi.test:8888");
window.close();
</script>
</body>
</html>

View File

@ -0,0 +1,47 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=304459
-->
<head>
<title>Test for Bug 304459</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=304459">Mozilla Bug 304459</a>
<p id="display"></p>
<div id="content" style="display: none">
<iframe id="frame"></iframe>
</div>
<pre id="test">
<script type="application/javascript">
/** Test for Bug 304459 **/
function iframeLoaded()
{
var frame = document.getElementById("frame");
frame.onload = undefined;
frame.src = "http://example.org/tests/dom/tests/mochitest/bugs/iframe_bug304459-2.html";
}
function receiveMessage(evt)
{
ok(evt.data, "Prototype leaks across navigation");
SimpleTest.finish();
}
SimpleTest.waitForExplicitFinish();
window.addEventListener("message", receiveMessage, false);
var frame = document.getElementById("frame");
frame.onload = iframeLoaded;
frame.src = "iframe_bug304459-1.html"
</script>
</pre>
</body>
</html>

View File

@ -0,0 +1,57 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=38959
-->
<head>
<title>Test for Bug 38959</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=38959">Mozilla Bug 38959</a>
<p id="display"></p>
<div id="content" style="display: none">
<iframe id="frame"></iframe>
</div>
<pre id="test">
<script type="application/javascript">
/** Test for Bug 38959 **/
var newValue;
function watcher(id, ol, ne)
{
newValue = ne;
return ne;
}
function openWindow(url, crossOrigin)
{
newValue = true;
var w = window.open(url);
w.watch("x", watcher);
}
function receiveMessage(evt)
{
ok(newValue, "Watchpoints only allowed same-origin.");
if (evt.data == 1) {
openWindow("/tests/dom/tests/mochitest/bugs/iframe_bug38959-2.html");
}
else {
SimpleTest.finish();
}
}
SimpleTest.waitForExplicitFinish();
window.addEventListener("message", receiveMessage, false);
openWindow("http://example.org/tests/dom/tests/mochitest/bugs/iframe_bug38959-1.html");
</script>
</pre>
</body>
</html>