Bug 1268688 - Start browser API for frames swapping to HTML. r=bz
MozReview-Commit-ID: 56lMg0b86Bp
--- a/dom/base/nsFrameLoader.cpp
+++ b/dom/base/nsFrameLoader.cpp
@@ -998,20 +998,38 @@ nsFrameLoader::SwapWithOtherRemoteLoader
ourFrameFrame->EndSwapDocShells(otherFrame);
ourShell->BackingScaleFactorChanged();
otherShell->BackingScaleFactorChanged();
ourDoc->FlushPendingNotifications(Flush_Layout);
otherDoc->FlushPendingNotifications(Flush_Layout);
+ // Initialize browser API if needed now that owner content has changed.
+ InitializeBrowserAPI();
+ aOther->InitializeBrowserAPI();
+
mInSwap = aOther->mInSwap = false;
- Unused << mRemoteBrowser->SendSwappedWithOtherRemoteLoader();
- Unused << aOther->mRemoteBrowser->SendSwappedWithOtherRemoteLoader();
+ // Send an updated tab context since owner content type may have changed.
+ MutableTabContext ourContext;
+ rv = GetNewTabContext(&ourContext);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ MutableTabContext otherContext;
+ rv = aOther->GetNewTabContext(&otherContext);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ Unused << mRemoteBrowser->SendSwappedWithOtherRemoteLoader(
+ ourContext.AsIPCTabContext());
+ Unused << aOther->mRemoteBrowser->SendSwappedWithOtherRemoteLoader(
+ otherContext.AsIPCTabContext());
return NS_OK;
}
class MOZ_RAII AutoResetInFrameSwap final
{
public:
AutoResetInFrameSwap(nsFrameLoader* aThisFrameLoader,
nsFrameLoader* aOtherFrameLoader,
@@ -1390,16 +1408,20 @@ nsFrameLoader::SwapWithOtherLoader(nsFra
// the wrong appUnitsPerDevPixel value. So we tell the PresShells that their
// backing scale factor may have changed. (Bug 822266)
ourShell->BackingScaleFactorChanged();
otherShell->BackingScaleFactorChanged();
ourParentDocument->FlushPendingNotifications(Flush_Layout);
otherParentDocument->FlushPendingNotifications(Flush_Layout);
+ // Initialize browser API if needed now that owner content has changed
+ InitializeBrowserAPI();
+ aOther->InitializeBrowserAPI();
+
return NS_OK;
}
NS_IMETHODIMP
nsFrameLoader::Destroy()
{
StartDestroy();
return NS_OK;
--- a/dom/base/test/chrome/test_swapFrameLoaders.xul
+++ b/dom/base/test/chrome/test_swapFrameLoaders.xul
@@ -13,14 +13,13 @@ Test swapFrameLoaders with different fra
<body xmlns="http://www.w3.org/1999/xhtml">
<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1242644"
target="_blank">Mozilla Bug 1242644</a>
</body>
<!-- test code goes here -->
<script type="application/javascript"><![CDATA[
SimpleTest.waitForExplicitFinish();
- SimpleTest.requestLongerTimeout(100);
window.open("window_swapFrameLoaders.xul", "bug1242644",
"chrome,width=600,height=600");
]]></script>
</window>
--- a/dom/base/test/chrome/window_swapFrameLoaders.xul
+++ b/dom/base/test/chrome/window_swapFrameLoaders.xul
@@ -31,16 +31,30 @@ Test swapFrameLoaders with different fra
["html", "xul"],
["html", "html"],
["xul", "xul", "remote"],
["xul", "html", "remote"],
["html", "xul", "remote"],
["html", "html", "remote"],
];
+ const HEIGHTS = [
+ 200,
+ 400
+ ];
+
+ function frameScript() {
+ addEventListener("load", function onLoad() {
+ sendAsyncMessage("test:load");
+ }, true);
+ }
+
+ // Watch for loads in new frames
+ window.messageManager.loadFrameScript(`data:,(${frameScript})();`, true);
+
function once(target, eventName, useCapture = false) {
info("Waiting for event: '" + eventName + "' on " + target + ".");
return new Promise(resolve => {
for (let [add, remove] of [
["addEventListener", "removeEventListener"],
["addMessageListener", "removeMessageListener"],
]) {
@@ -51,51 +65,57 @@ Test swapFrameLoaders with different fra
resolve(aArgs);
}, useCapture);
break;
}
}
});
}
- function* addFrame(type, remote) {
+ function* addFrame(type, remote, height) {
let frame = document.createElementNS(NS[type], TAG[type]);
frame.setAttribute("remote", remote);
if (remote && type == "xul") {
frame.setAttribute("style", "-moz-binding: none;");
}
if (type == "html") {
frame.setAttribute("mozbrowser", "true");
frame.setAttribute("noisolation", "true");
frame.setAttribute("allowfullscreen", "true");
} else if (type == "xul") {
frame.setAttribute("type", "content");
}
- frame.setAttribute("src", "about:blank");
+ let src = `data:text/html,<!doctype html>` +
+ `<body style="height:${height}px"/>`;
+ frame.setAttribute("src", src);
document.documentElement.appendChild(frame);
+ let mm = frame.frameLoader.messageManager;
+ yield once(mm, "test:load");
return frame;
}
add_task(function*() {
yield new Promise(resolve => {
SpecialPowers.pushPrefEnv(
{ "set": [["dom.mozBrowserFramesEnabled", true]] },
resolve);
});
});
add_task(function*() {
for (let scenario of SCENARIOS) {
let [ typeA, typeB, remote ] = scenario;
remote = !!remote;
- info(`Adding frame A, type ${typeA}, remote ${remote}`);
- let frameA = yield addFrame(typeA, remote);
+ let heightA = HEIGHTS[0];
+ info(`Adding frame A, type ${typeA}, remote ${remote}, height ${heightA}`);
+ let frameA = yield addFrame(typeA, remote, heightA);
- info(`Adding frame B, type ${typeB}, remote ${remote}`);
- let frameB = yield addFrame(typeB, remote);
+ let heightB = HEIGHTS[1];
+ info(`Adding frame B, type ${typeB}, remote ${remote}, height ${heightB}`);
+ let frameB = yield addFrame(typeB, remote, heightB);
let frameScriptFactory = function(name) {
return `function() {
addMessageListener("ping", function() {
sendAsyncMessage("pong", "${name}");
});
}`;
}
@@ -123,16 +143,28 @@ Test swapFrameLoaders with different fra
is(pongA, "A", "Frame A message manager gets reply A before swap");
info("Ping message manager for frame B");
mmB.sendAsyncMessage("ping");
let [ { data: pongB } ] = yield inflightB;
is(pongB, "B", "Frame B message manager gets reply B before swap");
}
+ // Check height before swap
+ {
+ if (frameA.getContentDimensions) {
+ let { height } = yield frameA.getContentDimensions();
+ is(height, heightA, "Frame A's content height is 200px before swap");
+ }
+ if (frameB.getContentDimensions) {
+ let { height } = yield frameB.getContentDimensions();
+ is(height, heightB, "Frame B's content height is 400px before swap");
+ }
+ }
+
// Ping after swap using message managers acquired before
{
let mmA = frameA.frameLoader.messageManager;
let mmB = frameB.frameLoader.messageManager;
info("swapFrameLoaders");
frameA.swapFrameLoaders(frameB);
@@ -145,16 +177,28 @@ Test swapFrameLoaders with different fra
is(pongA, "B", "Frame A message manager acquired before swap gets reply B after swap");
info("Ping message manager for frame B");
mmB.sendAsyncMessage("ping");
let [ { data: pongB } ] = yield inflightB;
is(pongB, "A", "Frame B message manager acquired before swap gets reply A after swap");
}
+ // Check height after swap
+ {
+ if (frameA.getContentDimensions) {
+ let { height } = yield frameA.getContentDimensions();
+ is(height, heightB, "Frame A's content height is 400px after swap");
+ }
+ if (frameB.getContentDimensions) {
+ let { height } = yield frameB.getContentDimensions();
+ is(height, heightA, "Frame B's content height is 200px after swap");
+ }
+ }
+
// Ping after swap using message managers acquired after
{
let mmA = frameA.frameLoader.messageManager;
let mmB = frameB.frameLoader.messageManager;
let inflightA = once(mmA, "pong");
let inflightB = once(mmB, "pong");
--- a/dom/ipc/PBrowser.ipdl
+++ b/dom/ipc/PBrowser.ipdl
@@ -14,16 +14,17 @@ include protocol PDocumentRenderer;
include protocol PFilePicker;
include protocol PIndexedDBPermissionRequest;
include protocol PRenderFrame;
include protocol PPluginWidget;
include DOMTypes;
include JavaScriptTypes;
include URIParams;
include BrowserConfiguration;
+include PTabContext;
using class IPC::Principal from "mozilla/dom/PermissionMessageUtils.h";
using class mozilla::gfx::Matrix from "mozilla/gfx/Matrix.h";
using struct gfxSize from "gfxPoint.h";
using CSSRect from "Units.h";
using CSSSize from "Units.h";
using mozilla::LayoutDeviceIntRect from "Units.h";
@@ -719,17 +720,17 @@ child:
* Tell the child of an app's offline status
*/
async AppOfflineStatus(uint32_t id, bool offline);
/**
* Tell the browser that its frame loader has been swapped
* with another.
*/
- async SwappedWithOtherRemoteLoader();
+ async SwappedWithOtherRemoteLoader(IPCTabContext context);
/**
* A potential accesskey was just pressed. Look for accesskey targets
* using the list of provided charCodes.
*
* @param charCode array of potential character codes
* @param isTrusted true if triggered by a trusted key event
* @param modifierMask indicates which accesskey modifiers are pressed
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -2282,17 +2282,17 @@ TabChild::RecvAppOfflineStatus(const uin
if (gIOService && ioService) {
gIOService->SetAppOfflineInternal(aId, aOffline ?
nsIAppOfflineInfo::OFFLINE : nsIAppOfflineInfo::ONLINE);
}
return true;
}
bool
-TabChild::RecvSwappedWithOtherRemoteLoader()
+TabChild::RecvSwappedWithOtherRemoteLoader(const IPCTabContext& aContext)
{
nsCOMPtr<nsIDocShell> ourDocShell = do_GetInterface(WebNavigation());
if (NS_WARN_IF(!ourDocShell)) {
return true;
}
nsCOMPtr<nsPIDOMWindowOuter> ourWindow = ourDocShell->GetWindow();
if (NS_WARN_IF(!ourWindow)) {
@@ -2302,16 +2302,39 @@ TabChild::RecvSwappedWithOtherRemoteLoad
RefPtr<nsDocShell> docShell = static_cast<nsDocShell*>(ourDocShell.get());
nsCOMPtr<EventTarget> ourEventTarget = ourWindow->GetParentTarget();
docShell->SetInFrameSwap(true);
nsContentUtils::FirePageShowEvent(ourDocShell, ourEventTarget, false);
nsContentUtils::FirePageHideEvent(ourDocShell, ourEventTarget);
+
+ // Owner content type may have changed, so store the possibly updated context
+ // and notify others.
+ MaybeInvalidTabContext maybeContext(aContext);
+ if (!maybeContext.IsValid()) {
+ NS_ERROR(nsPrintfCString("Received an invalid TabContext from "
+ "the parent process. (%s)",
+ maybeContext.GetInvalidReason()).get());
+ MOZ_CRASH("Invalid TabContext received from the parent process.");
+ }
+
+ if (!UpdateTabContextAfterSwap(maybeContext.GetTabContext())) {
+ MOZ_CRASH("Update to TabContext after swap was denied.");
+ }
+ NotifyTabContextUpdated();
+
+ // Ignore previous value of mTriedBrowserInit since owner content has changed.
+ mTriedBrowserInit = true;
+ // Initialize the child side of the browser element machinery, if appropriate.
+ if (IsMozBrowserOrApp()) {
+ RecvLoadRemoteScript(BROWSER_ELEMENT_CHILD_SCRIPT, true);
+ }
+
nsContentUtils::FirePageShowEvent(ourDocShell, ourEventTarget, true);
docShell->SetInFrameSwap(false);
return true;
}
bool
--- a/dom/ipc/TabChild.h
+++ b/dom/ipc/TabChild.h
@@ -412,17 +412,18 @@ public:
virtual bool RecvAsyncMessage(const nsString& aMessage,
InfallibleTArray<CpowEntry>&& aCpows,
const IPC::Principal& aPrincipal,
const ClonedMessageData& aData) override;
virtual bool RecvAppOfflineStatus(const uint32_t& aId,
const bool& aOffline) override;
- virtual bool RecvSwappedWithOtherRemoteLoader() override;
+ virtual bool
+ RecvSwappedWithOtherRemoteLoader(const IPCTabContext& aContext) override;
virtual PDocAccessibleChild*
AllocPDocAccessibleChild(PDocAccessibleChild*, const uint64_t&) override;
virtual bool DeallocPDocAccessibleChild(PDocAccessibleChild*) override;
virtual PDocumentRendererChild*
AllocPDocumentRendererChild(const nsRect& aDocumentRect,
--- a/dom/ipc/TabContext.cpp
+++ b/dom/ipc/TabContext.cpp
@@ -154,16 +154,35 @@ TabContext::SetTabContext(const TabConte
NS_ENSURE_FALSE(mInitialized, false);
*this = aContext;
mInitialized = true;
return true;
}
+bool
+TabContext::UpdateTabContextAfterSwap(const TabContext& aContext)
+{
+ // This is only used after already initialized.
+ MOZ_ASSERT(mInitialized);
+
+ // The only permissable change is to `mIsMozBrowserElement`. All other fields
+ // must match for the change to be accepted.
+ if (aContext.OwnAppId() != OwnAppId() ||
+ aContext.mContainingAppId != mContainingAppId ||
+ aContext.mOriginAttributes != mOriginAttributes ||
+ aContext.mSignedPkgOriginNoSuffix != mSignedPkgOriginNoSuffix) {
+ return false;
+ }
+
+ mIsMozBrowserElement = aContext.mIsMozBrowserElement;
+ return true;
+}
+
const DocShellOriginAttributes&
TabContext::OriginAttributesRef() const
{
return mOriginAttributes;
}
const nsACString&
TabContext::SignedPkgOriginNoSuffix() const
--- a/dom/ipc/TabContext.h
+++ b/dom/ipc/TabContext.h
@@ -155,16 +155,27 @@ protected:
* - a non-browser, non-app frame. Both own app and owner app should be null.
*/
bool SetTabContext(bool aIsMozBrowserElement,
mozIApplication* aOwnApp,
mozIApplication* aAppFrameOwnerApp,
const DocShellOriginAttributes& aOriginAttributes,
const nsACString& aSignedPkgOriginNoSuffix);
+ /**
+ * Modify this TabContext to match the given TabContext. This is a special
+ * case triggered by nsFrameLoader::SwapWithOtherRemoteLoader which may have
+ * caused the owner content to change.
+ *
+ * This special case only allows the field `mIsMozBrowserElement` to be
+ * changed. If any other fields have changed, the update is ignored and
+ * returns false.
+ */
+ bool UpdateTabContextAfterSwap(const TabContext& aContext);
+
private:
/**
* Has this TabContext been initialized? If so, mutator methods will fail.
*/
bool mInitialized;
/**
* Whether this TabContext corresponds to a mozbrowser.