Bug 890195 - device-width media queries should use the page width, not the actual device width. r=bz
☠☠ backed out by 01905185e9c0 ☠ ☠
authorPaul Rouget <paul@mozilla.com>
Wed, 17 Jul 2013 05:08:00 +0200
changeset 139764 dad6d29417803cb148da36faff2a17d2b58ffb34
parent 139763 11d1d03a247f916c740c7a407a7b21c5889fb693
child 139765 9ff218b84720678168ba8a7f2678d4ccdf84b972
push id270
push userpvanderbeken@mozilla.com
push dateThu, 06 Mar 2014 09:24:21 +0000
reviewersbz
bugs890195
milestone25.0a1
Bug 890195 - device-width media queries should use the page width, not the actual device width. r=bz
browser/devtools/responsivedesign/responsivedesign.jsm
browser/devtools/responsivedesign/test/Makefile.in
browser/devtools/responsivedesign/test/browser_responsive_devicewidth.js
docshell/base/nsDocShell.cpp
docshell/base/nsDocShell.h
docshell/base/nsIDocShell.idl
dom/base/nsScreen.cpp
dom/base/nsScreen.h
layout/base/nsPresContext.cpp
layout/base/nsPresContext.h
layout/style/nsMediaFeatures.cpp
--- a/browser/devtools/responsivedesign/responsivedesign.jsm
+++ b/browser/devtools/responsivedesign/responsivedesign.jsm
@@ -166,16 +166,22 @@ function ResponsiveUI(aWindow, aTab)
   // Events
   this.tab.addEventListener("TabClose", this);
   this.tabContainer.addEventListener("TabSelect", this);
   this.mainWindow.document.addEventListener("keypress", this.bound_onKeypress, false);
 
   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();
     }
   } catch(e) {}
 
   if (this._floatingScrollbars)
     switchToFloatingScrollbars(this.tab);
@@ -203,16 +209,18 @@ ResponsiveUI.prototype = {
   /**
    * Destroy the nodes. Remove listeners. Reset the style.
    */
   close: function RUI_unload() {
     if (this.closing)
       return;
     this.closing = true;
 
+    this.docShell.deviceSizeIsPageSize = false;
+
     if (this._floatingScrollbars)
       switchToNativeScrollbars(this.tab);
 
     this.unCheckMenus();
     // Reset style of the stack.
     let style = "max-width: none;" +
                 "min-width: 0;" +
                 "max-height: none;" +
@@ -236,16 +244,17 @@ ResponsiveUI.prototype = {
     this.container.removeChild(this.toolbar);
     this.stack.removeChild(this.resizer);
     this.stack.removeChild(this.resizeBar);
 
     // Unset the responsive mode.
     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);
   },
 
   /**
    * Handle keypressed.
    *
--- a/browser/devtools/responsivedesign/test/Makefile.in
+++ b/browser/devtools/responsivedesign/test/Makefile.in
@@ -11,12 +11,13 @@ relativesrcdir  = @relativesrcdir@
 include $(DEPTH)/config/autoconf.mk
 
 MOCHITEST_BROWSER_FILES := \
 		browser_responsiveui.js \
 		browser_responsiveuiaddcustompreset.js \
 		browser_responsiveruleview.js \
 		browser_responsive_cmd.js \
 		browser_responsivecomputedview.js \
+		browser_responsive_devicewidth.js \
 		head.js \
 		$(NULL)
 
 include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /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();
+  }
+}
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -766,16 +766,17 @@ nsDocShell::nsDocShell():
     mObserveErrorPages(true),
     mAllowAuth(true),
     mAllowKeywordFixup(false),
     mIsOffScreenBrowser(false),
     mIsActive(true),
     mIsAppTab(false),
     mUseGlobalHistory(false),
     mInPrivateBrowsing(false),
+    mDeviceSizeIsPageSize(false),
     mFiredUnloadEvent(false),
     mEODForCurrentDocument(false),
     mURIResultedInDocument(false),
     mIsBeingDestroyed(false),
     mIsExecutingOnLoadHandler(false),
     mIsPrintingOrPP(false),
     mSavingOldViewer(false),
 #ifdef DEBUG
@@ -3925,16 +3926,37 @@ nsDocShell::GetCurrentSHEntry(nsISHEntry
         NS_ADDREF(*aEntry = mLSHE);
     } else if (mOSHE) {
         NS_ADDREF(*aEntry = mOSHE);
         *aOSHE = true;
     }
     return NS_OK;
 }
 
+NS_IMETHODIMP
+nsDocShell::SetDeviceSizeIsPageSize(bool aValue)
+{
+    if (mDeviceSizeIsPageSize != aValue) {
+      mDeviceSizeIsPageSize = aValue;
+      nsRefPtr<nsPresContext> 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)
 {
   nsCOMPtr<nsISHContainer> shcontainer = do_QueryInterface(aEntry);
   nsCOMPtr<nsISHistory> rootSH;
   GetRootSessionHistory(getter_AddRefs(rootSH));
   nsCOMPtr<nsISHistoryInternal> history = do_QueryInterface(rootSH);
   if (!history || !shcontainer) {
--- a/docshell/base/nsDocShell.h
+++ b/docshell/base/nsDocShell.h
@@ -819,16 +819,17 @@ protected:
     bool                       mObserveErrorPages;
     bool                       mAllowAuth;
     bool                       mAllowKeywordFixup;
     bool                       mIsOffScreenBrowser;
     bool                       mIsActive;
     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
     // is allowed in this docshell.
     bool                       mFiredUnloadEvent;
 
     // this flag is for bug #21358. a docshell may load many urls
     // which don't result in new documents being created (i.e. a new
--- a/docshell/base/nsIDocShell.idl
+++ b/docshell/base/nsIDocShell.idl
@@ -868,9 +868,17 @@ interface nsIDocShell : nsIDocShellTreeI
    * Set when an iframe/frame is added dynamically.
    */
   attribute boolean createdDynamically;
 
   /**
    * 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;
 };
--- a/dom/base/nsScreen.cpp
+++ b/dom/base/nsScreen.cpp
@@ -382,16 +382,29 @@ nsScreen::MozUnlockOrientation()
 
 NS_IMETHODIMP
 nsScreen::SlowMozUnlockOrientation()
 {
   MozUnlockOrientation();
   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<JSObject*> aScope)
 {
   return ScreenBinding::Wrap(aCx, aScope, this);
 }
 
 NS_IMPL_ISUPPORTS1(nsScreen::FullScreenEventListener, nsIDOMEventListener)
--- a/dom/base/nsScreen.h
+++ b/dom/base/nsScreen.h
@@ -51,23 +51,41 @@ public:
     nsRect rect;
     aRv = GetRect(rect);
     return rect.x;
   }
 
   int32_t GetWidth(ErrorResult& aRv)
   {
     nsRect rect;
+    if (IsDeviceSizePageSize()) {
+      nsCOMPtr<nsPIDOMWindow> owner = GetOwner();
+      if (owner) {
+        int32_t innerWidth = 0;
+        aRv = owner->GetInnerWidth(&innerWidth);
+        return innerWidth;
+      }
+    }
+
     aRv = GetRect(rect);
     return rect.width;
   }
 
   int32_t GetHeight(ErrorResult& aRv)
   {
     nsRect rect;
+    if (IsDeviceSizePageSize()) {
+      nsCOMPtr<nsPIDOMWindow> owner = GetOwner();
+      if (owner) {
+        int32_t innerHeight = 0;
+        aRv = owner->GetInnerHeight(&innerHeight);
+        return innerHeight;
+      }
+    }
+
     aRv = GetRect(rect);
     return rect.height;
   }
 
   int32_t GetPixelDepth(ErrorResult& aRv);
   int32_t GetColorDepth(ErrorResult& aRv)
   {
     return GetPixelDepth(aRv);
@@ -140,12 +158,14 @@ private:
   enum LockPermission {
     LOCK_DENIED,
     FULLSCREEN_LOCK_ALLOWED,
     LOCK_ALLOWED
   };
 
   LockPermission GetLockOrientationPermission() const;
 
+  bool IsDeviceSizePageSize();
+
   nsRefPtr<FullScreenEventListener> mEventListener;
 };
 
 #endif /* nsScreen_h___ */
--- a/layout/base/nsPresContext.cpp
+++ b/layout/base/nsPresContext.cpp
@@ -2581,16 +2581,27 @@ nsPresContext::IsCrossProcessRootContent
   if (XRE_GetProcessType() == GeckoProcessType_Default) {
     return true;
   }
 
   TabChild* tabChild = GetTabChildFrom(mShell);
   return (tabChild && tabChild->IsRootContentDocument());
 }
 
+bool
+nsPresContext::IsDeviceSizePageSize()
+{
+  bool isDeviceSizePageSize = false;
+  nsCOMPtr<nsIDocShell> docShell(do_QueryReferent(mContainer));
+  if (docShell) {
+    isDeviceSizePageSize = docShell->GetDeviceSizeIsPageSize();
+  }
+  return isDeviceSizePageSize;
+}
+
 nsRootPresContext::nsRootPresContext(nsIDocument* aDocument,
                                      nsPresContextType aType)
   : nsPresContext(aDocument, aType),
     mDOMGeneration(0)
 {
   mRegisteredPlugins.Init();
 }
 
--- a/layout/base/nsPresContext.h
+++ b/layout/base/nsPresContext.h
@@ -1001,16 +1001,18 @@ public:
   bool ExistThrottledUpdates() const {
     return mExistThrottledUpdates;
   }
 
   void SetExistThrottledUpdates(bool aExistThrottledUpdates) {
     mExistThrottledUpdates = aExistThrottledUpdates;
   }
 
+  bool IsDeviceSizePageSize();
+
 protected:
   friend class nsRunnableMethod<nsPresContext>;
   NS_HIDDEN_(void) ThemeChangedInternal();
   NS_HIDDEN_(void) SysColorChangedInternal();
   NS_HIDDEN_(void) UIResolutionChangedInternal();
 
   static NS_HIDDEN_(bool)
   UIResolutionChangedSubdocumentCallback(nsIDocument* aDocument, void* aData);
--- a/layout/style/nsMediaFeatures.cpp
+++ b/layout/style/nsMediaFeatures.cpp
@@ -104,24 +104,29 @@ GetDeviceContextFor(nsPresContext* aPres
   return aPresContext->DeviceContext();
 }
 
 // A helper for three features below.
 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;
 }
 
 static nsresult
 GetDeviceWidth(nsPresContext* aPresContext, const nsMediaFeature*,
                nsCSSValue& aResult)
 {
     nsSize size = GetDeviceSize(aPresContext);