diff --git a/dom/interfaces/base/nsITabChild.idl b/dom/interfaces/base/nsITabChild.idl index fc1e5e608fd6..b89a32565fd6 100644 --- a/dom/interfaces/base/nsITabChild.idl +++ b/dom/interfaces/base/nsITabChild.idl @@ -25,6 +25,9 @@ interface nsITabChild : nsISupports in CommandsArrayRef enabledCommands, in CommandsArrayRef disabledCommands); + [noscript] void remoteSizeShellTo(in int32_t width, in int32_t height, + in int32_t shellItemWidth, in int32_t shellItemHeight); + readonly attribute uint64_t tabId; }; diff --git a/dom/ipc/PBrowser.ipdl b/dom/ipc/PBrowser.ipdl index 083d6df99a07..f9326d761ae5 100644 --- a/dom/ipc/PBrowser.ipdl +++ b/dom/ipc/PBrowser.ipdl @@ -159,6 +159,21 @@ parent: */ async MoveFocus(bool forward, bool forDocumentNavigation); + /** + * SizeShellTo request propagation to parent. + * + * aFlag Can indicate if one of the dimensions should be ignored. + * If only one dimension has changed it has to be indicated + * by the nsIEmbeddingSiteWindow::DIM_FLAGS_IGNORE_* flags. + * aShellItemWidth, + * aShellItemHeight On parent side we won't be able to decide the dimensions + * of the shell item parameter in the original SizeShellTo + * call so we send over its dimensions that will be used + * for the actual resize. + **/ + async SizeShellTo(uint32_t aFlag, int32_t aWidth, int32_t aHeight, + int32_t aShellItemWidth, int32_t aShellItemHeight); + async Event(RemoteDOMEvent aEvent); sync SyncMessage(nsString aMessage, ClonedMessageData aData, diff --git a/dom/ipc/TabChild.cpp b/dom/ipc/TabChild.cpp index 36317b7b5cd9..81bbffcc8154 100644 --- a/dom/ipc/TabChild.cpp +++ b/dom/ipc/TabChild.cpp @@ -52,6 +52,7 @@ #include "nsContentUtils.h" #include "nsDocShell.h" #include "nsEmbedCID.h" +#include "nsGlobalWindow.h" #include #ifdef MOZ_CRASHREPORTER #include "nsExceptionHandler.h" @@ -943,7 +944,30 @@ TabChild::DestroyBrowserWindow() } NS_IMETHODIMP -TabChild::SizeBrowserTo(int32_t aCX, int32_t aCY) +TabChild::RemoteSizeShellTo(int32_t aWidth, int32_t aHeight, + int32_t aShellItemWidth, int32_t aShellItemHeight) +{ + nsCOMPtr ourDocShell = do_GetInterface(WebNavigation()); + nsCOMPtr docShellAsWin(do_QueryInterface(ourDocShell)); + int32_t width, height; + docShellAsWin->GetSize(&width, &height); + + uint32_t flags = 0; + if (width == aWidth) { + flags |= nsIEmbeddingSiteWindow::DIM_FLAGS_IGNORE_CX; + } + + if (height == aHeight) { + flags |= nsIEmbeddingSiteWindow::DIM_FLAGS_IGNORE_CY; + } + + bool sent = SendSizeShellTo(flags, aWidth, aHeight, aShellItemWidth, aShellItemHeight); + + return sent ? NS_OK : NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +TabChild::SizeBrowserTo(int32_t aWidth, int32_t aHeight) { NS_WARNING("TabChild::SizeBrowserTo not supported in TabChild"); @@ -987,9 +1011,37 @@ TabChild::SetStatusWithContext(uint32_t aStatusType, NS_IMETHODIMP TabChild::SetDimensions(uint32_t aFlags, int32_t aX, int32_t aY, - int32_t aCx, int32_t aCy) + int32_t aCx, int32_t aCy) { - Unused << PBrowserChild::SendSetDimensions(aFlags, aX, aY, aCx, aCy); + // The parent is in charge of the dimension changes. If JS code wants to + // change the dimensions (moveTo, screenX, etc.) we send a message to the + // parent about the new requested dimension, the parent does the resize/move + // then send a message to the child to update itself. For APIs like screenX + // this function is called with the current value for the non-changed values. + // In a series of calls like window.screenX = 10; window.screenY = 10; for + // the second call, since screenX is not yet updated we might accidentally + // reset back screenX to it's old value. To avoid this if a parameter did not + // change we want the parent to ignore its value. + int32_t x, y, cx, cy; + GetDimensions(aFlags, &x, &y, &cx, &cy); + + if (x == aX) { + aFlags |= nsIEmbeddingSiteWindow::DIM_FLAGS_IGNORE_X; + } + + if (y == aY) { + aFlags |= nsIEmbeddingSiteWindow::DIM_FLAGS_IGNORE_Y; + } + + if (cx == aCx) { + aFlags |= nsIEmbeddingSiteWindow::DIM_FLAGS_IGNORE_CX; + } + + if (cy == aCy) { + aFlags |= nsIEmbeddingSiteWindow::DIM_FLAGS_IGNORE_CY; + } + + Unused << SendSetDimensions(aFlags, aX, aY, aCx, aCy); return NS_OK; } diff --git a/dom/ipc/TabParent.cpp b/dom/ipc/TabParent.cpp index 1f7c7d315497..477e86207315 100644 --- a/dom/ipc/TabParent.cpp +++ b/dom/ipc/TabParent.cpp @@ -619,6 +619,37 @@ TabParent::RecvMoveFocus(const bool& aForward, const bool& aForDocumentNavigatio return true; } +bool +TabParent::RecvSizeShellTo(const uint32_t& aFlags, const int32_t& aWidth, const int32_t& aHeight, + const int32_t& aShellItemWidth, const int32_t& aShellItemHeight) +{ + NS_ENSURE_TRUE(mFrameElement, true); + + nsCOMPtr docShell = mFrameElement->OwnerDoc()->GetDocShell(); + NS_ENSURE_TRUE(docShell, true); + + nsCOMPtr treeOwner; + nsresult rv = docShell->GetTreeOwner(getter_AddRefs(treeOwner)); + NS_ENSURE_SUCCESS(rv, true); + + int32_t width = aWidth; + int32_t height = aHeight; + + if (aFlags & nsIEmbeddingSiteWindow::DIM_FLAGS_IGNORE_CX) { + width = mDimensions.width; + } + + if (aFlags & nsIEmbeddingSiteWindow::DIM_FLAGS_IGNORE_CY) { + height = mDimensions.height; + } + + nsCOMPtr xulWin(do_GetInterface(treeOwner)); + NS_ENSURE_TRUE(xulWin, true); + xulWin->SizeShellToWithLimit(width, height, aShellItemWidth, aShellItemHeight); + + return true; +} + bool TabParent::RecvEvent(const RemoteDOMEvent& aEvent) { @@ -814,19 +845,44 @@ TabParent::RecvSetDimensions(const uint32_t& aFlags, nsCOMPtr treeOwnerAsWin = do_QueryInterface(treeOwner); NS_ENSURE_TRUE(treeOwnerAsWin, true); + // We only care about the parameters that actually changed, see more + // details in TabChild::SetDimensions. + int32_t unused; + int32_t x = aX; + if (aFlags & nsIEmbeddingSiteWindow::DIM_FLAGS_IGNORE_X) { + treeOwnerAsWin->GetPosition(&x, &unused); + } + + int32_t y = aY; + if (aFlags & nsIEmbeddingSiteWindow::DIM_FLAGS_IGNORE_Y) { + treeOwnerAsWin->GetPosition(&unused, &y); + } + + int32_t cx = aCx; + if (aFlags & nsIEmbeddingSiteWindow::DIM_FLAGS_IGNORE_CX) { + treeOwnerAsWin->GetSize(&cx, &unused); + } + + int32_t cy = aCy; + if (aFlags & nsIEmbeddingSiteWindow::DIM_FLAGS_IGNORE_CY) { + treeOwnerAsWin->GetSize(&unused, &cy); + } + if (aFlags & nsIEmbeddingSiteWindow::DIM_FLAGS_POSITION && aFlags & nsIEmbeddingSiteWindow::DIM_FLAGS_SIZE_OUTER) { - treeOwnerAsWin->SetPositionAndSize(aX, aY, aCx, aCy, true); + treeOwnerAsWin->SetPositionAndSize(x, y, cx, cy, true); return true; } if (aFlags & nsIEmbeddingSiteWindow::DIM_FLAGS_POSITION) { - treeOwnerAsWin->SetPosition(aX, aY); + treeOwnerAsWin->SetPosition(x, y); + mUpdatedDimensions = false; + UpdatePosition(); return true; } if (aFlags & nsIEmbeddingSiteWindow::DIM_FLAGS_SIZE_OUTER) { - treeOwnerAsWin->SetSize(aCx, aCy, true); + treeOwnerAsWin->SetSize(cx, cy, true); return true; } diff --git a/dom/ipc/TabParent.h b/dom/ipc/TabParent.h index cbc8e0571373..e5d7f6749f69 100644 --- a/dom/ipc/TabParent.h +++ b/dom/ipc/TabParent.h @@ -161,6 +161,12 @@ public: virtual bool RecvMoveFocus(const bool& aForward, const bool& aForDocumentNavigation) override; + virtual bool RecvSizeShellTo(const uint32_t& aFlags, + const int32_t& aWidth, + const int32_t& aHeight, + const int32_t& aShellItemWidth, + const int32_t& aShellItemHeight) override; + virtual bool RecvEvent(const RemoteDOMEvent& aEvent) override; virtual bool RecvReplyKeyEvent(const WidgetKeyboardEvent& aEvent) override; diff --git a/dom/tests/mochitest/bugs/mochitest.ini b/dom/tests/mochitest/bugs/mochitest.ini index c033a9876525..a44a96260bf1 100644 --- a/dom/tests/mochitest/bugs/mochitest.ini +++ b/dom/tests/mochitest/bugs/mochitest.ini @@ -160,9 +160,9 @@ skip-if = toolkit == 'android' #bug 775227 [test_onerror_message.html] [test_protochains.html] [test_resize_move_windows.html] -skip-if = buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android' || e10s #Windows can't change size and position on Android # b2g(Windows can't change size and position on B2G) b2g-debug(Windows can't change size and position on B2G) b2g-desktop(Windows can't change size and position on B2G) +skip-if = buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android' || os == 'linux' #Windows can't change size and position on Android # b2g(Windows can't change size and position on B2G) b2g-debug(Windows can't change size and position on B2G) b2g-desktop(Windows can't change size and position on B2G) [test_sizetocontent_clamp.html] -skip-if = buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android' || e10s #Windows can't change size on Android # b2g(Windows can't change size on B2G) b2g-debug(Windows can't change size on B2G) b2g-desktop(Windows can't change size on B2G) +skip-if = buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android' #Windows can't change size on Android # b2g(Windows can't change size on B2G) b2g-debug(Windows can't change size on B2G) b2g-desktop(Windows can't change size on B2G) [test_toJSON.html] [test_window_bar.html] skip-if = buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android' diff --git a/dom/tests/mochitest/bugs/test_resize_move_windows.html b/dom/tests/mochitest/bugs/test_resize_move_windows.html index 3da53781c762..0762e923139b 100644 --- a/dom/tests/mochitest/bugs/test_resize_move_windows.html +++ b/dom/tests/mochitest/bugs/test_resize_move_windows.html @@ -237,9 +237,6 @@ function checkChangeIsEnabled(aWindow, aNext) function outerChangeTest() { isnot(aWindow.outerWidth, oWidth, "Window outerWidth should have changed"); isnot(aWindow.outerHeight, oHeight, "Window outerHeight should have changed"); - - aWindow.outerWidth = oWidth; - aWindow.outerHeight = oHeight; } /** @@ -247,31 +244,22 @@ function checkChangeIsEnabled(aWindow, aNext) */ prevWidth = aWindow.innerWidth; prevHeight = aWindow.innerHeight; - aWindow.innerWidth = getNewWidth(aWindow); aWindow.innerHeight = getNewHeight(aWindow); hitEventLoop(sizeChangeCondition, sizeChangeTest, hits) .then(function() { aWindow.resizeTo(getNewWidth(aWindow), getNewHeight(aWindow)); - }) - .then(function() { return hitEventLoop(sizeChangeCondition, sizeChangeTest, hits); }) .then(function () { aWindow.resizeBy(getNewWidth(aWindow) - aWindow.innerWidth, getNewHeight(aWindow) - aWindow.innerHeight); - }) - .then(function() { return hitEventLoop(sizeChangeCondition, sizeChangeTest, hits); }) - .then(function () { - prevWidth = aWindow.innerWidth = getNewWidth(aWindow); - prevHeight = aWindow.innerHeight = getNewHeight(aWindow); - aWindow.sizeToContent(); - }) .then(function() { - hitEventLoop(sizeChangeCondition, sizeChangeTest, hits); + aWindow.sizeToContent(); + return hitEventLoop(sizeChangeCondition, sizeChangeTest, hits); }) .then(function() { /** @@ -282,8 +270,6 @@ function checkChangeIsEnabled(aWindow, aNext) aWindow.screenX = getNewX(aWindow); aWindow.screenY = getNewY(aWindow); - }) - .then(function() { return hitEventLoop(posChangeCondition, posChangeTest, hits); }) .then(function() { @@ -291,8 +277,6 @@ function checkChangeIsEnabled(aWindow, aNext) prevY = aWindow.screenY; aWindow.moveTo(getNewX(aWindow), getNewY(aWindow)); - }) - .then(function() { return hitEventLoop(posChangeCondition, posChangeTest, hits); }) .then(function() { @@ -301,8 +285,6 @@ function checkChangeIsEnabled(aWindow, aNext) aWindow.moveBy(getNewX(aWindow) - aWindow.screenX, getNewY(aWindow) - aWindow.screenY); - }) - .then(function() { return hitEventLoop(posChangeCondition, posChangeTest, hits); }) .then(function() { @@ -314,8 +296,17 @@ function checkChangeIsEnabled(aWindow, aNext) aWindow.outerWidth = oWidth * 2; aWindow.outerHeight = oHeight * 2; + return hitEventLoop(outerChangeCondition, outerChangeTest, hits); }) .then(function() { + let origWidth = oWidth; + let origHeight = oHeight; + + oWidth = aWindow.outerWidth; + oHeight = aWindow.outerHeight; + + aWindow.outerWidth = origWidth; + aWindow.outerHeight = origHeight; return hitEventLoop(outerChangeCondition, outerChangeTest, hits); }) .then(aNext); diff --git a/dom/tests/mochitest/bugs/test_sizetocontent_clamp.html b/dom/tests/mochitest/bugs/test_sizetocontent_clamp.html index 95f1eff6604a..90767e6fb979 100644 --- a/dom/tests/mochitest/bugs/test_sizetocontent_clamp.html +++ b/dom/tests/mochitest/bugs/test_sizetocontent_clamp.html @@ -35,25 +35,24 @@ var isWin8 = (navigator.userAgent.indexOf("Windows NT 6.2") != -1); var innerWidthMin = (isWin8 ? 120 : 100); var innerWidthMax = (isWin8 ? 125 : 100); +var isExecuted = false; + function test() { var w = window.open('data:text/html,null', null, 'width=300,height=300'); - var nbResize = 0; SimpleTest.waitForFocus(function() { w.onresize = function() { - nbResize++; - if (nbResize == 1) { + if (w.innerWidth > 300 - epsilon || isExecuted) { return; } - ok(w.innerWidth + epsilon >= innerWidthMin && w.innerWidth - epsilon <= innerWidthMax, - "innerWidth should be between " + innerWidthMin + " and " + innerWidthMax); - ok(w.innerHeight + epsilon >= 100 && w.innerHeight - epsilon <= 100, - "innerHeight should be around 100"); + isExecuted = true; - // It's not clear why 2 events are coming... - is(nbResize, 2, "We should get 2 events."); + ok(w.innerWidth + epsilon >= innerWidthMin && w.innerWidth - epsilon <= innerWidthMax, + "innerWidth should be between " + innerWidthMin + " and " + innerWidthMax + " but it was: " + w.innerWidth); + ok(w.innerHeight + epsilon >= 100 && w.innerHeight - epsilon <= 100, + "innerHeight should be around 100" + " but it was: " + w.innerHeight); w.close(); diff --git a/embedding/browser/nsDocShellTreeOwner.cpp b/embedding/browser/nsDocShellTreeOwner.cpp index a410e4d6a930..cc029db0f6d9 100644 --- a/embedding/browser/nsDocShellTreeOwner.cpp +++ b/embedding/browser/nsDocShellTreeOwner.cpp @@ -57,6 +57,7 @@ #include "nsPIWindowWatcher.h" #include "nsIPrompt.h" #include "nsITabParent.h" +#include "nsITabChild.h" #include "nsRect.h" #include "nsIWebBrowserChromeFocus.h" #include "nsIContent.h" @@ -455,6 +456,19 @@ nsDocShellTreeOwner::SizeShellTo(nsIDocShellTreeItem* aShellItem, } if (aShellItem == mWebBrowser->mDocShell) { + nsCOMPtr tabChild = do_QueryInterface(webBrowserChrome); + if (tabChild) { + // The XUL window to resize is in the parent process, but there we + // won't be able to get aShellItem to do the hack in nsXULWindow::SizeShellTo, + // so let's send the width and height of aShellItem too. + nsCOMPtr shellAsWin(do_QueryInterface(aShellItem)); + NS_ENSURE_TRUE(shellAsWin, NS_ERROR_FAILURE); + + int32_t width = 0; + int32_t height = 0; + shellAsWin->GetSize(&width, &height); + return tabChild->RemoteSizeShellTo(aCX, aCY, width, height); + } return webBrowserChrome->SizeBrowserTo(aCX, aCY); } @@ -578,7 +592,8 @@ NS_IMETHODIMP nsDocShellTreeOwner::SetPositionDesktopPix(int32_t aX, int32_t aY) { if (mWebBrowser) { - return mWebBrowser->SetPositionDesktopPix(aX, aY); + nsresult rv = mWebBrowser->SetPositionDesktopPix(aX, aY); + NS_ENSURE_SUCCESS(rv, rv); } double scale = 1.0; diff --git a/embedding/browser/nsIEmbeddingSiteWindow.idl b/embedding/browser/nsIEmbeddingSiteWindow.idl index f1365f206e3e..a8eefacb429d 100644 --- a/embedding/browser/nsIEmbeddingSiteWindow.idl +++ b/embedding/browser/nsIEmbeddingSiteWindow.idl @@ -46,6 +46,35 @@ interface nsIEmbeddingSiteWindow : nsISupports */ const unsigned long DIM_FLAGS_SIZE_OUTER = 4; + /** + * Flag indicates that the x parameter should be ignored. + * + * @see setDimensions + */ + const unsigned long DIM_FLAGS_IGNORE_X = 8; + + /** + * Flag indicates that the y parameter should be ignored. + * + * @see setDimensions + */ + const unsigned long DIM_FLAGS_IGNORE_Y = 16; + + /** + * Flag indicates that the cx parameter should be ignored. + * + * @see setDimensions + */ + const unsigned long DIM_FLAGS_IGNORE_CX = 32; + + /** + * Flag indicates that the cy parameter should be ignored. + * + * @see setDimensions + */ + const unsigned long DIM_FLAGS_IGNORE_CY = 64; + + /** * Sets the dimensions for the window; the position & size. The * flags to indicate what the caller wants to set and whether the size @@ -54,6 +83,9 @@ interface nsIEmbeddingSiteWindow : nsISupports * surrounding chrome, window frame, title bar, and so on. * * @param flags Combination of position, inner and outer size flags. + * The ignore flags are telling the parent to use the + * current values for those dimensions and ignore the + * corresponding parameters the child sends. * @param x Left hand corner of the outer area. * @param y Top corner of the outer area. * @param cx Width of the inner or outer area. diff --git a/xpfe/appshell/nsIXULWindow.idl b/xpfe/appshell/nsIXULWindow.idl index 8844caff113f..1753c39dc45c 100644 --- a/xpfe/appshell/nsIXULWindow.idl +++ b/xpfe/appshell/nsIXULWindow.idl @@ -134,4 +134,13 @@ interface nsIXULWindow : nsISupports * docshell could cause problems. */ [noscript] void applyChromeFlags(); + + + /** + * Sometimes the child's nsDocShellTreeOwner needs to propogate a SizeShellTo call to the parent. But the + * shellItem argument of the call will not be available on the parent side, so we pass its dimensions here. + * Note: this is an internal method, other consumers should never call this. + */ + [noscript, notxpcom] void sizeShellToWithLimit(in int32_t aCx, in int32_t aCy, + in int32_t shellItemCx, in int32_t shellItemCy); }; diff --git a/xpfe/appshell/nsXULWindow.cpp b/xpfe/appshell/nsXULWindow.cpp index b91e398e2f42..ea23e6124f97 100644 --- a/xpfe/appshell/nsXULWindow.cpp +++ b/xpfe/appshell/nsXULWindow.cpp @@ -1815,22 +1815,7 @@ NS_IMETHODIMP nsXULWindow::SizeShellTo(nsIDocShellTreeItem* aShellItem, int32_t height = 0; shellAsWin->GetSize(&width, &height); - int32_t widthDelta = aCX - width; - int32_t heightDelta = aCY - height; - - if (widthDelta || heightDelta) { - int32_t winCX = 0; - int32_t winCY = 0; - - GetSize(&winCX, &winCY); - // There's no point in trying to make the window smaller than the - // desired docshell size --- that's not likely to work. This whole - // function assumes that the outer docshell is adding some constant - // "border" chrome to aShellItem. - winCX = std::max(winCX + widthDelta, aCX); - winCY = std::max(winCY + heightDelta, aCY); - SetSize(winCX, winCY, true); - } + SizeShellToWithLimit(aCX, aCY, width, height); return NS_OK; } @@ -2196,6 +2181,27 @@ NS_IMETHODIMP nsXULWindow::SetXULBrowserWindow(nsIXULBrowserWindow * aXULBrowser return NS_OK; } +void nsXULWindow::SizeShellToWithLimit(int32_t aCX, int32_t aCY, + int32_t shellItemCx, int32_t shellItemCy) +{ + int32_t widthDelta = aCX - shellItemCx; + int32_t heightDelta = aCY - shellItemCy; + + if (widthDelta || heightDelta) { + int32_t winCX = 0; + int32_t winCY = 0; + + GetSize(&winCX, &winCY); + // There's no point in trying to make the window smaller than the + // desired docshell size --- that's not likely to work. This whole + // function assumes that the outer docshell is adding some constant + // "border" chrome to aShellItem. + winCX = std::max(winCX + widthDelta, aCX); + winCY = std::max(winCY + heightDelta, aCY); + SetSize(winCX, winCY, true); + } +} + //***************************************************************************** //*** nsContentShellInfo: Object Management //*****************************************************************************