From dfd208bb197b0522d3ac334b7c3e52a7fcf35b3a Mon Sep 17 00:00:00 2001 From: Paul Rouget Date: Wed, 17 Jul 2013 05:08:00 +0200 Subject: [PATCH] Bug 890195 - device-width media queries should use the page width, not the actual device width. r=bz --- .../responsivedesign/responsivedesign.jsm | 9 +++ .../responsivedesign/test/Makefile.in | 1 + .../test/browser_responsive_devicewidth.js | 61 +++++++++++++++++++ docshell/base/nsDocShell.cpp | 22 +++++++ docshell/base/nsDocShell.h | 1 + docshell/base/nsIDocShell.idl | 8 +++ dom/base/nsScreen.cpp | 13 ++++ dom/base/nsScreen.h | 20 ++++++ layout/base/nsPresContext.cpp | 11 ++++ layout/base/nsPresContext.h | 2 + layout/style/nsMediaFeatures.cpp | 9 ++- 11 files changed, 155 insertions(+), 2 deletions(-) create mode 100644 browser/devtools/responsivedesign/test/browser_responsive_devicewidth.js diff --git a/browser/devtools/responsivedesign/responsivedesign.jsm b/browser/devtools/responsivedesign/responsivedesign.jsm index f1c151106793..ff1f2fffa2c9 100644 --- a/browser/devtools/responsivedesign/responsivedesign.jsm +++ b/browser/devtools/responsivedesign/responsivedesign.jsm @@ -171,6 +171,12 @@ function ResponsiveUI(aWindow, aTab) this.buildUI(); this.checkMenus(); + this.docShell = this.browser.contentWindow.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIWebNavigation) + .QueryInterface(Ci.nsIDocShell); + + this.docShell.deviceSizeIsPageSize = true; + try { if (Services.prefs.getBoolPref("devtools.responsiveUI.rotate")) { this.rotate(); @@ -208,6 +214,8 @@ ResponsiveUI.prototype = { return; this.closing = true; + this.docShell.deviceSizeIsPageSize = false; + if (this._floatingScrollbars) switchToNativeScrollbars(this.tab); @@ -241,6 +249,7 @@ ResponsiveUI.prototype = { this.container.removeAttribute("responsivemode"); this.stack.removeAttribute("responsivemode"); + delete this.docShell; delete this.tab.__responsiveUI; this._telemetry.toolClosed("responsive"); ResponsiveUIManager.emit("off", this.tab, this); diff --git a/browser/devtools/responsivedesign/test/Makefile.in b/browser/devtools/responsivedesign/test/Makefile.in index a01d9dba9f97..d74fdbcc5360 100644 --- a/browser/devtools/responsivedesign/test/Makefile.in +++ b/browser/devtools/responsivedesign/test/Makefile.in @@ -16,6 +16,7 @@ MOCHITEST_BROWSER_FILES := \ browser_responsiveruleview.js \ browser_responsive_cmd.js \ browser_responsivecomputedview.js \ + browser_responsive_devicewidth.js \ head.js \ $(NULL) diff --git a/browser/devtools/responsivedesign/test/browser_responsive_devicewidth.js b/browser/devtools/responsivedesign/test/browser_responsive_devicewidth.js new file mode 100644 index 000000000000..88437afde4cd --- /dev/null +++ b/browser/devtools/responsivedesign/test/browser_responsive_devicewidth.js @@ -0,0 +1,61 @@ +/* Any copyright is dedicated to the Public Domain. +http://creativecommons.org/publicdomain/zero/1.0/ */ + +function test() { + let instance; + let mgr = ResponsiveUI.ResponsiveUIManager; + + waitForExplicitFinish(); + + gBrowser.selectedTab = gBrowser.addTab(); + gBrowser.selectedBrowser.addEventListener("load", function onload() { + gBrowser.selectedBrowser.removeEventListener("load", onload, true); + waitForFocus(startTest, content); + }, true); + + content.location = "data:text/html,mop"; + + function startTest() { + mgr.once("on", function() {executeSoon(onUIOpen)}); + document.getElementById("Tools:ResponsiveUI").doCommand(); + } + + function onUIOpen() { + instance = gBrowser.selectedTab.__responsiveUI; + instance.stack.setAttribute("notransition", "true"); + ok(instance, "instance of the module is attached to the tab."); + + let mql = content.matchMedia("(max-device-width:100px)") + + ok(!mql.matches, "media query doesn't match."); + + mql.addListener(onMediaChange); + instance.setSize(90, 500); + } + + function onMediaChange(mql) { + mql.removeListener(onMediaChange); + ok(mql.matches, "media query matches."); + ok(window.screen.width != content.screen.width, "screen.width is not the size of the screen."); + is(content.screen.width, 90, "screen.width is the width of the page."); + is(content.screen.height, 500, "screen.height is the height of the page."); + + + let docShell = content.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIWebNavigation) + .QueryInterface(Ci.nsIDocShell); + + mql.addListener(onMediaChange2); + docShell.deviceSizeIsPageSize = false; + } + + function onMediaChange2(mql) { + mql.removeListener(onMediaChange); + ok(!mql.matches, "media query has been re-evaluated."); + ok(window.screen.width == content.screen.width, "screen.width is not the size of the screen."); + instance.stack.removeAttribute("notransition"); + document.getElementById("Tools:ResponsiveUI").doCommand(); + gBrowser.removeCurrentTab(); + finish(); + } +} diff --git a/docshell/base/nsDocShell.cpp b/docshell/base/nsDocShell.cpp index 87e2660f1674..5bf559594e28 100644 --- a/docshell/base/nsDocShell.cpp +++ b/docshell/base/nsDocShell.cpp @@ -771,6 +771,7 @@ nsDocShell::nsDocShell(): mIsAppTab(false), mUseGlobalHistory(false), mInPrivateBrowsing(false), + mDeviceSizeIsPageSize(false), mFiredUnloadEvent(false), mEODForCurrentDocument(false), mURIResultedInDocument(false), @@ -3930,6 +3931,27 @@ nsDocShell::GetCurrentSHEntry(nsISHEntry** aEntry, bool* aOSHE) return NS_OK; } +NS_IMETHODIMP +nsDocShell::SetDeviceSizeIsPageSize(bool aValue) +{ + if (mDeviceSizeIsPageSize != aValue) { + mDeviceSizeIsPageSize = aValue; + nsRefPtr presContext; + GetPresContext(getter_AddRefs(presContext)); + if (presContext) { + presContext->MediaFeatureValuesChanged(presContext->eAlwaysRebuildStyle); + } + } + return NS_OK; +} + +NS_IMETHODIMP +nsDocShell::GetDeviceSizeIsPageSize(bool* aValue) +{ + *aValue = mDeviceSizeIsPageSize; + return NS_OK; +} + void nsDocShell::ClearFrameHistory(nsISHEntry* aEntry) { diff --git a/docshell/base/nsDocShell.h b/docshell/base/nsDocShell.h index 59b01658e27f..4e1bbe455ab7 100644 --- a/docshell/base/nsDocShell.h +++ b/docshell/base/nsDocShell.h @@ -824,6 +824,7 @@ protected: bool mIsAppTab; bool mUseGlobalHistory; bool mInPrivateBrowsing; + bool mDeviceSizeIsPageSize; // This boolean is set to true right before we fire pagehide and generally // unset when we embed a new content viewer. While it's true no navigation diff --git a/docshell/base/nsIDocShell.idl b/docshell/base/nsIDocShell.idl index e527df055915..56deac47e104 100644 --- a/docshell/base/nsIDocShell.idl +++ b/docshell/base/nsIDocShell.idl @@ -873,4 +873,12 @@ interface nsIDocShell : nsIDocShellTreeItem * Returns false for mLSHE, true for mOSHE */ boolean getCurrentSHEntry(out nsISHEntry aEntry); + + /** + * If deviceSizeIsPageSize is set to true, device-width/height media queries + * will be calculated from the page size, not the device size. + * + * Used by the Responsive Design View. + */ + [infallible] attribute boolean deviceSizeIsPageSize; }; diff --git a/dom/base/nsScreen.cpp b/dom/base/nsScreen.cpp index 17a240f74741..380e257bc888 100644 --- a/dom/base/nsScreen.cpp +++ b/dom/base/nsScreen.cpp @@ -387,6 +387,19 @@ nsScreen::SlowMozUnlockOrientation() return NS_OK; } +bool +nsScreen::IsDeviceSizePageSize() +{ + nsPIDOMWindow* owner = GetOwner(); + if (owner) { + nsIDocShell* docShell = owner->GetDocShell(); + if (docShell) { + return docShell->GetDeviceSizeIsPageSize(); + } + } + return false; +} + /* virtual */ JSObject* nsScreen::WrapObject(JSContext* aCx, JS::Handle aScope) diff --git a/dom/base/nsScreen.h b/dom/base/nsScreen.h index 653c69054723..f96e41318cc2 100644 --- a/dom/base/nsScreen.h +++ b/dom/base/nsScreen.h @@ -56,6 +56,15 @@ public: int32_t GetWidth(ErrorResult& aRv) { nsRect rect; + if (IsDeviceSizePageSize()) { + nsCOMPtr owner = GetOwner(); + if (owner) { + int32_t innerWidth = 0; + aRv = owner->GetInnerWidth(&innerWidth); + return innerWidth; + } + } + aRv = GetRect(rect); return rect.width; } @@ -63,6 +72,15 @@ public: int32_t GetHeight(ErrorResult& aRv) { nsRect rect; + if (IsDeviceSizePageSize()) { + nsCOMPtr owner = GetOwner(); + if (owner) { + int32_t innerHeight = 0; + aRv = owner->GetInnerHeight(&innerHeight); + return innerHeight; + } + } + aRv = GetRect(rect); return rect.height; } @@ -145,6 +163,8 @@ private: LockPermission GetLockOrientationPermission() const; + bool IsDeviceSizePageSize(); + nsRefPtr mEventListener; }; diff --git a/layout/base/nsPresContext.cpp b/layout/base/nsPresContext.cpp index 0bdd016d778f..4e1bf047aaea 100644 --- a/layout/base/nsPresContext.cpp +++ b/layout/base/nsPresContext.cpp @@ -2586,6 +2586,17 @@ nsPresContext::IsCrossProcessRootContentDocument() return (tabChild && tabChild->IsRootContentDocument()); } +bool +nsPresContext::IsDeviceSizePageSize() +{ + bool isDeviceSizePageSize = false; + nsCOMPtr docShell(do_QueryReferent(mContainer)); + if (docShell) { + isDeviceSizePageSize = docShell->GetDeviceSizeIsPageSize(); + } + return isDeviceSizePageSize; +} + nsRootPresContext::nsRootPresContext(nsIDocument* aDocument, nsPresContextType aType) : nsPresContext(aDocument, aType), diff --git a/layout/base/nsPresContext.h b/layout/base/nsPresContext.h index 11fffb3d1e3b..e16b3ce31fc5 100644 --- a/layout/base/nsPresContext.h +++ b/layout/base/nsPresContext.h @@ -1006,6 +1006,8 @@ public: mExistThrottledUpdates = aExistThrottledUpdates; } + bool IsDeviceSizePageSize(); + protected: friend class nsRunnableMethod; NS_HIDDEN_(void) ThemeChangedInternal(); diff --git a/layout/style/nsMediaFeatures.cpp b/layout/style/nsMediaFeatures.cpp index 2da68c26f796..bddc6c150839 100644 --- a/layout/style/nsMediaFeatures.cpp +++ b/layout/style/nsMediaFeatures.cpp @@ -109,14 +109,19 @@ static nsSize GetDeviceSize(nsPresContext* aPresContext) { nsSize size; - if (aPresContext->IsRootPaginatedDocument()) + + if (aPresContext->IsDeviceSizePageSize()) { + size = GetSize(aPresContext); + } else if (aPresContext->IsRootPaginatedDocument()) { // We want the page size, including unprintable areas and margins. // XXX The spec actually says we want the "page sheet size", but // how is that different? size = aPresContext->GetPageSize(); - else + } else { GetDeviceContextFor(aPresContext)-> GetDeviceSurfaceDimensions(size.width, size.height); + } + return size; }