diff --git a/content/base/src/nsFrameLoader.cpp b/content/base/src/nsFrameLoader.cpp index 0b8f36f20a2..cea1a7f9596 100644 --- a/content/base/src/nsFrameLoader.cpp +++ b/content/base/src/nsFrameLoader.cpp @@ -937,7 +937,10 @@ nsFrameLoader::ShowRemoteFrame(const nsIntSize& size) nsCOMPtr os = services::GetObserverService(); if (OwnerIsBrowserFrame() && os) { os->NotifyObservers(NS_ISUPPORTS_CAST(nsIFrameLoader*, this), - "remote-browser-frame-shown", NULL); + "remote-browser-frame-shown", + mOwnerContent->HasAttr(kNameSpaceID_None, nsGkAtoms::mozapp) + ? NS_LITERAL_STRING("is-moz-app:true").get() + : NS_LITERAL_STRING("is-moz-app:false").get()); } } else { nsRect dimensions; @@ -1522,7 +1525,10 @@ nsFrameLoader::MaybeCreateDocShell() if (OwnerIsBrowserFrame() && os) { mDocShell->SetIsBrowserFrame(true); os->NotifyObservers(NS_ISUPPORTS_CAST(nsIFrameLoader*, this), - "in-process-browser-frame-shown", NULL); + "in-process-browser-frame-shown", + mOwnerContent->HasAttr(kNameSpaceID_None, nsGkAtoms::mozapp) + ? NS_LITERAL_STRING("is-moz-app:true").get() + : NS_LITERAL_STRING("is-moz-app:false").get()); } // This is nasty, this code (the do_GetInterface(mDocShell) below) diff --git a/content/base/src/nsGkAtomList.h b/content/base/src/nsGkAtomList.h index 673ac99985f..034dc33d26b 100644 --- a/content/base/src/nsGkAtomList.h +++ b/content/base/src/nsGkAtomList.h @@ -108,6 +108,7 @@ GK_ATOM(ancestor, "ancestor") GK_ATOM(ancestorOrSelf, "ancestor-or-self") GK_ATOM(_and, "and") GK_ATOM(any, "any") +GK_ATOM(mozapp, "mozapp") GK_ATOM(applet, "applet") GK_ATOM(applyImports, "apply-imports") GK_ATOM(applyTemplates, "apply-templates") diff --git a/content/html/content/src/nsGenericHTMLFrameElement.cpp b/content/html/content/src/nsGenericHTMLFrameElement.cpp index 0d6d5a24ba3..3dad5f91386 100644 --- a/content/html/content/src/nsGenericHTMLFrameElement.cpp +++ b/content/html/content/src/nsGenericHTMLFrameElement.cpp @@ -284,6 +284,7 @@ nsGenericHTMLFrameElement::GetReallyIsBrowser(bool *aOut) } // Fail if the node principal isn't trusted. + // TODO: check properly for mozApps rights when mozApps will be less hacky. nsIPrincipal *principal = NodePrincipal(); nsCOMPtr principalURI; principal->GetURI(getter_AddRefs(principalURI)); diff --git a/dom/base/BrowserElementChild.js b/dom/base/BrowserElementChild.js index 77ce4dcfe90..d1ad3d4b5d7 100644 --- a/dom/base/BrowserElementChild.js +++ b/dom/base/BrowserElementChild.js @@ -41,6 +41,17 @@ BrowserElementChild.prototype = { Ci.nsIWebProgress.NOTIFY_LOCATION | Ci.nsIWebProgress.NOTIFY_STATE_WINDOW); + // A mozbrowser iframe contained inside a mozapp iframe should return false + // for nsWindowUtils::IsPartOfApp (unless the mozbrowser iframe is itself + // also mozapp). That is, mozapp is transitive down to its children, but + // mozbrowser serves as a barrier. + // + // This is because mozapp iframes have some privileges which we don't want + // to extend to untrusted mozbrowser content. + content.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Components.interfaces.nsIDOMWindowUtils) + .setIsApp(false); + addEventListener('DOMTitleChanged', this._titleChangedHandler.bind(this), /* useCapture = */ true, diff --git a/dom/base/BrowserElementParent.js b/dom/base/BrowserElementParent.js index eda4ccd8fcb..68dbba75d89 100644 --- a/dom/base/BrowserElementParent.js +++ b/dom/base/BrowserElementParent.js @@ -67,17 +67,17 @@ BrowserElementParent.prototype = { } }, - _observeInProcessBrowserFrameShown: function(frameLoader) { + _observeInProcessBrowserFrameShown: function(frameLoader, isMozApp) { debug("In-process browser frame shown " + frameLoader); - this._setUpMessageManagerListeners(frameLoader); + this._setUpMessageManagerListeners(frameLoader, isMozApp); }, - _observeRemoteBrowserFrameShown: function(frameLoader) { + _observeRemoteBrowserFrameShown: function(frameLoader, isMozApp) { debug("Remote browser frame shown " + frameLoader); - this._setUpMessageManagerListeners(frameLoader); + this._setUpMessageManagerListeners(frameLoader, isMozApp); }, - _setUpMessageManagerListeners: function(frameLoader) { + _setUpMessageManagerListeners: function(frameLoader, isMozApp) { let frameElement = frameLoader.QueryInterface(Ci.nsIFrameLoader).ownerElement; if (!frameElement) { debug("No frame element?"); @@ -102,6 +102,12 @@ BrowserElementParent.prototype = { mm.loadFrameScript("chrome://global/content/BrowserElementChild.js", /* allowDelayedLoad = */ true); + if (isMozApp) { + mm.loadFrameScript("data:,content.QueryInterface(Ci.nsIInterfaceRequestor)" + + " .getInterface(Components.interfaces.nsIDOMWindowUtils)" + + " .setIsApp(true);", + /* allowDelayedLoad = */ true); + } }, _recvHello: function(frameElement, data) { @@ -144,10 +150,10 @@ BrowserElementParent.prototype = { } break; case 'remote-browser-frame-shown': - this._observeRemoteBrowserFrameShown(subject); + this._observeRemoteBrowserFrameShown(subject, data == "is-moz-app:true"); break; case 'in-process-browser-frame-shown': - this._observeInProcessBrowserFrameShown(subject); + this._observeInProcessBrowserFrameShown(subject, data == "is-moz-app:true"); break; case 'content-document-global-created': this._observeContentGlobalCreated(subject); diff --git a/dom/base/nsDOMWindowUtils.cpp b/dom/base/nsDOMWindowUtils.cpp index 32557638c39..97cdc1bbc82 100644 --- a/dom/base/nsDOMWindowUtils.cpp +++ b/dom/base/nsDOMWindowUtils.cpp @@ -2453,3 +2453,14 @@ nsDOMWindowUtils::SetScrollPositionClampingScrollPortSize(float aWidth, float aH return NS_OK; } + +NS_IMETHODIMP +nsDOMWindowUtils::SetIsApp(bool aValue) +{ + nsCOMPtr window = do_QueryReferent(mWindow); + NS_ENSURE_TRUE(window, NS_ERROR_FAILURE); + + static_cast(window.get())->SetIsApp(aValue); + + return NS_OK; +} diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp index 118bdb04579..9dccce2b424 100644 --- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -692,6 +692,7 @@ nsGlobalWindow::nsGlobalWindow(nsGlobalWindow *aOuterWindow) mShowFocusRingForContent(false), mFocusByKeyOccurred(false), mNotifiedIDDestroyed(false), + mIsApp(TriState_Unknown), mTimeoutInsertionPoint(nsnull), mTimeoutPublicIdCounter(1), mTimeoutFiringDepth(0), @@ -10015,6 +10016,33 @@ nsGlobalWindow::SizeOfIncludingThis(nsWindowSizes* aWindowSizes) const mNavigator->SizeOfIncludingThis(aWindowSizes->mMallocSizeOf) : 0; } +void +nsGlobalWindow::SetIsApp(bool aValue) +{ + FORWARD_TO_OUTER_VOID(SetIsApp, (aValue)); + + mIsApp = aValue ? TriState_True : TriState_False; +} + +bool +nsGlobalWindow::IsPartOfApp() +{ + FORWARD_TO_OUTER(IsPartOfApp, (), TriState_False); + + // We go trough all window parents until we find one with |mIsApp| set to + // something. If none is found, we are not part of an application. + for (nsGlobalWindow* w = this; w; + w = static_cast(w->GetParentInternal())) { + if (w->mIsApp == TriState_True) { + return true; + } else if (w->mIsApp == TriState_False) { + return false; + } + } + + return false; +} + // nsGlobalChromeWindow implementation NS_IMPL_CYCLE_COLLECTION_CLASS(nsGlobalChromeWindow) diff --git a/dom/base/nsGlobalWindow.h b/dom/base/nsGlobalWindow.h index 39ce418cf65..8e22b091e0c 100644 --- a/dom/base/nsGlobalWindow.h +++ b/dom/base/nsGlobalWindow.h @@ -558,10 +558,23 @@ public: void AddEventTargetObject(nsDOMEventTargetHelper* aObject); void RemoveEventTargetObject(nsDOMEventTargetHelper* aObject); + /** + * Returns if the window is part of an application. + * It will check for the window app state and its parents until a window has + * an app state different from |TriState_Unknown|. + */ + bool IsPartOfApp(); + protected: friend class HashchangeCallback; friend class nsBarProp; + enum TriState { + TriState_Unknown = -1, + TriState_False, + TriState_True + }; + // Object Management virtual ~nsGlobalWindow(); void CleanUp(bool aIgnoreModalDialog); @@ -811,6 +824,8 @@ protected: nsresult CloneStorageEvent(const nsAString& aType, nsCOMPtr& aEvent); + void SetIsApp(bool aValue); + // When adding new member variables, be careful not to create cycles // through JavaScript. If there is any chance that a member variable // could own objects that are implemented in JavaScript, then those @@ -878,6 +893,11 @@ protected: // whether we've sent the destroy notification for our window id bool mNotifiedIDDestroyed : 1; + // Whether the window is the window of an application frame. + // This is TriState_Unknown if the object is the content window of an + // iframe which is neither mozBrowser nor mozApp. + TriState mIsApp : 2; + nsCOMPtr mContext; nsWeakPtr mOpener; nsCOMPtr mControllers; diff --git a/dom/interfaces/base/nsIDOMWindowUtils.idl b/dom/interfaces/base/nsIDOMWindowUtils.idl index 0ee77a50f3e..92b33e6f31f 100644 --- a/dom/interfaces/base/nsIDOMWindowUtils.idl +++ b/dom/interfaces/base/nsIDOMWindowUtils.idl @@ -70,7 +70,7 @@ interface nsIDOMFile; interface nsIFile; interface nsIDOMTouch; -[scriptable, uuid(f75d0a14-e278-4716-a151-637862451a2f)] +[scriptable, uuid(2e5a1f37-786b-4a52-b0e3-f711ee2268a8)] interface nsIDOMWindowUtils : nsISupports { /** @@ -1149,4 +1149,13 @@ interface nsIDOMWindowUtils : nsISupports { * The caller of this method must have UniversalXPConnect privileges. */ void setScrollPositionClampingScrollPortSize(in float aWidth, in float aHeight); + + /** + * Mark if the window is an application window or not. + * This should only be set for top-level mozApp or mozBrowser frames. + * It should not be set for other frames unless you want a frame (and its + * children) to have a different value for IsPartOfApp than the frame's + * parent. + */ + void setIsApp(in boolean value); };