mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 860941 - Separate the handling of |dialogArguments| and |arguments|, and use IDL for the |dialogArguments| getter. r=jst
This patch is bigger than I'd like it to be, but there are a lot of interlocked dependencies and I eventually decided it was easier to just lump it together. The semantics of |showModalDialog|/|window.dialogArguments| (an web-exposed HTML5 feature) and |openDialog|/|window.arguments| (a XUL-proprietary feature) are quite different. The former is essentially a security-checked JSVal, while the latter gets converted into an array. We handled them together in the old world, which led to a lot of confusion and muddled semantics. This patch separates them. This patch also eschews the roundabout resolve hook for dialogArguments in favor of returning them directly from the XPIDL getter. This better matches the behavior in the spec, especially because it allows dialogArguments to live on the outer as they're supposed to, rather than the first inner that happens to end up in the docshell. All in all, this should make this all very straightforward to convert WebIDL when the time comes. The current spec on the origin checks here is pretty fictional, so I've filed https://www.w3.org/Bugs/Public/show_bug.cgi?id=21932 to fix it. This patch should more or less preserve the current security behavior.
This commit is contained in:
parent
e81a69d26b
commit
e00d8aa943
@ -1005,7 +1005,6 @@ jsid nsDOMClassInfo::sToolbar_id = JSID_VOID;
|
||||
jsid nsDOMClassInfo::sLocationbar_id = JSID_VOID;
|
||||
jsid nsDOMClassInfo::sPersonalbar_id = JSID_VOID;
|
||||
jsid nsDOMClassInfo::sStatusbar_id = JSID_VOID;
|
||||
jsid nsDOMClassInfo::sDialogArguments_id = JSID_VOID;
|
||||
jsid nsDOMClassInfo::sControllers_id = JSID_VOID;
|
||||
jsid nsDOMClassInfo::sLength_id = JSID_VOID;
|
||||
jsid nsDOMClassInfo::sScrollX_id = JSID_VOID;
|
||||
@ -1265,7 +1264,6 @@ nsDOMClassInfo::DefineStaticJSVals(JSContext *cx)
|
||||
SET_JSID_TO_STRING(sLocationbar_id, cx, "locationbar");
|
||||
SET_JSID_TO_STRING(sPersonalbar_id, cx, "personalbar");
|
||||
SET_JSID_TO_STRING(sStatusbar_id, cx, "statusbar");
|
||||
SET_JSID_TO_STRING(sDialogArguments_id, cx, "dialogArguments");
|
||||
SET_JSID_TO_STRING(sControllers_id, cx, "controllers");
|
||||
SET_JSID_TO_STRING(sLength_id, cx, "length");
|
||||
SET_JSID_TO_STRING(sScrollX_id, cx, "scrollX");
|
||||
@ -2973,7 +2971,6 @@ nsDOMClassInfo::ShutDown()
|
||||
sLocationbar_id = JSID_VOID;
|
||||
sPersonalbar_id = JSID_VOID;
|
||||
sStatusbar_id = JSID_VOID;
|
||||
sDialogArguments_id = JSID_VOID;
|
||||
sControllers_id = JSID_VOID;
|
||||
sLength_id = JSID_VOID;
|
||||
sScrollX_id = JSID_VOID;
|
||||
@ -5082,23 +5079,6 @@ nsWindowSH::NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (sDialogArguments_id == id && win->IsModalContentWindow()) {
|
||||
nsCOMPtr<nsIArray> args;
|
||||
((nsGlobalModalWindow *)win)->GetDialogArguments(getter_AddRefs(args));
|
||||
|
||||
nsIScriptContext *script_cx = win->GetContext();
|
||||
if (script_cx) {
|
||||
// Make nsJSContext::SetProperty()'s magic argument array
|
||||
// handling happen.
|
||||
rv = script_cx->SetProperty(obj, "dialogArguments", args);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
*objp = obj;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
rv = nsDOMGenericSH::NewResolve(wrapper, cx, obj, id, flags, objp,
|
||||
|
@ -535,6 +535,16 @@ nsPIDOMWindow::nsPIDOMWindow(nsPIDOMWindow *aOuterWindow)
|
||||
|
||||
nsPIDOMWindow::~nsPIDOMWindow() {}
|
||||
|
||||
// DialogValueHolder CC goop.
|
||||
NS_IMPL_CYCLE_COLLECTION_1(DialogValueHolder, mValue)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DialogValueHolder)
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(DialogValueHolder)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(DialogValueHolder)
|
||||
|
||||
//*****************************************************************************
|
||||
// nsOuterWindowProxy: Outer Window Proxy
|
||||
//*****************************************************************************
|
||||
@ -1402,7 +1412,7 @@ nsGlobalWindow::CleanUp(bool aIgnoreModalDialog)
|
||||
|
||||
mInnerWindowHolder = nullptr;
|
||||
mArguments = nullptr;
|
||||
mArgumentsOrigin = nullptr;
|
||||
mDialogArguments = nullptr;
|
||||
|
||||
CleanupCachedXBLHandlers(this);
|
||||
|
||||
@ -1630,6 +1640,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(nsGlobalWindow)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mControllers)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mArguments)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDialogArguments)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPerformance)
|
||||
|
||||
@ -1676,6 +1687,7 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsGlobalWindow)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mControllers)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mArguments)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mDialogArguments)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mPerformance)
|
||||
|
||||
@ -2514,11 +2526,7 @@ nsGlobalWindow::SetNewDocument(nsIDocument* aDocument,
|
||||
|
||||
if (mArguments) {
|
||||
newInnerWindow->DefineArgumentsProperty(mArguments);
|
||||
newInnerWindow->mArguments = mArguments;
|
||||
newInnerWindow->mArgumentsOrigin = mArgumentsOrigin;
|
||||
|
||||
mArguments = nullptr;
|
||||
mArgumentsOrigin = nullptr;
|
||||
}
|
||||
|
||||
// Give the new inner window our chrome event handler (since it
|
||||
@ -3139,50 +3147,50 @@ nsGlobalWindow::SetScriptsEnabled(bool aEnabled, bool aFireTimeouts)
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsGlobalWindow::SetArguments(nsIArray *aArguments, nsIPrincipal *aOrigin)
|
||||
nsGlobalWindow::SetArguments(nsIArray *aArguments)
|
||||
{
|
||||
FORWARD_TO_OUTER(SetArguments, (aArguments, aOrigin),
|
||||
FORWARD_TO_OUTER(SetArguments, (aArguments),
|
||||
NS_ERROR_NOT_INITIALIZED);
|
||||
nsresult rv;
|
||||
|
||||
// Hold on to the arguments so that we can re-set them once the next
|
||||
// document is loaded.
|
||||
mArguments = aArguments;
|
||||
mArgumentsOrigin = aOrigin;
|
||||
|
||||
// Historically, we've used the same machinery to handle openDialog arguments
|
||||
// (exposed via window.arguments) and showModalDialog arguments (exposed via
|
||||
// window.dialogArguments), even though the former is XUL-only and uses an XPCOM
|
||||
// array while the latter is web-exposed and uses an arbitrary JS value.
|
||||
// Moreover, per-spec |dialogArguments| is a property of the browsing context
|
||||
// (outer), whereas |arguments| lives on the inner.
|
||||
//
|
||||
// We've now mostly separated them, but the difference is still opaque to
|
||||
// nsWindowWatcher (the caller of SetArguments in this little back-and-forth
|
||||
// embedding waltz we do here).
|
||||
//
|
||||
// So we need to demultiplex the two cases here.
|
||||
nsGlobalWindow *currentInner = GetCurrentInnerWindowInternal();
|
||||
|
||||
if (mIsModalContentWindow && currentInner) {
|
||||
// SetArguments() is being called on a modal content window that
|
||||
// already has an inner window. This can happen when loading
|
||||
// javascript: URIs as modal content dialogs. In this case, we'll
|
||||
// set up the dialog window, both inner and outer, before we call
|
||||
// SetArguments() on the window, so to deal with that, make sure
|
||||
// here that the arguments are propagated to the inner window.
|
||||
|
||||
currentInner->mArguments = aArguments;
|
||||
currentInner->mArgumentsOrigin = aOrigin;
|
||||
if (mIsModalContentWindow) {
|
||||
// nsWindowWatcher blindly converts the original nsISupports into an array
|
||||
// of length 1. We need to recover it, and then cast it back to the concrete
|
||||
// object we know it to be.
|
||||
nsCOMPtr<nsISupports> supports = do_QueryElementAt(aArguments, 0, &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
mDialogArguments = static_cast<DialogValueHolder*>(supports.get());
|
||||
} else {
|
||||
mArguments = aArguments;
|
||||
rv = currentInner->DefineArgumentsProperty(aArguments);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
return currentInner ?
|
||||
currentInner->DefineArgumentsProperty(aArguments) : NS_OK;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsGlobalWindow::DefineArgumentsProperty(nsIArray *aArguments)
|
||||
{
|
||||
MOZ_ASSERT(!mIsModalContentWindow); // Handled separately.
|
||||
nsIScriptContext *ctx = GetOuterWindowInternal()->mContext;
|
||||
NS_ENSURE_TRUE(aArguments && ctx, NS_ERROR_NOT_INITIALIZED);
|
||||
AutoPushJSContext cx(ctx->GetNativeContext());
|
||||
NS_ENSURE_TRUE(cx, NS_ERROR_NOT_INITIALIZED);
|
||||
|
||||
if (mIsModalContentWindow) {
|
||||
// Modal content windows don't have an "arguments" property, they
|
||||
// have a "dialogArguments" property which is handled
|
||||
// separately. See nsWindowSH::NewResolve().
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
JS::Rooted<JSObject*> obj(cx, mJSObject);
|
||||
return GetContextInternal()->SetProperty(obj, "arguments", aArguments);
|
||||
}
|
||||
@ -7642,6 +7650,9 @@ nsGlobalWindow::ShowModalDialog(const nsAString& aURI, nsIVariant *aArgs,
|
||||
if (Preferences::GetBool("dom.disable_window_showModalDialog", false))
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
|
||||
nsRefPtr<DialogValueHolder> argHolder =
|
||||
new DialogValueHolder(nsContentUtils::GetSubjectPrincipal(), aArgs);
|
||||
|
||||
// Before bringing up the window/dialog, unsuppress painting and flush
|
||||
// pending reflows.
|
||||
EnsureReflowFlushAndPaint();
|
||||
@ -7671,7 +7682,7 @@ nsGlobalWindow::ShowModalDialog(const nsAString& aURI, nsIVariant *aArgs,
|
||||
true, // aCalledNoScript
|
||||
true, // aDoJSFixups
|
||||
true, // aNavigate
|
||||
nullptr, aArgs, // args
|
||||
nullptr, argHolder, // args
|
||||
GetPrincipal(), // aCalleePrincipal
|
||||
nullptr, // aJSCallerContext
|
||||
getter_AddRefs(dlgWin));
|
||||
@ -11616,21 +11627,14 @@ NS_IMPL_RELEASE_INHERITED(nsGlobalModalWindow, nsGlobalWindow)
|
||||
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsGlobalModalWindow::GetDialogArguments(nsIArray **aArguments)
|
||||
nsGlobalModalWindow::GetDialogArguments(nsIVariant **aArguments)
|
||||
{
|
||||
FORWARD_TO_INNER_MODAL_CONTENT_WINDOW(GetDialogArguments, (aArguments),
|
||||
FORWARD_TO_OUTER_MODAL_CONTENT_WINDOW(GetDialogArguments, (aArguments),
|
||||
NS_ERROR_NOT_INITIALIZED);
|
||||
|
||||
bool subsumes = false;
|
||||
nsIPrincipal *self = GetPrincipal();
|
||||
if (self && NS_SUCCEEDED(self->Subsumes(mArgumentsOrigin, &subsumes)) &&
|
||||
subsumes) {
|
||||
NS_IF_ADDREF(*aArguments = mArguments);
|
||||
} else {
|
||||
*aArguments = nullptr;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
// This does an internal origin check, and returns undefined if the subject
|
||||
// does not subsumes the origin of the arguments.
|
||||
return mDialogArguments->Get(nsContentUtils::GetSubjectPrincipal(), aArguments);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
|
@ -234,6 +234,50 @@ struct IdleObserverHolder
|
||||
}
|
||||
};
|
||||
|
||||
static inline already_AddRefed<nsIVariant>
|
||||
CreateVoidVariant()
|
||||
{
|
||||
nsCOMPtr<nsIWritableVariant> writable =
|
||||
do_CreateInstance(NS_VARIANT_CONTRACTID);
|
||||
writable->SetAsVoid();
|
||||
return writable.forget();
|
||||
}
|
||||
|
||||
// Helper class to manage modal dialog arguments and all their quirks.
|
||||
//
|
||||
// Given our clunky embedding APIs, modal dialog arguments need to be passed
|
||||
// as an nsISupports parameter to WindowWatcher, get stuck inside an array of
|
||||
// length 1, and then passed back to the newly-created dialog.
|
||||
//
|
||||
// However, we need to track both the caller-passed value as well as the
|
||||
// caller's, so that we can do an origin check (even for primitives) when the
|
||||
// value is accessed. This class encapsulates that magic.
|
||||
class DialogValueHolder : public nsISupports
|
||||
{
|
||||
public:
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS(DialogValueHolder)
|
||||
|
||||
DialogValueHolder(nsIPrincipal* aSubject, nsIVariant* aValue)
|
||||
: mOrigin(aSubject)
|
||||
, mValue(aValue) {}
|
||||
nsresult Get(nsIPrincipal* aSubject, nsIVariant** aResult)
|
||||
{
|
||||
nsCOMPtr<nsIVariant> result;
|
||||
if (aSubject->Subsumes(mOrigin)) {
|
||||
result = mValue;
|
||||
} else {
|
||||
result = CreateVoidVariant();
|
||||
}
|
||||
result.forget(aResult);
|
||||
return NS_OK;
|
||||
}
|
||||
virtual ~DialogValueHolder() {}
|
||||
private:
|
||||
nsCOMPtr<nsIPrincipal> mOrigin;
|
||||
nsCOMPtr<nsIVariant> mValue;
|
||||
};
|
||||
|
||||
//*****************************************************************************
|
||||
// nsGlobalWindow: Global Object for Scripting
|
||||
//*****************************************************************************
|
||||
@ -571,7 +615,7 @@ public:
|
||||
virtual void DisableNetworkEvent(uint32_t aType);
|
||||
#endif // MOZ_B2G
|
||||
|
||||
virtual nsresult SetArguments(nsIArray *aArguments, nsIPrincipal *aOrigin);
|
||||
virtual nsresult SetArguments(nsIArray *aArguments);
|
||||
|
||||
static bool DOMWindowDumpEnabled();
|
||||
|
||||
@ -1113,8 +1157,13 @@ protected:
|
||||
nsCOMPtr<nsIScriptContext> mContext;
|
||||
nsWeakPtr mOpener;
|
||||
nsCOMPtr<nsIControllers> mControllers;
|
||||
|
||||
// For |window.arguments|, via |openDialog|.
|
||||
nsCOMPtr<nsIArray> mArguments;
|
||||
nsCOMPtr<nsIPrincipal> mArgumentsOrigin;
|
||||
|
||||
// For |window.dialogArguments|, via |showModalDialog|.
|
||||
nsRefPtr<DialogValueHolder> mDialogArguments;
|
||||
|
||||
nsRefPtr<Navigator> mNavigator;
|
||||
nsRefPtr<nsScreen> mScreen;
|
||||
nsRefPtr<nsDOMWindowList> mFrames;
|
||||
|
@ -1677,15 +1677,8 @@ nsJSContext::SetProperty(JS::Handle<JSObject*> aTarget, const char* aPropName, n
|
||||
ConvertSupportsTojsvals(aArgs, global, &argc, &argv, tempStorage);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
JS::Value vargs;
|
||||
|
||||
// got the arguments, now attach them.
|
||||
|
||||
// window.dialogArguments is supposed to be an array if a JS array
|
||||
// was passed to showModalDialog(), deal with that here.
|
||||
if (strcmp(aPropName, "dialogArguments") == 0 && argc <= 1) {
|
||||
vargs = argc ? argv[0] : JSVAL_VOID;
|
||||
} else {
|
||||
for (uint32_t i = 0; i < argc; ++i) {
|
||||
if (!JS_WrapValue(mContext, &argv[i])) {
|
||||
return NS_ERROR_FAILURE;
|
||||
@ -1693,11 +1686,8 @@ nsJSContext::SetProperty(JS::Handle<JSObject*> aTarget, const char* aPropName, n
|
||||
}
|
||||
|
||||
JSObject *args = ::JS_NewArrayObject(mContext, argc, argv);
|
||||
vargs = OBJECT_TO_JSVAL(args);
|
||||
}
|
||||
JS::Value vargs = OBJECT_TO_JSVAL(args);
|
||||
|
||||
// Make sure to use JS_DefineProperty here so that we can override
|
||||
// readonly XPConnect properties here as well (read dialogArguments).
|
||||
return JS_DefineProperty(mContext, aTarget, aPropName, vargs, NULL, NULL, 0)
|
||||
? NS_OK
|
||||
: NS_ERROR_FAILURE;
|
||||
|
@ -58,8 +58,8 @@ class AudioContext;
|
||||
}
|
||||
|
||||
#define NS_PIDOMWINDOW_IID \
|
||||
{ 0x81fe131f, 0x57c9, 0x4992, \
|
||||
{ 0xa7, 0xad, 0x82, 0x67, 0x3f, 0xc4, 0xe2, 0x53 } }
|
||||
{ 0x7202842a, 0x0e24, 0x46dc, \
|
||||
{ 0xb2, 0x25, 0xd2, 0x9d, 0x28, 0xda, 0x87, 0xd8 } }
|
||||
|
||||
class nsPIDOMWindow : public nsIDOMWindowInternal
|
||||
{
|
||||
@ -603,11 +603,14 @@ public:
|
||||
/**
|
||||
* Set a arguments for this window. This will be set on the window
|
||||
* right away (if there's an existing document) and it will also be
|
||||
* installed on the window when the next document is loaded. Each
|
||||
* language impl is responsible for converting to an array of args
|
||||
* as appropriate for that language.
|
||||
* installed on the window when the next document is loaded.
|
||||
*
|
||||
* This function serves double-duty for passing both |arguments| and
|
||||
* |dialogArguments| back from nsWindowWatcher to nsGlobalWindow. For the
|
||||
* latter, the array is an array of length 0 whose only element is a
|
||||
* DialogArgumentsHolder representing the JS value passed to showModalDialog.
|
||||
*/
|
||||
virtual nsresult SetArguments(nsIArray *aArguments, nsIPrincipal *aOrigin) = 0;
|
||||
virtual nsresult SetArguments(nsIArray *aArguments) = 0;
|
||||
|
||||
/**
|
||||
* NOTE! This function *will* be called on multiple threads so the
|
||||
|
@ -8,14 +8,15 @@
|
||||
interface nsIVariant;
|
||||
interface nsIArray;
|
||||
|
||||
[scriptable, uuid(51aebd45-b979-4ec6-9d11-3a3fd3d5d59e)]
|
||||
[scriptable, uuid(3f4cb2d0-5f7e-44a9-9f4f-370945f8db08)]
|
||||
interface nsIDOMModalContentWindow : nsISupports
|
||||
{
|
||||
/**
|
||||
* Readonly attribute containing an array of arguments that was
|
||||
* passed to the code that opened this modal content window.
|
||||
* Readonly attribute containing an arbitrary JS value passed by the
|
||||
* code that opened the modal content window. A security check is
|
||||
* performed at access time, per spec.
|
||||
*/
|
||||
readonly attribute nsIArray dialogArguments;
|
||||
readonly attribute nsIVariant dialogArguments;
|
||||
|
||||
/**
|
||||
* The return value that will be returned to the function that
|
||||
|
@ -519,20 +519,7 @@ nsWindowWatcher::OpenWindowInternal(nsIDOMWindow *aParent,
|
||||
nsCOMPtr<nsIScriptSecurityManager>
|
||||
sm(do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID));
|
||||
|
||||
NS_ENSURE_TRUE(sm, NS_ERROR_FAILURE);
|
||||
|
||||
// Remember who's calling us. This code used to assume a null
|
||||
// subject principal if it failed to get the principal, but that's
|
||||
// just not safe, so bail on errors here.
|
||||
nsCOMPtr<nsIPrincipal> callerPrincipal;
|
||||
rv = sm->GetSubjectPrincipal(getter_AddRefs(callerPrincipal));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
bool isCallerChrome = true;
|
||||
if (callerPrincipal) {
|
||||
rv = sm->IsSystemPrincipal(callerPrincipal, &isCallerChrome);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
bool isCallerChrome = nsContentUtils::IsCallerChrome();
|
||||
|
||||
JSContext *cx = GetJSContextFromWindow(aParent);
|
||||
|
||||
@ -746,7 +733,7 @@ nsWindowWatcher::OpenWindowInternal(nsIDOMWindow *aParent,
|
||||
nsCOMPtr<nsPIDOMWindow> piwin(do_QueryInterface(*_retval));
|
||||
NS_ENSURE_TRUE(piwin, NS_ERROR_UNEXPECTED);
|
||||
|
||||
rv = piwin->SetArguments(argv, callerPrincipal);
|
||||
rv = piwin->SetArguments(argv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user