зеркало из https://github.com/mozilla/pjs.git
Fixing bug 304423. Make window instanceof Object and Window etc be true again. This regressed with the split window landing. The fix here is to make the inner and outer windows share the outer's XPConnect prototype (but only that, everything below that on the proto chain comes from the inner window). To make this work with fastback we also needed a way to tell XPConnect to restore an old prototype for the window object when going back/forward. r=mrbkap@gmail.com, sr=brendan@mozilla.org
This commit is contained in:
Родитель
c18f957613
Коммит
de940fdeab
|
@ -2996,6 +2996,15 @@ nsDOMClassInfo::PostCreate(nsIXPConnectWrappedNative *wrapper,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
{
|
||||
nsCOMPtr<nsIScriptGlobalObject> sgo(do_QueryWrappedNative(wrapper));
|
||||
|
||||
NS_ASSERTION(!sgo || sgo->GetGlobalJSObject() == nsnull,
|
||||
"Multiple wrappers created for global object!");
|
||||
}
|
||||
#endif
|
||||
|
||||
JSObject *proto = nsnull;
|
||||
|
||||
wrapper->GetJSObjectPrototype(&proto);
|
||||
|
@ -5177,8 +5186,30 @@ nsWindowSH::GlobalResolve(nsGlobalWindow *aWin, JSContext *cx,
|
|||
|
||||
nsCOMPtr<nsIXPConnectJSObjectHolder> proto_holder;
|
||||
|
||||
// In most cases we want to find the wrapped native prototype in
|
||||
// aWin's scope and use that prototype for
|
||||
// ClassName.prototype. But in the case where we're setting up
|
||||
// "Window.prototype" or "ChromeWindow.prototype" we want to do
|
||||
// the look up in aWin's outer window's scope since the inner
|
||||
// window's wrapped native prototype comes from the outer
|
||||
// window's scope.
|
||||
nsGlobalWindow *scopeWindow;
|
||||
|
||||
if (ci_id == eDOMClassInfo_Window_id ||
|
||||
ci_id == eDOMClassInfo_ChromeWindow_id) {
|
||||
scopeWindow = aWin->GetOuterWindowInternal();
|
||||
|
||||
if (!scopeWindow) {
|
||||
scopeWindow = aWin;
|
||||
}
|
||||
} else {
|
||||
scopeWindow = aWin;
|
||||
}
|
||||
|
||||
rv =
|
||||
sXPConnect->GetWrappedNativePrototype(cx, obj, ci,
|
||||
sXPConnect->GetWrappedNativePrototype(cx,
|
||||
scopeWindow->GetGlobalJSObject(),
|
||||
ci,
|
||||
getter_AddRefs(proto_holder));
|
||||
NS_ENSURE_SUCCESS(rv, NS_ERROR_UNEXPECTED);
|
||||
|
||||
|
@ -5395,39 +5426,6 @@ nsWindowSH::NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
|
|||
|
||||
OBJ_DROP_PROPERTY(cx, pobj, prop);
|
||||
|
||||
JSObject *proto;
|
||||
if (pobj != innerObj && (proto = ::JS_GetPrototype(cx, obj))) {
|
||||
// A property was found on one of innerObj's prototypes,
|
||||
// this means the property could've been found on the
|
||||
// XPCWrappedNative proto, which means that the property is
|
||||
// likely a function (getter, setter, or a method). In this
|
||||
// case we can't expose this method as a property of the
|
||||
// outer since a call to it would be on the wrong object and
|
||||
// XPConnect can't deal. In this case only, check if the
|
||||
// property also exists on our prototype, and if it does,
|
||||
// return it instead of the one found on the inner object's
|
||||
// prototype chain.
|
||||
|
||||
#ifdef DEBUG_SH_FORWARDING
|
||||
printf(" ... but the propety was found on the prototype, "
|
||||
"checking to see if the property also exists on our "
|
||||
"prototype.\n");
|
||||
#endif
|
||||
|
||||
JSObject *mypobj;
|
||||
OBJ_LOOKUP_PROPERTY(cx, proto, interned_id, &mypobj,
|
||||
&prop);
|
||||
|
||||
if (prop) {
|
||||
// We found the prop on obj's prototype too, use it
|
||||
// instead.
|
||||
|
||||
OBJ_DROP_PROPERTY(cx, mypobj, prop);
|
||||
|
||||
pobj = mypobj;
|
||||
}
|
||||
}
|
||||
|
||||
*objp = pobj;
|
||||
}
|
||||
|
||||
|
|
|
@ -655,7 +655,7 @@ nsGlobalWindow::GetPopupControlState() const
|
|||
}
|
||||
|
||||
#define WINDOWSTATEHOLDER_IID \
|
||||
{0x6fb7a1b5, 0x2dfe, 0x40a7, {0xbc, 0xee, 0x1f, 0xac, 0xd2, 0x8d, 0x47, 0x62}}
|
||||
{0x0b917c3e, 0xbd50, 0x4683, {0xaf, 0xc9, 0xc7, 0x81, 0x07, 0xae, 0x33, 0x26}}
|
||||
|
||||
class WindowStateHolder : public nsISupports
|
||||
{
|
||||
|
@ -666,7 +666,8 @@ public:
|
|||
WindowStateHolder(nsGlobalWindow *aWindow,
|
||||
nsIXPConnectJSObjectHolder *aHolder,
|
||||
nsNavigator *aNavigator,
|
||||
nsLocation *aLocation);
|
||||
nsLocation *aLocation,
|
||||
nsIXPConnectJSObjectHolder *aOuterProto);
|
||||
|
||||
// Get the contents of focus memory when the state was saved
|
||||
// (if the focus was inside of this window).
|
||||
|
@ -679,6 +680,7 @@ public:
|
|||
|
||||
nsNavigator* GetNavigator() { return mNavigator; }
|
||||
nsLocation* GetLocation() { return mLocation; }
|
||||
nsIXPConnectJSObjectHolder* GetOuterProto() { return mOuterProto; }
|
||||
|
||||
void DidRestoreWindow()
|
||||
{
|
||||
|
@ -686,6 +688,7 @@ public:
|
|||
mInnerWindowHolder = nsnull;
|
||||
mNavigator = nsnull;
|
||||
mLocation = nsnull;
|
||||
mOuterProto = nsnull;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
@ -699,16 +702,19 @@ protected:
|
|||
nsRefPtr<nsLocation> mLocation;
|
||||
nsCOMPtr<nsIDOMElement> mFocusedElement;
|
||||
nsCOMPtr<nsIDOMWindowInternal> mFocusedWindow;
|
||||
nsCOMPtr<nsIXPConnectJSObjectHolder> mOuterProto;
|
||||
};
|
||||
|
||||
WindowStateHolder::WindowStateHolder(nsGlobalWindow *aWindow,
|
||||
nsIXPConnectJSObjectHolder *aHolder,
|
||||
nsNavigator *aNavigator,
|
||||
nsLocation *aLocation)
|
||||
nsLocation *aLocation,
|
||||
nsIXPConnectJSObjectHolder *aOuterProto)
|
||||
: mInnerWindow(aWindow),
|
||||
mInnerWindowHolder(aHolder),
|
||||
mNavigator(aNavigator),
|
||||
mLocation(aLocation)
|
||||
mLocation(aLocation),
|
||||
mOuterProto(aOuterProto)
|
||||
{
|
||||
NS_PRECONDITION(aWindow, "null window");
|
||||
NS_PRECONDITION(aWindow->IsInnerWindow(), "Saving an outer window");
|
||||
|
@ -1023,10 +1029,10 @@ nsGlobalWindow::SetNewDocument(nsIDOMDocument* aDocument,
|
|||
// and proto, which makes the JS engine look up classes in
|
||||
// cx->globalObject, i.e. this outer window].
|
||||
|
||||
Freeze();
|
||||
mInnerWindow = nsnull;
|
||||
|
||||
nsresult rv = xpc->
|
||||
Freeze();
|
||||
rv = xpc->
|
||||
InitClassesWithNewWrappedGlobal(cx, sgo, NS_GET_IID(nsISupports),
|
||||
flags,
|
||||
getter_AddRefs(mInnerWindowHolder));
|
||||
|
@ -1104,12 +1110,82 @@ nsGlobalWindow::SetNewDocument(nsIDOMDocument* aDocument,
|
|||
html_doc);
|
||||
}
|
||||
|
||||
// InitClassesWithNewWrappedGlobal() for the new inner window sets
|
||||
// the global object in cx to be the new wrapped global. We don't
|
||||
// want that, but re-initializing the outer window will fix that
|
||||
// for us. This has to happen unconditionally since we clear the
|
||||
// outer window's scope unconditionally.
|
||||
scx->InitContext(this);
|
||||
if (!aState && !reUseInnerWindow) {
|
||||
// Loading a new page and creating a new inner window, *not*
|
||||
// restoring from session history.
|
||||
|
||||
// InitClassesWithNewWrappedGlobal() for the new inner window
|
||||
// sets the global object in cx to be the new wrapped global. We
|
||||
// don't want that, but re-initializing the outer window will
|
||||
// fix that for us. And perhaps more importantly, this will
|
||||
// ensure that the outer window gets a new prototype so we don't
|
||||
// leak prototype properties from the old inner window to the
|
||||
// new one.
|
||||
|
||||
scx->InitContext(this);
|
||||
|
||||
// Now that both the the inner and outer windows are initialized
|
||||
// we can clear the outer scope again to make *all* properties
|
||||
// forward to the inner window.
|
||||
::JS_ClearScope(cx, mJSObject);
|
||||
|
||||
// Make the inner and outer window both share the same
|
||||
// prototype. The prototype we share is the outer window's
|
||||
// prototype, this way XPConnect can still find the wrapper to
|
||||
// use when making a call like alert() (w/o qualifying it with
|
||||
// "window."). XPConnect looks up the wrapper based on the
|
||||
// function object's parent, which is the object the function
|
||||
// was called on, and when calling alert() we'll be calling the
|
||||
// alert() function from the outer window's prototype off of the
|
||||
// inner window. In this case XPConnect is able to find the
|
||||
// outer (through the JSExtendedClass hook outerObject), so this
|
||||
// prototype sharing works.
|
||||
|
||||
// We do *not* want to use anything else out of the outer
|
||||
// object's prototype chain than the first prototype, which is
|
||||
// the XPConnect prototype. The rest we want from the inner
|
||||
// window's prototype, i.e. the global scope polluter and
|
||||
// Object.prototype. This way the outer also gets the benefits
|
||||
// of the global scope polluter, and the inner window's
|
||||
// Object.prototype.
|
||||
JSObject *proto = ::JS_GetPrototype(cx, mJSObject);
|
||||
JSObject *innerProto = ::JS_GetPrototype(cx, newInnerWindow->mJSObject);
|
||||
JSObject *innerProtoProto = ::JS_GetPrototype(cx, innerProto);
|
||||
|
||||
::JS_SetPrototype(cx, newInnerWindow->mJSObject, proto);
|
||||
::JS_SetPrototype(cx, proto, innerProtoProto);
|
||||
}
|
||||
|
||||
if (aState) {
|
||||
// Restoring from session history.
|
||||
|
||||
nsCOMPtr<WindowStateHolder> wsh = do_QueryInterface(aState);
|
||||
NS_ASSERTION(wsh, "What kind of weird state are you giving me here?");
|
||||
|
||||
// Restore the prototype for the Window/ChromeWindow class in
|
||||
// the outer window scope.
|
||||
nsCOMPtr<nsIClassInfo> ci =
|
||||
do_QueryInterface((nsIScriptGlobalObject *)this);
|
||||
|
||||
rv = xpc->RestoreWrappedNativePrototype(cx, mJSObject, ci,
|
||||
wsh->GetOuterProto());
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Refresh the outer window's prototype to what it was when the
|
||||
// window state was saved. This will make the outer window
|
||||
// object (and wrapper) pick up the prototype it had when the
|
||||
// window state was saved. This means Object.prototype etc from
|
||||
// the old inner will again be on the outer window's prototype
|
||||
// chain.
|
||||
|
||||
nsCOMPtr<nsIXPConnectWrappedNative> wrapper;
|
||||
rv = xpc->GetWrappedNativeOfJSObject(cx, mJSObject,
|
||||
getter_AddRefs(wrapper));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = wrapper->RefreshPrototype();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
if (newDoc) {
|
||||
newDoc->SetScriptGlobalObject(newInnerWindow);
|
||||
|
@ -6670,10 +6746,20 @@ nsGlobalWindow::SaveWindowState(nsISupports **aState)
|
|||
nsGlobalWindow *inner = GetCurrentInnerWindowInternal();
|
||||
NS_ASSERTION(inner, "No inner window to save");
|
||||
|
||||
// Remember the outer window's XPConnect prototype.
|
||||
nsCOMPtr<nsIClassInfo> ci =
|
||||
do_QueryInterface((nsIScriptGlobalObject *)this);
|
||||
nsCOMPtr<nsIXPConnectJSObjectHolder> proto;
|
||||
nsresult rv = nsContentUtils::XPConnect()->
|
||||
GetWrappedNativePrototype((JSContext *)mContext->GetNativeContext(),
|
||||
mJSObject, ci, getter_AddRefs(proto));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<nsISupports> state = new WindowStateHolder(inner,
|
||||
mInnerWindowHolder,
|
||||
mNavigator,
|
||||
mLocation);
|
||||
mLocation,
|
||||
proto);
|
||||
NS_ENSURE_TRUE(state, NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
#ifdef DEBUG_PAGE_CACHE
|
||||
|
|
|
@ -1616,9 +1616,10 @@ nsJSContext::InitContext(nsIScriptGlobalObject *aGlobalObject)
|
|||
}
|
||||
} else {
|
||||
// If there's already a global object in mContext we're called
|
||||
// after ::JS_ClearScope() was called. We'll have to tell XPConnect
|
||||
// to re-initialize the global object to do things like define the
|
||||
// Components object on the global again.
|
||||
// after ::JS_ClearScope() was called. We'll have to tell
|
||||
// XPConnect to re-initialize the global object to do things like
|
||||
// define the Components object on the global again and forget all
|
||||
// old prototypes in this scope.
|
||||
rv = xpc->InitClasses(mContext, global);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче