Bug 840488 - Directly mark compartments whose docshells disable script execution. r=bz

This commit is contained in:
Bobby Holley
2013-11-12 16:43:33 -08:00
parent 035fbf805a
commit fcc9169d4f
7 changed files with 88 additions and 33 deletions

View File

@@ -1637,24 +1637,8 @@ nsScriptSecurityManager::CanExecuteScripts(JSContext* cx,
if (!sgo) {
return NS_ERROR_FAILURE;
}
// window can be null here if we're running with a non-DOM window
// as the script global (i.e. a XUL prototype document).
nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(sgo);
nsCOMPtr<nsIDocShell> docshell;
nsresult rv;
if (window) {
docshell = window->GetDocShell();
}
if (docshell) {
rv = docshell->GetCanExecuteScripts(result);
if (NS_FAILED(rv)) return rv;
if (!*result) return NS_OK;
}
}
// OK, the docshell doesn't have script execution explicitly disabled.
// Check whether our URI is an "about:" URI that allows scripts. If it is,
// we need to allow JS to run. In this case, don't apply the JS enabled
// pref or policies. On failures, just press on and don't do this special

View File

@@ -762,6 +762,7 @@ nsDocShell::nsDocShell():
mUseGlobalHistory(false),
mInPrivateBrowsing(false),
mDeviceSizeIsPageSize(false),
mCanExecuteScripts(false),
mFiredUnloadEvent(false),
mEODForCurrentDocument(false),
mURIResultedInDocument(false),
@@ -2084,6 +2085,7 @@ NS_IMETHODIMP
nsDocShell::SetAllowJavascript(bool aAllowJavascript)
{
mAllowJavascript = aAllowJavascript;
RecomputeCanExecuteScripts();
return NS_OK;
}
@@ -2817,6 +2819,48 @@ nsDocShell::GetParentDocshell()
return docshell.forget().downcast<nsDocShell>();
}
void
nsDocShell::RecomputeCanExecuteScripts()
{
bool old = mCanExecuteScripts;
nsRefPtr<nsDocShell> parent = GetParentDocshell();
// If we have no tree owner, that means that we've been detached from the
// docshell tree (this is distinct from having no parent dochshell, which
// is the case for root docshells). In that case, don't allow script.
if (!mTreeOwner) {
mCanExecuteScripts = false;
// If scripting has been explicitly disabled on our docshell, we're done.
} else if (!mAllowJavascript) {
mCanExecuteScripts = false;
// If we have a parent, inherit.
} else if (parent) {
mCanExecuteScripts = parent->mCanExecuteScripts;
// Otherwise, we're the root of the tree, and we haven't explicitly disabled
// script. Allow.
} else {
mCanExecuteScripts = true;
}
// Inform our active DOM window.
//
// This will pass the outer, which will be in the scope of the active inner.
if (mScriptGlobal && mScriptGlobal->GetGlobalJSObject()) {
xpc::Scriptability& scriptability =
xpc::Scriptability::Get(mScriptGlobal->GetGlobalJSObject());
scriptability.SetDocShellAllowsScript(mCanExecuteScripts);
}
// If our value has changed, our children might be affected. Recompute their
// value as well.
if (old != mCanExecuteScripts) {
nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
while (iter.HasMore()) {
static_cast<nsDocShell*>(iter.GetNext())->RecomputeCanExecuteScripts();
}
}
}
nsresult
nsDocShell::SetDocLoaderParent(nsDocLoader * aParent)
{
@@ -2886,6 +2930,10 @@ nsDocShell::SetDocLoaderParent(nsDocLoader * aParent)
nsCOMPtr<nsIURIContentListener> parentURIListener(do_GetInterface(parent));
if (parentURIListener)
mContentListener->SetParentContentListener(parentURIListener);
// Our parent has changed. Recompute scriptability.
RecomputeCanExecuteScripts();
return NS_OK;
}
@@ -3440,6 +3488,16 @@ nsDocShell::SetTreeOwner(nsIDocShellTreeOwner * aTreeOwner)
child->SetTreeOwner(aTreeOwner);
}
// Our tree owner has changed. Recompute scriptability.
//
// Note that this is near-redundant with the recomputation in
// SetDocLoaderParent(), but not so for the root DocShell, where the call to
// SetTreeOwner() happens after the initial AddDocLoaderAsChildOfRoot(),
// and we never set another parent. Given that this is neither expensive nor
// performance-critical, let's be safe and unconditionally recompute this
// state whenever dependent state changes.
RecomputeCanExecuteScripts();
return NS_OK;
}
@@ -12651,20 +12709,7 @@ unsigned long nsDocShell::gNumberOfDocShells = 0;
NS_IMETHODIMP
nsDocShell::GetCanExecuteScripts(bool *aResult)
{
NS_ENSURE_ARG_POINTER(aResult);
*aResult = false; // disallow by default
nsRefPtr<nsDocShell> docshell = this;
do {
nsresult rv = docshell->GetAllowJavascript(aResult);
if (NS_FAILED(rv)) return rv;
if (!*aResult) {
return NS_OK;
}
docshell = docshell->GetParentDocshell();
} while (docshell);
*aResult = mCanExecuteScripts;
return NS_OK;
}

View File

@@ -813,6 +813,12 @@ protected:
bool mInPrivateBrowsing;
bool mDeviceSizeIsPageSize;
// Because scriptability depends on the mAllowJavascript values of our
// ancestors, we cache the effective scriptability and recompute it when
// it might have changed;
bool mCanExecuteScripts;
void RecomputeCanExecuteScripts();
// This boolean is set to true right before we fire pagehide and generally
// unset when we embed a new content viewer. While it's true no navigation
// is allowed in this docshell.

View File

@@ -570,7 +570,7 @@ interface nsIDocShell : nsIDocShellTreeItem
* The rule of thumb here is that we disable js if this docshell or any
* of its parents disallow scripting.
*/
readonly attribute boolean canExecuteScripts;
[infallible] readonly attribute boolean canExecuteScripts;
/**
* Sets whether a docshell is active. An active docshell is one that is

View File

@@ -2062,6 +2062,9 @@ WindowStateHolder::WindowStateHolder(nsGlobalWindow *aWindow,
mInnerWindowHolder = aHolder;
aWindow->SuspendTimeouts();
// When a global goes into the bfcache, we disable script.
xpc::Scriptability::Get(aWindow->mJSObject).SetDocShellAllowsScript(false);
}
WindowStateHolder::~WindowStateHolder()
@@ -2460,6 +2463,10 @@ nsGlobalWindow::SetNewDocument(nsIDocument* aDocument,
// Enter the new global's compartment.
JSAutoCompartment ac(cx, mJSObject);
// Set scriptability based on the state of the docshell.
bool allow = GetDocShell()->GetCanExecuteScripts();
xpc::Scriptability::Get(mJSObject).SetDocShellAllowsScript(allow);
// If we created a new inner window above, we need to do the last little bit
// of initialization now that the dust has settled.
if (createdInnerWindow) {

View File

@@ -410,12 +410,14 @@ EnsureCompartmentPrivate(JSCompartment *c)
return priv;
}
Scriptability::Scriptability() : mScriptBlocks(0) {}
Scriptability::Scriptability() : mScriptBlocks(0)
, mDocShellAllowsScript(true)
{}
bool
Scriptability::Allowed()
{
return mScriptBlocks == 0;
return mDocShellAllowsScript && mScriptBlocks == 0;
}
void
@@ -431,6 +433,12 @@ Scriptability::Unblock()
--mScriptBlocks;
}
void
Scriptability::SetDocShellAllowsScript(bool aAllowed)
{
mDocShellAllowsScript = aAllowed;
}
/* static */
Scriptability&
Scriptability::Get(JSObject *aScope)

View File

@@ -42,6 +42,7 @@ public:
void Block();
void Unblock();
void SetDocShellAllowsScript(bool aAllowed);
static Scriptability& Get(JSObject *aScope);
@@ -51,6 +52,10 @@ private:
// re-enable it (if ever), it decrements this value with a call to Unblock().
// Script may not run if this value is non-zero.
uint32_t mScriptBlocks;
// Whether the docshell allows javascript in this scope. If this scope
// doesn't have a docshell, this value is always true.
bool mDocShellAllowsScript;
};
JSObject *