--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -1407,16 +1407,20 @@ var gBrowserInit = {
_delayedStartup() {
let { TelemetryTimestamps } =
ChromeUtils.import("resource://gre/modules/TelemetryTimestamps.jsm", {});
TelemetryTimestamps.add("delayedStartupStarted");
this._cancelDelayedStartup();
+ gBrowser.initialBrowser
+ .messageManager
+ .sendAsyncMessage("Browser:HasSiblings", false);
+
// We need to set the OfflineApps message listeners up before we
// load homepages, which might need them.
OfflineApps.init();
// This pageshow listener needs to be registered before we may call
// swapBrowsersAndCloseOther() to receive pageshow events fired by that.
window.messageManager.addMessageListener("PageVisibility:Show", function(message) {
if (message.target == gBrowser.selectedBrowser) {
--- a/browser/base/content/tab-content.js
+++ b/browser/base/content/tab-content.js
@@ -60,16 +60,24 @@ addMessageListener("Browser:HideSessionR
let doc = content.document;
let container;
if (doc.documentURI.toLowerCase() == "about:home" &&
(container = doc.getElementById("sessionRestoreContainer"))) {
container.hidden = true;
}
});
+if (Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT) {
+ addMessageListener("Browser:HasSiblings", function(message) {
+ let tabChild = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsITabChild);
+ let hasSiblings = message.data;
+ tabChild.hasSiblings = hasSiblings;
+ });
+}
// XXX(nika): Should we try to call this in the parent process instead?
addMessageListener("Browser:Reload", function(message) {
/* First, we'll try to use the session history object to reload so
* that framesets are handled properly. If we're in a special
* window (such as view-source) that has no session history, fall
* back on using the web navigation's reload method.
*/
--- a/browser/base/content/tabbrowser.js
+++ b/browser/base/content/tabbrowser.js
@@ -1703,16 +1703,20 @@ window._gBrowser = {
if (this.isFindBarInitialized(tab)) {
this.getCachedFindBar(tab).browser = aBrowser;
}
evt = document.createEvent("Events");
evt.initEvent("TabRemotenessChange", true, false);
tab.dispatchEvent(evt);
+ tab.linkedBrowser
+ .messageManager
+ .sendAsyncMessage("Browser:HasSiblings", this.tabs.length > 1);
+
return true;
},
updateBrowserRemotenessByURL(aBrowser, aURL, aOptions) {
aOptions = aOptions || {};
if (!gMultiProcessBrowser)
return this.updateBrowserRemoteness(aBrowser, false);
@@ -2441,17 +2445,39 @@ window._gBrowser = {
this._tabFilters.delete(t);
this._tabListeners.delete(t);
let notificationbox = this.getNotificationBox(t.linkedBrowser);
notificationbox.remove();
}
throw e;
}
- // Hack to ensure that the about:newtab, and about:welcome favicon is loaded
+ // Update TabChild hasSiblings properties
+ if (!aCreateLazyBrowser) {
+ // If we transitioned from one tab to two tabs, we need to set
+ // hasSiblings=false on both the existing tab and the new tab.
+ // Tell the original tab it now has siblings
+ if (this.tabs.length == 2) {
+ // Don't assume any ordering of the tabs array.
+ for (let tab of this.tabs) {
+ if (tab !== t) {
+ tab.linkedBrowser
+ .messageManager
+ .sendAsyncMessage("Browser:HasSiblings", true);
+ break;
+ }
+ }
+ }
+
+ t.linkedBrowser
+ .messageManager
+ .sendAsyncMessage("Browser:HasSiblings", this.tabs.length > 1);
+ }
+
+ // Hack to ensure that the about:newtab favicon is loaded
// instantaneously, to avoid flickering and improve perceived performance.
if (aURI == "about:newtab" || aURI == "about:home" || aURI == "about:welcome") {
this.setIcon(t, "chrome://branding/content/icon32.png");
} else if (aURI == "about:privatebrowsing") {
this.setIcon(t, "chrome://browser/skin/privatebrowsing/favicon.svg");
}
// Dispatch a new tab notification. We do this once we're
@@ -2843,16 +2869,23 @@ window._gBrowser = {
// We're committed to closing the tab now.
// Dispatch a notification.
// We dispatch it before any teardown so that event listeners can
// inspect the tab that's about to close.
var evt = new CustomEvent("TabClose", { bubbles: true, detail: { adoptedBy: aAdoptedByTab } });
aTab.dispatchEvent(evt);
+ if (this.tabs.length == 2) {
+ // We're closing one of our two open tabs, inform the other tab that its
+ // sibling is going away.
+ window.messageManager
+ .broadcastAsyncMessage("Browser:HasSiblings", false);
+ }
+
if (aTab.linkedPanel) {
if (!aAdoptedByTab && !gMultiProcessBrowser) {
// Prevent this tab from showing further dialogs, since we're closing it
var windowUtils = browser.contentWindow.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils);
windowUtils.disableDialogs();
}
--- a/dom/base/nsGlobalWindowOuter.cpp
+++ b/dom/base/nsGlobalWindowOuter.cpp
@@ -4507,17 +4507,17 @@ nsGlobalWindowOuter::CanMoveResizeWindow
if (XRE_IsContentProcess()) {
nsCOMPtr<nsIDocShell> docShell = GetDocShell();
if (docShell) {
nsCOMPtr<nsITabChild> child = docShell->GetTabChild();
bool hasSiblings = true;
if (child &&
NS_SUCCEEDED(child->GetHasSiblings(&hasSiblings)) &&
hasSiblings) {
- child->SendGetTabCount(&itemCount);
+ return false;
}
}
} else {
nsCOMPtr<nsIDocShellTreeOwner> treeOwner = GetTreeOwner();
uint32_t itemCount = 0;
if (treeOwner && NS_SUCCEEDED(treeOwner->GetTabCount(&itemCount)) &&
itemCount > 1) {
return false;
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -969,16 +969,17 @@ ContentChild::ProvideWindowCommon(TabChi
*aWindowIsNew = info.windowOpened();
nsTArray<FrameScriptInfo> frameScripts(info.frameScripts());
nsCString urlToLoad = info.urlToLoad();
TextureFactoryIdentifier textureFactoryIdentifier = info.textureFactoryIdentifier();
layers::LayersId layersId = info.layersId();
CompositorOptions compositorOptions = info.compositorOptions();
uint32_t maxTouchPoints = info.maxTouchPoints();
DimensionInfo dimensionInfo = info.dimensions();
+ bool hasSiblings = info.hasSiblings();
// Once this function exits, we should try to exit the nested event loop.
ready = true;
// NOTE: We have to handle this immediately in the resolve callback in order
// to make sure that we don't process any more IPC messages before returning
// from ProvideWindowCommon.
@@ -1011,16 +1012,17 @@ ContentChild::ProvideWindowCommon(TabChi
showInfo = ShowInfo(EmptyString(), false,
context->UsePrivateBrowsing(), true, false,
aTabOpener->WebWidget()->GetDPI(),
aTabOpener->WebWidget()->RoundsWidgetCoordinatesTo(),
aTabOpener->WebWidget()->GetDefaultScale().scale);
}
newChild->SetMaxTouchPoints(maxTouchPoints);
+ newChild->SetHasSiblings(hasSiblings);
// Set the opener window for this window before we start loading the document
// inside of it. We have to do this before loading the remote scripts, because
// they can poke at the document and cause the nsDocument to be created before
// the openerwindow
nsCOMPtr<mozIDOMWindowProxy> windowProxy = do_GetInterface(newChild->WebNavigation());
if (!aForceNoOpener && windowProxy && aParent) {
nsPIDOMWindowOuter* outer = nsPIDOMWindowOuter::From(windowProxy);
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -4659,16 +4659,17 @@ ContentParent::CommonCreateWindow(PBrows
const nsCString& aFeatures,
const nsCString& aBaseURI,
const float& aFullZoom,
uint64_t aNextTabParentId,
const nsString& aName,
nsresult& aResult,
nsCOMPtr<nsITabParent>& aNewTabParent,
bool* aWindowIsNew,
+ int32_t& aOpenLocation,
nsIPrincipal* aTriggeringPrincipal,
uint32_t aReferrerPolicy,
bool aLoadURI)
{
// The content process should never be in charge of computing whether or
// not a window should be private or remote - the parent will do that.
const uint32_t badFlags = nsIWebBrowserChrome::CHROME_PRIVATE_WINDOW |
@@ -4715,32 +4716,32 @@ ContentParent::CommonCreateWindow(PBrows
}
nsCOMPtr<nsIDOMChromeWindow> rootChromeWin = do_QueryInterface(outerWin);
if (rootChromeWin) {
rootChromeWin->GetBrowserDOMWindow(getter_AddRefs(browserDOMWin));
}
}
- int32_t openLocation = nsWindowWatcher::GetWindowOpenLocation(
+ aOpenLocation = nsWindowWatcher::GetWindowOpenLocation(
outerWin, aChromeFlags, aCalledFromJS, aPositionSpecified, aSizeSpecified);
- MOZ_ASSERT(openLocation == nsIBrowserDOMWindow::OPEN_NEWTAB ||
- openLocation == nsIBrowserDOMWindow::OPEN_NEWWINDOW);
+ MOZ_ASSERT(aOpenLocation == nsIBrowserDOMWindow::OPEN_NEWTAB ||
+ aOpenLocation == nsIBrowserDOMWindow::OPEN_NEWWINDOW);
// Read the origin attributes for the tab from the opener tabParent.
OriginAttributes openerOriginAttributes;
if (thisTabParent) {
nsCOMPtr<nsILoadContext> loadContext = thisTabParent->GetLoadContext();
loadContext->GetOriginAttributes(openerOriginAttributes);
} else if (Preferences::GetBool("browser.privatebrowsing.autostart")) {
openerOriginAttributes.mPrivateBrowsingId = 1;
}
- if (openLocation == nsIBrowserDOMWindow::OPEN_NEWTAB) {
+ if (aOpenLocation == nsIBrowserDOMWindow::OPEN_NEWTAB) {
if (NS_WARN_IF(!browserDOMWin)) {
aResult = NS_ERROR_ABORT;
return IPC_OK();
}
nsCOMPtr<nsIFrameLoaderOwner> opener = do_QueryInterface(frame);
nsCOMPtr<nsIOpenURIInFrameParams> params =
@@ -4748,23 +4749,23 @@ ContentParent::CommonCreateWindow(PBrows
params->SetReferrer(NS_ConvertUTF8toUTF16(aBaseURI));
MOZ_ASSERT(aTriggeringPrincipal, "need a valid triggeringPrincipal");
params->SetTriggeringPrincipal(aTriggeringPrincipal);
params->SetReferrerPolicy(aReferrerPolicy);
nsCOMPtr<nsIFrameLoaderOwner> frameLoaderOwner;
if (aLoadURI) {
aResult = browserDOMWin->OpenURIInFrame(aURIToLoad,
- params, openLocation,
+ params, aOpenLocation,
nsIBrowserDOMWindow::OPEN_NEW,
aNextTabParentId, aName,
getter_AddRefs(frameLoaderOwner));
} else {
aResult = browserDOMWin->CreateContentWindowInFrame(aURIToLoad,
- params, openLocation,
+ params, aOpenLocation,
nsIBrowserDOMWindow::OPEN_NEW,
aNextTabParentId, aName,
getter_AddRefs(frameLoaderOwner));
}
if (NS_SUCCEEDED(aResult) && frameLoaderOwner) {
RefPtr<nsFrameLoader> frameLoader = frameLoaderOwner->GetFrameLoader();
if (frameLoader) {
aNewTabParent = frameLoader->GetTabParent();
@@ -4773,23 +4774,23 @@ ContentParent::CommonCreateWindow(PBrows
// frameloader to display will be correct (instead of falling back to a 10x10
// default), we force layout if necessary to get the most up-to-date dimensions.
// See bug 1358712 for details.
frameLoader->ForceLayoutIfNecessary();
}
} else if (NS_SUCCEEDED(aResult) && !frameLoaderOwner) {
// Fall through to the normal window opening code path when there is no
// window which we can open a new tab in.
- openLocation = nsIBrowserDOMWindow::OPEN_NEWWINDOW;
+ aOpenLocation = nsIBrowserDOMWindow::OPEN_NEWWINDOW;
} else {
*aWindowIsNew = false;
}
// If we didn't retarget our window open into a new window, we should return now.
- if (openLocation != nsIBrowserDOMWindow::OPEN_NEWWINDOW) {
+ if (aOpenLocation != nsIBrowserDOMWindow::OPEN_NEWWINDOW) {
return IPC_OK();
}
}
nsCOMPtr<nsPIWindowWatcher> pwwatch =
do_GetService(NS_WINDOWWATCHER_CONTRACTID, &aResult);
if (NS_WARN_IF(NS_FAILED(aResult))) {
return IPC_OK();
@@ -4881,16 +4882,17 @@ ContentParent::RecvCreateWindow(PBrowser
{
nsresult rv = NS_OK;
CreatedWindowInfo cwi;
// We always expect to open a new window here. If we don't, it's an error.
cwi.windowOpened() = true;
cwi.layersId() = LayersId{0};
cwi.maxTouchPoints() = 0;
+ cwi.hasSiblings() = false;
// Make sure to resolve the resolver when this function exits, even if we
// failed to generate a valid response.
auto resolveOnExit = MakeScopeExit([&] {
// Copy over the nsresult, and then resolve.
cwi.rv() = rv;
aResolve(cwi);
});
@@ -4913,22 +4915,23 @@ ContentParent::RecvCreateWindow(PBrowser
TabParent::AutoUseNewTab aunt(newTab, &cwi.urlToLoad());
const uint64_t nextTabParentId = ++sNextTabParentId;
sNextTabParents.Put(nextTabParentId, newTab);
const nsCOMPtr<nsIURI> uriToLoad = DeserializeURI(aURIToLoad);
nsCOMPtr<nsITabParent> newRemoteTab;
+ int32_t openLocation = nsIBrowserDOMWindow::OPEN_NEWWINDOW;
mozilla::ipc::IPCResult ipcResult =
CommonCreateWindow(aThisTab, /* aSetOpener = */ true, aChromeFlags,
aCalledFromJS, aPositionSpecified, aSizeSpecified,
uriToLoad, aFeatures, aBaseURI, aFullZoom,
nextTabParentId, VoidString(), rv,
- newRemoteTab, &cwi.windowOpened(),
+ newRemoteTab, &cwi.windowOpened(), openLocation,
aTriggeringPrincipal, aReferrerPolicy,
/* aLoadUri = */ false);
if (!ipcResult) {
return ipcResult;
}
if (NS_WARN_IF(NS_FAILED(rv)) || !newRemoteTab) {
return IPC_OK();
@@ -4949,16 +4952,18 @@ ContentParent::RecvCreateWindow(PBrowser
cwi.compositorOptions() = rfp->GetCompositorOptions();
nsCOMPtr<nsIWidget> widget = newTab->GetWidget();
if (widget) {
cwi.maxTouchPoints() = widget->GetMaxTouchPoints();
cwi.dimensions() = newTab->GetDimensionInfo();
}
+ cwi.hasSiblings() = (openLocation == nsIBrowserDOMWindow::OPEN_NEWTAB);
+
return IPC_OK();
}
mozilla::ipc::IPCResult
ContentParent::RecvCreateWindowInDifferentProcess(
PBrowserParent* aThisTab,
const uint32_t& aChromeFlags,
const bool& aCalledFromJS,
@@ -4970,24 +4975,26 @@ ContentParent::RecvCreateWindowInDiffere
const float& aFullZoom,
const nsString& aName,
const IPC::Principal& aTriggeringPrincipal,
const uint32_t& aReferrerPolicy)
{
nsCOMPtr<nsITabParent> newRemoteTab;
bool windowIsNew;
nsCOMPtr<nsIURI> uriToLoad = DeserializeURI(aURIToLoad);
+ int32_t openLocation = nsIBrowserDOMWindow::OPEN_NEWWINDOW;
nsresult rv;
mozilla::ipc::IPCResult ipcResult =
CommonCreateWindow(aThisTab, /* aSetOpener = */ false, aChromeFlags,
aCalledFromJS, aPositionSpecified, aSizeSpecified,
uriToLoad, aFeatures, aBaseURI, aFullZoom,
/* aNextTabParentId = */ 0, aName, rv,
- newRemoteTab, &windowIsNew, aTriggeringPrincipal,
- aReferrerPolicy, /* aLoadUri = */ true);
+ newRemoteTab, &windowIsNew, openLocation,
+ aTriggeringPrincipal, aReferrerPolicy,
+ /* aLoadUri = */ true);
if (!ipcResult) {
return ipcResult;
}
if (NS_FAILED(rv)) {
NS_WARNING("Call to CommonCreateWindow failed.");
}
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -722,16 +722,17 @@ private:
const nsCString& aFeatures,
const nsCString& aBaseURI,
const float& aFullZoom,
uint64_t aNextTabParentId,
const nsString& aName,
nsresult& aResult,
nsCOMPtr<nsITabParent>& aNewTabParent,
bool* aWindowIsNew,
+ int32_t& aOpenLocation,
nsIPrincipal* aTriggeringPrincipal,
uint32_t aReferrerPolicy,
bool aLoadUri);
FORWARD_SHMEM_ALLOCATOR_TO(PContentParent)
explicit ContentParent(int32_t aPluginID)
: ContentParent(nullptr, EmptyString(), aPluginID)
--- a/dom/ipc/DOMTypes.ipdlh
+++ b/dom/ipc/DOMTypes.ipdlh
@@ -120,16 +120,17 @@ struct CreatedWindowInfo
bool windowOpened;
FrameScriptInfo[] frameScripts;
nsCString urlToLoad;
TextureFactoryIdentifier textureFactoryIdentifier;
LayersId layersId;
CompositorOptions compositorOptions;
uint32_t maxTouchPoints;
DimensionInfo dimensions;
+ bool hasSiblings;
};
/**
* PerformanceInfo is used to pass performance info stored
* in WorkerPrivate & DocGroup instances
*
* Each (host, pid, wid, pwid) is unique to a given DocGroup or
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -408,17 +408,17 @@ TabChild::TabChild(nsIContentChild* aMan
, mDidFakeShow(false)
, mNotified(false)
, mTriedBrowserInit(false)
, mOrientation(eScreenOrientation_PortraitPrimary)
, mIgnoreKeyPressEvent(false)
, mHasValidInnerSize(false)
, mDestroyed(false)
, mUniqueId(aTabId)
- , mHasSiblings(true)
+ , mHasSiblings(false)
, mIsTransparent(false)
, mIPCOpen(false)
, mParentIsActive(false)
, mDidSetRealShowInfo(false)
, mDidLoadURLInit(false)
, mAwaitingLA(false)
, mSkipKeyPress(false)
, mLayerObserverEpoch(1)
@@ -3481,16 +3481,30 @@ TabChild::BeforeUnloadRemoved()
}
mozilla::dom::TabGroup*
TabChild::TabGroup()
{
return mTabGroup;
}
+nsresult
+TabChild::GetHasSiblings(bool* aHasSiblings)
+{
+ *aHasSiblings = mHasSiblings;
+ return NS_OK;
+}
+
+nsresult
+TabChild::SetHasSiblings(bool aHasSiblings)
+{
+ mHasSiblings = aHasSiblings;
+ return NS_OK;
+}
+
TabChildGlobal::TabChildGlobal(TabChild* aTabChild)
: ContentFrameMessageManager(new nsFrameMessageManager(aTabChild)),
mTabChild(aTabChild)
{
}
TabChildGlobal::~TabChildGlobal()
{
--- a/dom/tests/mochitest/general/test_resizeby.html
+++ b/dom/tests/mochitest/general/test_resizeby.html
@@ -24,18 +24,19 @@
await SimpleTest.promiseFocus();
let clicky = document.querySelector("#clicky");
let popupPromise = new Promise(resolve => {
let linkclick = () => {
clicky.removeEventListener('click', linkclick);
let popup = window.open("about:blank", "_blank", "width=500,height=500");
- is(popup.innerHeight, 500);
- is(popup.innerWidth, 500);
+ is(popup.innerHeight, 500, "starting width is 500");
+ is(popup.innerWidth, 500, "starting height in 500");
+
popup.resizeBy(50, 0);
// We resized synchronously. If this happened, we sometimes won't fire
// an resize event, so we resolve immediately.
if (popup.innerWidth == 550) {
resolve(popup);
} else {
let popupresize = () => {
@@ -47,15 +48,15 @@
};
clicky.addEventListener('click', linkclick);
});
synthesizeMouseAtCenter(clicky, {}, window);
let popup = await popupPromise;
- is(popup.innerHeight, 500);
- is(popup.innerWidth, 550);
+ is(popup.innerHeight, 500, "ending height is 500");
+ is(popup.innerWidth, 550, "ending width is 550");
popup.close();
});
</script>
</body>
</html>