Fix for bug 637116 (Leak documents+windows with replaceState, focus, event handler closure). r=mrbkap.

--HG--
extra : rebase_source : 57090db5f6805d70811dc5753e2bc91feb65c0b5
This commit is contained in:
Peter Van der Beken 2011-03-03 12:55:55 +01:00
parent 8f5ba7a3c0
commit dbe2853e3d
7 changed files with 78 additions and 29 deletions

View File

@ -9547,7 +9547,8 @@ nsDocShell::SetReferrerURI(nsIURI * aURI)
//*****************************************************************************
nsresult
nsDocShell::StringifyJSValVariant(nsIVariant *aData, nsAString &aResult)
nsDocShell::StringifyJSValVariant(JSContext *aCx, nsIVariant *aData,
nsAString &aResult)
{
nsresult rv;
aResult.Truncate();
@ -9558,28 +9559,32 @@ nsDocShell::StringifyJSValVariant(nsIVariant *aData, nsAString &aResult)
rv = aData->GetAsJSVal(&jsData);
NS_ENSURE_SUCCESS(rv, NS_ERROR_UNEXPECTED);
// Now get the JSContext associated with the current document.
// First get the current document.
nsCOMPtr<nsIDocument> document = do_GetInterface(GetAsSupports(this));
NS_ENSURE_TRUE(document, NS_ERROR_FAILURE);
nsCOMPtr<nsIJSContextStack> contextStack;
JSContext *cx = aCx;
if (!cx) {
// Now get the JSContext associated with the current document.
// First get the current document.
nsCOMPtr<nsIDocument> document = do_GetInterface(GetAsSupports(this));
NS_ENSURE_TRUE(document, NS_ERROR_FAILURE);
// Get the JSContext from the document, like we do in
// nsContentUtils::GetContextFromDocument().
nsIScriptGlobalObject *sgo = document->GetScopeObject();
NS_ENSURE_TRUE(sgo, NS_ERROR_FAILURE);
// Get the JSContext from the document, like we do in
// nsContentUtils::GetContextFromDocument().
nsIScriptGlobalObject *sgo = document->GetScopeObject();
NS_ENSURE_TRUE(sgo, NS_ERROR_FAILURE);
nsIScriptContext *scx = sgo->GetContext();
NS_ENSURE_TRUE(scx, NS_ERROR_FAILURE);
nsIScriptContext *scx = sgo->GetContext();
NS_ENSURE_TRUE(scx, NS_ERROR_FAILURE);
JSContext *cx = (JSContext *)scx->GetNativeContext();
cx = (JSContext *)scx->GetNativeContext();
// If our json call triggers a JS-to-C++ call, we want that call to use cx
// as the context. So we push cx onto the context stack.
nsCOMPtr<nsIJSContextStack> contextStack =
do_GetService("@mozilla.org/js/xpc/ContextStack;1", &rv);
NS_ENSURE_SUCCESS(rv, rv);
// If our json call triggers a JS-to-C++ call, we want that call to use
// aCx as the context. So we push aCx onto the context stack.
contextStack =
do_GetService("@mozilla.org/js/xpc/ContextStack;1", &rv);
NS_ENSURE_SUCCESS(rv, rv);
contextStack->Push(cx);
contextStack->Push(cx);
}
nsCOMPtr<nsIJSON> json = do_GetService("@mozilla.org/dom/json;1");
if(json) {
@ -9590,15 +9595,20 @@ nsDocShell::StringifyJSValVariant(nsIVariant *aData, nsAString &aResult)
rv = NS_ERROR_FAILURE;
}
// Always pop the stack!
contextStack->Pop(&cx);
if (contextStack) {
if (NS_FAILED(rv)) {
JS_ClearPendingException(cx);
}
contextStack->Pop(&cx);
}
return rv;
}
NS_IMETHODIMP
nsDocShell::AddState(nsIVariant *aData, const nsAString& aTitle,
const nsAString& aURL, PRBool aReplace)
const nsAString& aURL, PRBool aReplace, JSContext* aCx)
{
// Implements History.pushState and History.replaceState
@ -9651,7 +9661,7 @@ nsDocShell::AddState(nsIVariant *aData, const nsAString& aTitle,
return NS_ERROR_DOM_SECURITY_ERR;
nsCOMPtr<nsIPrincipal> origPrincipal = origDocument->NodePrincipal();
rv = StringifyJSValVariant(aData, dataStr);
rv = StringifyJSValVariant(aCx, aData, dataStr);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIDocument> newDocument =

View File

@ -338,7 +338,8 @@ protected:
// Tries to stringify a given variant by converting it to JSON. This only
// works if the variant is backed by a JSVal.
nsresult StringifyJSValVariant(nsIVariant *aData, nsAString &aResult);
nsresult StringifyJSValVariant(JSContext *aCx, nsIVariant *aData,
nsAString &aResult);
// Returns PR_TRUE if would have called FireOnLocationChange,
// but did not because aFireOnLocationChange was false on entry.

View File

@ -43,6 +43,7 @@
%{ C++
class nsPresContext;
class nsIPresShell;
struct JSContext;
%}
/**
@ -71,7 +72,7 @@ interface nsIPrincipal;
interface nsIWebBrowserPrint;
interface nsIVariant;
[scriptable, uuid(1917a6d6-31f6-4033-868d-f15ca08ca2df)]
[scriptable, uuid(f77271a1-0b22-4581-af6d-529125f1901d)]
interface nsIDocShell : nsISupports
{
/**
@ -174,6 +175,7 @@ interface nsIDocShell : nsISupports
* Do either a history.pushState() or history.replaceState() operation,
* depending on the value of aReplace.
*/
[implicit_jscontext]
void addState(in nsIVariant aData, in DOMString aTitle,
in DOMString aURL, in boolean aReplace);

View File

@ -0,0 +1,29 @@
<!DOCTYPE html>
<html>
<head>
<script>
function K(v) { return function() { return v; } }
var errorProxy = Proxy.create({get: function() { throw new Error(); }});
function boom()
{
var focused = document.createElementNS("http://www.w3.org/1999/xhtml", "input");
document.body.appendChild(focused);
var otherWin = window.open("data:text/html,1", "_blank", "width=200,height=200");
try { otherWin.history.replaceState(errorProxy, "title", "replaceState.html"); } catch(e) {}
focused.focus();
focused.addEventListener("foo", K(otherWin.applicationCache), false);
otherWin.close();
}
</script>
</head>
<body onload="boom();">
<button onclick="boom();">If you have popups blocked, click here to start the leak test</button>
</body>
</html>

View File

@ -25,3 +25,4 @@ asserts(1) load 504224.html # bug 564098
load 603531.html
load 601247.html
load 612018-1.html
load 637116.html

View File

@ -284,7 +284,7 @@ nsHistory::Go(PRInt32 aDelta)
NS_IMETHODIMP
nsHistory::PushState(nsIVariant *aData, const nsAString& aTitle,
const nsAString& aURL)
const nsAString& aURL, JSContext* aCx)
{
// Check that PushState hasn't been pref'ed off.
if (!nsContentUtils::GetBoolPref(sAllowPushStatePrefStr, PR_FALSE))
@ -305,7 +305,7 @@ nsHistory::PushState(nsIVariant *aData, const nsAString& aTitle,
// PR_FALSE tells the docshell to add a new history entry instead of
// modifying the current one.
nsresult rv = docShell->AddState(aData, aTitle, aURL, PR_FALSE);
nsresult rv = docShell->AddState(aData, aTitle, aURL, PR_FALSE, aCx);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
@ -313,7 +313,7 @@ nsHistory::PushState(nsIVariant *aData, const nsAString& aTitle,
NS_IMETHODIMP
nsHistory::ReplaceState(nsIVariant *aData, const nsAString& aTitle,
const nsAString& aURL)
const nsAString& aURL, JSContext* aCx)
{
// Check that ReplaceState hasn't been pref'ed off
if (!nsContentUtils::GetBoolPref(sAllowReplaceStatePrefStr, PR_FALSE))
@ -334,7 +334,7 @@ nsHistory::ReplaceState(nsIVariant *aData, const nsAString& aTitle,
// PR_TRUE tells the docshell to modify the current SHEntry, rather than
// create a new one.
return docShell->AddState(aData, aTitle, aURL, PR_TRUE);
return docShell->AddState(aData, aTitle, aURL, PR_TRUE, aCx);
}
NS_IMETHODIMP

View File

@ -39,9 +39,13 @@
#include "domstubs.idl"
%{ C++
struct JSContext;
%}
interface nsIVariant;
[scriptable, uuid(dba2e0d5-1682-40a3-be46-28eda99d757e)]
[scriptable, uuid(d5a3006b-dd6b-4ba3-81be-6559f8889e60)]
interface nsIDOMHistory : nsISupports
{
readonly attribute long length;
@ -54,9 +58,11 @@ interface nsIDOMHistory : nsISupports
void go([optional] in long aDelta);
DOMString item(in unsigned long index);
[implicit_jscontext]
void pushState(in nsIVariant aData,
in DOMString aTitle,
[optional] in DOMString aURL);
[implicit_jscontext]
void replaceState(in nsIVariant aData,
in DOMString aTitle,
[optional] in DOMString aURL);