Bug 989584. Allow sites to set window.opener to any value. r=peterv

This commit is contained in:
Boris Zbarsky 2014-06-03 11:38:37 -04:00
Родитель 4b32240082
Коммит da05bf53a8
5 изменённых файлов: 103 добавлений и 54 удалений

Просмотреть файл

@ -4339,13 +4339,7 @@ nsGlobalWindow::GetOwnPropertyNames(JSContext* aCx, nsTArray<nsString>& aNames,
nsGlobalWindow::IsChromeWindow(JSContext* aCx, JSObject* aObj)
{
// For now, have to deal with XPConnect objects here.
nsGlobalWindow* win;
nsresult rv = UNWRAP_OBJECT(Window, aObj, win);
if (NS_FAILED(rv)) {
nsCOMPtr<nsPIDOMWindow> piWin = do_QueryWrapper(aCx, aObj);
win = static_cast<nsGlobalWindow*>(piWin.get());
}
return win->IsChromeWindow();
return xpc::WindowOrNull(aObj)->IsChromeWindow();
}
nsIDOMOfflineResourceList*
@ -4471,9 +4465,9 @@ nsGlobalWindow::GetControllers(nsIControllers** aResult)
}
nsIDOMWindow*
nsGlobalWindow::GetOpener(ErrorResult& aError)
nsGlobalWindow::GetOpenerWindow(ErrorResult& aError)
{
FORWARD_TO_OUTER_OR_THROW(GetOpener, (aError), aError, nullptr);
FORWARD_TO_OUTER_OR_THROW(GetOpenerWindow, (aError), aError, nullptr);
nsCOMPtr<nsPIDOMWindow> opener = do_QueryReferent(mOpener);
if (!opener) {
@ -4512,18 +4506,41 @@ nsGlobalWindow::GetOpener(ErrorResult& aError)
return nullptr;
}
JS::Value
nsGlobalWindow::GetOpener(JSContext* aCx, ErrorResult& aError)
{
nsCOMPtr<nsIDOMWindow> opener = GetOpenerWindow(aError);
if (aError.Failed() || !opener) {
return JS::NullValue();
}
JS::Rooted<JS::Value> val(aCx);
aError = nsContentUtils::WrapNative(aCx, opener, &val);
return val;
}
NS_IMETHODIMP
nsGlobalWindow::GetOpener(nsIDOMWindow** aOpener)
nsGlobalWindow::GetScriptableOpener(JSContext* aCx,
JS::MutableHandle<JS::Value> aOpener)
{
ErrorResult rv;
nsCOMPtr<nsIDOMWindow> opener = GetOpener(rv);
opener.forget(aOpener);
aOpener.set(GetOpener(aCx, rv));
return rv.ErrorCode();
}
NS_IMETHODIMP
nsGlobalWindow::GetOpener(nsIDOMWindow** aOpener)
{
ErrorResult rv;
nsCOMPtr<nsIDOMWindow> opener = GetOpenerWindow(rv);
opener.forget(aOpener);
return rv.ErrorCode();
}
void
nsGlobalWindow::SetOpener(nsIDOMWindow* aOpener, ErrorResult& aError)
nsGlobalWindow::SetOpener(JSContext* aCx, JS::Handle<JS::Value> aOpener,
ErrorResult& aError)
{
// Check if we were called from a privileged chrome script. If not, and if
// aOpener is not null, just define aOpener on our inner window's JS object,
@ -4531,35 +4548,15 @@ nsGlobalWindow::SetOpener(nsIDOMWindow* aOpener, ErrorResult& aError)
// Xray expando object, but don't set it on the outer window, so that it'll
// get reset on navigation. This is just like replaceable properties, but
// we're not quite readonly.
if (aOpener && !nsContentUtils::IsCallerChrome()) {
// JS_WrapObject will outerize, so we don't care if aOpener is an inner.
nsCOMPtr<nsIGlobalObject> glob = do_QueryInterface(aOpener);
if (!glob) {
aError.Throw(NS_ERROR_UNEXPECTED);
return;
}
AutoJSContext cx;
JSAutoRequest ar(cx);
// Note we explicitly do NOT enter any particular compartment here; we want
// the caller compartment in cases when we have a caller, so that we define
// expandos on Xrays as needed.
JS::Rooted<JSObject*> otherObj(cx, glob->GetGlobalJSObject());
if (!otherObj) {
aError.Throw(NS_ERROR_UNEXPECTED);
return;
}
JS::Rooted<JSObject*> thisObj(cx, GetWrapperPreserveColor());
if (!aOpener.isNull() && !nsContentUtils::IsCallerChrome()) {
JS::Rooted<JSObject*> thisObj(aCx, GetWrapperPreserveColor());
if (!thisObj) {
aError.Throw(NS_ERROR_UNEXPECTED);
return;
}
if (!JS_WrapObject(cx, &otherObj) ||
!JS_WrapObject(cx, &thisObj) ||
!JS_DefineProperty(cx, thisObj, "opener", otherObj, JSPROP_ENUMERATE,
if (!JS_WrapObject(aCx, &thisObj) ||
!JS_DefineProperty(aCx, thisObj, "opener", aOpener, JSPROP_ENUMERATE,
JS_PropertyStub, JS_StrictPropertyStub)) {
aError.Throw(NS_ERROR_FAILURE);
}
@ -4567,16 +4564,47 @@ nsGlobalWindow::SetOpener(nsIDOMWindow* aOpener, ErrorResult& aError)
return;
}
SetOpenerWindow(aOpener, false);
if (!aOpener.isObjectOrNull()) {
// Chrome code trying to set some random value as opener
aError.Throw(NS_ERROR_INVALID_ARG);
return;
}
nsGlobalWindow* win = nullptr;
if (aOpener.isObject()) {
JSObject* unwrapped = js::CheckedUnwrap(&aOpener.toObject(),
/* stopAtOuter = */ false);
if (!unwrapped) {
aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
return;
}
win = xpc::WindowOrNull(unwrapped);
if (!win) {
// Wasn't a window
aError.Throw(NS_ERROR_INVALID_ARG);
return;
}
}
SetOpenerWindow(win, false);
}
NS_IMETHODIMP
nsGlobalWindow::SetScriptableOpener(JSContext* aCx,
JS::Handle<JS::Value> aOpener)
{
ErrorResult rv;
SetOpener(aCx, aOpener, rv);
return rv.ErrorCode();
}
NS_IMETHODIMP
nsGlobalWindow::SetOpener(nsIDOMWindow* aOpener)
{
ErrorResult rv;
SetOpener(aOpener, rv);
return rv.ErrorCode();
SetOpenerWindow(aOpener, false);
return NS_OK;
}
void
@ -13699,13 +13727,7 @@ bool
nsGlobalWindow::IsModalContentWindow(JSContext* aCx, JSObject* aGlobal)
{
// For now, have to deal with XPConnect objects here.
nsGlobalWindow* win;
nsresult rv = UNWRAP_OBJECT(Window, aGlobal, win);
if (NS_FAILED(rv)) {
nsCOMPtr<nsPIDOMWindow> piWin = do_QueryWrapper(aCx, aGlobal);
win = static_cast<nsGlobalWindow*>(piWin.get());
}
return win->IsModalContentWindow();
return xpc::WindowOrNull(aGlobal)->IsModalContentWindow();
}
NS_IMETHODIMP

Просмотреть файл

@ -836,8 +836,12 @@ public:
aError = GetScriptableTop(getter_AddRefs(top));
return top.forget();
}
nsIDOMWindow* GetOpener(mozilla::ErrorResult& aError);
void SetOpener(nsIDOMWindow* aOpener, mozilla::ErrorResult& aError);
protected:
nsIDOMWindow* GetOpenerWindow(mozilla::ErrorResult& aError);
public:
JS::Value GetOpener(JSContext* aCx, mozilla::ErrorResult& aError);
void SetOpener(JSContext* aCx, JS::Handle<JS::Value> aOpener,
mozilla::ErrorResult& aError);
using nsIDOMWindow::GetParent;
already_AddRefed<nsIDOMWindow> GetParent(mozilla::ErrorResult& aError);
mozilla::dom::Element* GetFrameElement(mozilla::ErrorResult& aError);

Просмотреть файл

@ -59,6 +59,25 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=868996
is(evalsb("win.opener", sb2), window,
"Navigating a window should have reset the opener in sb2");
win.opener = 5;
evalsb("win.opener = 5", sb1);
evalsb("win.opener = 5", sb2);
is(win.opener, 5, "Should be able to set an opener to a primitive");
is(evalsb("win.opener", sb1), 5,
"Should be able to set the opener to a primitive in a sandbox one");
is(evalsb("win.opener", sb2), 5,
"Should be able to set the opener to a primitive in a sandbox two");
win.location = "data:text/html,<script>opener.setTimeout(opener.continueOpenerTest2, 0, this);</" + "script>";
}
function continueOpenerTest2(win) {
is(win.opener, window,
"Navigating a window again should have reset the opener we stashed on it temporarily");
is(evalsb("win.opener", sb1), window,
"Navigating a window again should have reset the opener in sb1");
is(evalsb("win.opener", sb2), window,
"Navigating a window again should have reset the opener in sb2");
win.opener = null;
is(win.opener, null, "Should be able to set the opener to null");
is(evalsb("win.opener", sb1), null,

Просмотреть файл

@ -24,7 +24,7 @@ interface nsIVariant;
* @see <http://www.whatwg.org/html/#window>
*/
[scriptable, uuid(fbefa573-0ba2-4d15-befb-d60277643a0b)]
[scriptable, uuid(d4316591-d16e-405c-8093-b441cbef3230)]
interface nsIDOMWindow : nsISupports
{
// the current browsing context
@ -151,7 +151,11 @@ interface nsIDOMWindow : nsISupports
}
%}
attribute nsIDOMWindow opener;
[implicit_jscontext, binaryname(ScriptableOpener)]
attribute jsval opener;
[noscript, binaryname(Opener)]
attribute nsIDOMWindow openerWindow;
/**
* |frameElement| gets this window's <iframe> or <frame> element, if it has

Просмотреть файл

@ -54,7 +54,7 @@ typedef any Transferable;
[Replaceable, CrossOriginReadable] readonly attribute unsigned long length;
//[Unforgeable, Throws, CrossOriginReadable] readonly attribute WindowProxy top;
[Unforgeable, Throws, CrossOriginReadable] readonly attribute WindowProxy? top;
[Throws, CrossOriginReadable] attribute WindowProxy? opener;
[Throws, CrossOriginReadable] attribute any opener;
//[Throws] readonly attribute WindowProxy parent;
[Replaceable, Throws, CrossOriginReadable] readonly attribute WindowProxy? parent;
[Throws] readonly attribute Element? frameElement;