Backed out changeset b099e7e0b264 (bug 1454358) for build bustages on Element.h CLOSED TREE
authorBogdan Tara <btara@mozilla.com>
Thu, 12 Jul 2018 01:50:53 +0300
changeset 481348 d610dd7bfbcdc0ec00ce393dff833f2d8e1b7986
parent 481347 b099e7e0b26494e0b2cdc549f75ff19291627968
child 481349 192fb5d1f926574d62868d7a660da8aa01339852
push id9719
push userffxbld-merge
push dateFri, 24 Aug 2018 17:49:46 +0000
treeherdermozilla-beta@719ec98fba77 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1454358
milestone63.0a1
backs outb099e7e0b26494e0b2cdc549f75ff19291627968
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Backed out changeset b099e7e0b264 (bug 1454358) for build bustages on Element.h CLOSED TREE
browser/components/places/content/menu.xml
devtools/client/shared/widgets/FastListWidget.js
devtools/client/shared/widgets/SideMenuWidget.jsm
devtools/client/shared/widgets/SimpleListWidget.jsm
devtools/client/shared/widgets/VariablesView.jsm
dom/base/Element.h
dom/base/nsDocument.cpp
dom/bindings/BindingUtils.cpp
dom/chrome-webidl/XULScrollElement.webidl
dom/chrome-webidl/moz.build
dom/events/test/test_bug602962.xul
dom/tests/mochitest/general/test_interfaces.js
dom/webidl/ScrollBoxObject.webidl
dom/webidl/moz.build
dom/xul/XULScrollElement.cpp
dom/xul/XULScrollElement.h
dom/xul/moz.build
dom/xul/nsXULElement.cpp
layout/build/nsLayoutCID.h
layout/build/nsLayoutModule.cpp
layout/reftests/invalidation/540247-1-ref.xul
layout/reftests/invalidation/540247-1.xul
layout/xul/ScrollBoxObject.cpp
layout/xul/ScrollBoxObject.h
layout/xul/moz.build
testing/mochitest/browser-harness.xul
toolkit/content/tests/chrome/test_mousescroll.xul
toolkit/content/tests/chrome/test_position.xul
toolkit/content/tests/chrome/test_richlist_direction.xul
toolkit/content/tests/chrome/window_largemenu.xul
toolkit/content/widgets/richlistbox.xml
toolkit/content/widgets/scrollbox.xml
--- a/browser/components/places/content/menu.xml
+++ b/browser/components/places/content/menu.xml
@@ -80,17 +80,18 @@
             // The element we are dragging over
             let elt = aEvent.target;
             if (elt.localName == "menupopup")
               elt = elt.parentNode;
 
             // Calculate positions taking care of arrowscrollbox
             let scrollbox = this._scrollBox;
             let eventY = aEvent.layerY + (scrollbox.boxObject.y - this.boxObject.y);
-            let scrollboxOffset = this.boxObject.y;
+            let scrollboxOffset = scrollbox.scrollBoxObject.y -
+                                  (scrollbox.boxObject.y - this.boxObject.y);
             let eltY = elt.boxObject.y - scrollboxOffset;
             let eltHeight = elt.boxObject.height;
 
             if (!elt._placesNode) {
               // If we are dragging over a non places node drop at the end.
               dropPoint.ip = new PlacesInsertionPoint({
                 parentId: PlacesUtils.getConcreteItemId(resultNode),
                 parentGuid: PlacesUtils.getConcreteItemGuid(resultNode)
@@ -428,30 +429,30 @@
         if (dropPoint.folderElt || this._hideDropIndicator(event)) {
           this._indicatorBar.hidden = true;
           event.preventDefault();
           event.stopPropagation();
           return;
         }
 
         // We should display the drop indicator relative to the arrowscrollbox.
-        let scrollbox = this._scrollBox.boxObject;
+        let sbo = this._scrollBox.scrollBoxObject;
         let newMarginTop = 0;
         if (scrollDir == 0) {
           let elt = this.firstChild;
           while (elt && event.screenY > elt.boxObject.screenY +
                                         elt.boxObject.height / 2)
             elt = elt.nextSibling;
-          newMarginTop = elt ? elt.boxObject.screenY - scrollbox.screenY :
-                               scrollbox.height;
+          newMarginTop = elt ? elt.boxObject.screenY - sbo.screenY :
+                               sbo.height;
         } else if (scrollDir == 1)
-          newMarginTop = scrollbox.height;
+          newMarginTop = sbo.height;
 
         // Set the new marginTop based on arrowscrollbox.
-        newMarginTop += scrollbox.y - this._scrollBox.boxObject.y;
+        newMarginTop += sbo.y - this._scrollBox.boxObject.y;
         this._indicatorBar.firstChild.style.marginTop = newMarginTop + "px";
         this._indicatorBar.hidden = false;
 
         event.preventDefault();
         event.stopPropagation();
       ]]></handler>
 
       <handler event="dragexit"><![CDATA[
--- a/devtools/client/shared/widgets/FastListWidget.js
+++ b/devtools/client/shared/widgets/FastListWidget.js
@@ -188,19 +188,19 @@ FastListWidget.prototype = {
    *        The element to make visible.
    */
   ensureElementIsVisible: function(element) {
     if (!element) {
       return;
     }
 
     // Ensure the element is visible but not scrolled horizontally.
-    const scrollbox = this._list;
-    scrollbox.ensureElementIsVisible(element);
-    scrollbox.scrollBy(-this._list.clientWidth, 0);
+    const boxObject = this._list.boxObject;
+    boxObject.ensureElementIsVisible(element);
+    boxObject.scrollBy(-this._list.clientWidth, 0);
   },
 
   /**
    * Sets the text displayed in this container when empty.
    * @param string aValue
    */
   set _textWhenEmpty(value) {
     if (this._emptyTextNode) {
--- a/devtools/client/shared/widgets/SideMenuWidget.jsm
+++ b/devtools/client/shared/widgets/SideMenuWidget.jsm
@@ -222,19 +222,19 @@ SideMenuWidget.prototype = {
    *        The element to make visible.
    */
   ensureElementIsVisible: function(aElement) {
     if (!aElement) {
       return;
     }
 
     // Ensure the element is visible but not scrolled horizontally.
-    const scrollbox = this._list;
-    scrollbox.ensureElementIsVisible(aElement);
-    scrollbox.scrollBy(-this._list.clientWidth, 0);
+    const boxObject = this._list.boxObject;
+    boxObject.ensureElementIsVisible(aElement);
+    boxObject.scrollBy(-this._list.clientWidth, 0);
   },
 
   /**
    * Shows all the groups, even the ones with no visible children.
    */
   showEmptyGroups: function() {
     for (const group of this._orderedGroupElementsArray) {
       group.hidden = false;
--- a/devtools/client/shared/widgets/SimpleListWidget.jsm
+++ b/devtools/client/shared/widgets/SimpleListWidget.jsm
@@ -165,19 +165,19 @@ SimpleListWidget.prototype = {
    *        The element to make visible.
    */
   ensureElementIsVisible: function(aElement) {
     if (!aElement) {
       return;
     }
 
     // Ensure the element is visible but not scrolled horizontally.
-    const scrollbox = this._list;
-    scrollbox.ensureElementIsVisible(aElement);
-    scrollbox.scrollBy(-this._list.clientWidth, 0);
+    const boxObject = this._list.boxObject;
+    boxObject.ensureElementIsVisible(aElement);
+    boxObject.scrollBy(-this._list.clientWidth, 0);
   },
 
   /**
    * Sets the text displayed permanently in this container as a header.
    * @param string aValue
    */
   set _textAsHeader(aValue) {
     if (this._headerTextNode) {
--- a/devtools/client/shared/widgets/VariablesView.jsm
+++ b/devtools/client/shared/widgets/VariablesView.jsm
@@ -786,17 +786,17 @@ VariablesView.prototype = {
   _focusItem: function(aItem, aCollapseFlag) {
     if (!aItem.focusable) {
       return false;
     }
     if (aCollapseFlag) {
       aItem.collapse();
     }
     aItem._target.focus();
-    this._list.ensureElementIsVisible(aItem._arrow);
+    this.boxObject.ensureElementIsVisible(aItem._arrow);
     return true;
   },
 
   /**
    * Listener handling a key down event on the view.
    */
   _onViewKeyDown: function(e) {
     const item = this.getFocusedItem();
@@ -3970,17 +3970,17 @@ Editable.prototype = {
     // element's specified label location.
     const input = this._input = this._variable.document.createElement("textbox");
     input.className = "plain " + this.className;
     input.setAttribute("value", initialString);
     input.setAttribute("flex", "1");
 
     // Replace the specified label with a textbox input element.
     label.parentNode.replaceChild(input, label);
-    this._variable._variablesView._list.ensureElementIsVisible(input);
+    this._variable._variablesView.boxObject.ensureElementIsVisible(input);
     input.select();
 
     // When the value is a string (displayed as "value"), then we probably want
     // to change it to another string in the textbox, so to avoid typing the ""
     // again, tackle with the selection bounds just a bit.
     if (initialString.match(/^".+"$/)) {
       input.selectionEnd--;
       input.selectionStart++;
@@ -4004,18 +4004,18 @@ Editable.prototype = {
    * state.
    */
   deactivate: function() {
     this._input.removeEventListener("keydown", this._onKeydown);
     this._input.removeEventListener("blur", this.deactivate);
     this._input.parentNode.replaceChild(this.label, this._input);
     this._input = null;
 
-    const scrollbox = this._variable._variablesView._list;
-    scrollbox.scrollBy(-this._variable._target, 0);
+    const { boxObject } = this._variable._variablesView;
+    boxObject.scrollBy(-this._variable._target, 0);
     this._variable.locked = false;
     this._variable.twisty = this._prevExpandable;
     this._variable.expanded = this._prevExpanded;
     this._variable.editing = false;
     this._onCleanup && this._onCleanup();
   },
 
   /**
--- a/dom/base/Element.h
+++ b/dom/base/Element.h
@@ -590,20 +590,16 @@ protected:
   void RemoveStatesSilently(EventStates aStates)
   {
     mState &= ~aStates;
   }
 
   already_AddRefed<ShadowRoot> AttachShadowInternal(
     ShadowRootMode, ErrorResult& aError);
 
-  MOZ_CAN_RUN_SCRIPT
-  nsIScrollableFrame* GetScrollFrame(nsIFrame **aStyledFrame = nullptr,
-                                     FlushType aFlushType = FlushType::Layout);
-
 private:
   // Need to allow the ESM, nsGlobalWindow, and the focus manager to
   // set our state
   friend class mozilla::EventStateManager;
   friend class ::nsGlobalWindowInner;
   friend class ::nsGlobalWindowOuter;
   friend class ::nsFocusManager;
 
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -251,16 +251,17 @@
 #include "mozilla/StyleSheetInlines.h"
 #include "mozilla/dom/SVGDocument.h"
 #include "mozilla/dom/SVGSVGElement.h"
 #include "mozilla/dom/DocGroup.h"
 #include "mozilla/dom/TabGroup.h"
 #ifdef MOZ_XUL
 #include "mozilla/dom/ListBoxObject.h"
 #include "mozilla/dom/MenuBoxObject.h"
+#include "mozilla/dom/ScrollBoxObject.h"
 #include "mozilla/dom/TreeBoxObject.h"
 #include "nsIXULWindow.h"
 #include "nsIDocShellTreeOwner.h"
 #endif
 #include "nsIPresShellInlines.h"
 
 #include "mozilla/DocLoadingTimelineMarker.h"
 
@@ -6324,16 +6325,18 @@ nsIDocument::GetBoxObjectFor(Element* aE
 #ifdef MOZ_XUL
   if (namespaceID == kNameSpaceID_XUL) {
     if (tag == nsGkAtoms::menu) {
       boxObject = new MenuBoxObject();
     } else if (tag == nsGkAtoms::tree) {
       boxObject = new TreeBoxObject();
     } else if (tag == nsGkAtoms::listbox) {
       boxObject = new ListBoxObject();
+    } else if (tag == nsGkAtoms::scrollbox) {
+      boxObject = new ScrollBoxObject();
     } else {
       boxObject = new BoxObject();
     }
   } else
 #endif // MOZ_XUL
   {
     boxObject = new BoxObject();
   }
--- a/dom/bindings/BindingUtils.cpp
+++ b/dom/bindings/BindingUtils.cpp
@@ -51,17 +51,16 @@
 #include "mozilla/dom/XULFrameElementBinding.h"
 #include "mozilla/dom/XULPopupElementBinding.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/ResolveSystemBinding.h"
 #include "mozilla/dom/WebIDLGlobalNameHash.h"
 #include "mozilla/dom/WorkerPrivate.h"
 #include "mozilla/dom/WorkerScope.h"
 #include "mozilla/dom/XrayExpandoClass.h"
-#include "mozilla/dom/XULScrollElementBinding.h"
 #include "mozilla/jsipc/CrossProcessObjectWrappers.h"
 #include "ipc/ErrorIPCUtils.h"
 #include "mozilla/UseCounter.h"
 #include "mozilla/dom/DocGroup.h"
 #include "nsXULElement.h"
 
 namespace mozilla {
 namespace dom {
@@ -3855,18 +3854,16 @@ HTMLConstructor(JSContext* aCx, unsigned
           definition->mLocalName == nsGkAtoms::popup ||
           definition->mLocalName == nsGkAtoms::panel ||
           definition->mLocalName == nsGkAtoms::tooltip) {
         cb = XULPopupElement_Binding::GetConstructorObject;
       } else if (definition->mLocalName == nsGkAtoms::iframe ||
                  definition->mLocalName == nsGkAtoms::browser ||
                  definition->mLocalName == nsGkAtoms::editor) {
         cb = XULFrameElement_Binding::GetConstructorObject;
-      } else if (definition->mLocalName == nsGkAtoms::scrollbox) {
-          cb = XULScrollElement_Binding::GetConstructorObject;
       } else {
         cb = XULElement_Binding::GetConstructorObject;
       }
     }
 
     if (!cb) {
       return ThrowErrorMessage(aCx, MSG_ILLEGAL_CONSTRUCTOR);
     }
--- a/dom/chrome-webidl/moz.build
+++ b/dom/chrome-webidl/moz.build
@@ -40,17 +40,16 @@ WEBIDL_FILES = [
     'MozStorageAsyncStatementParams.webidl',
     'MozStorageStatementParams.webidl',
     'MozStorageStatementRow.webidl',
     'PrecompiledScript.webidl',
     'PromiseDebugging.webidl',
     'StructuredCloneHolder.webidl',
     'WebExtensionContentScript.webidl',
     'WebExtensionPolicy.webidl',
-    'XULFrameElement.webidl',
-    'XULScrollElement.webidl'
+    'XULFrameElement.webidl'
 ]
 
 if CONFIG['MOZ_PLACES']:
     WEBIDL_FILES += [
         'PlacesEvent.webidl',
         'PlacesObservers.webidl',
     ]
--- a/dom/events/test/test_bug602962.xul
+++ b/dom/events/test/test_bug602962.xul
@@ -13,72 +13,73 @@ https://bugzilla.mozilla.org/show_bug.cg
   <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=602962">Mozilla Bug 602962</a>
   <p id="display"></p>
 <div id="content" style="display: none">
 </div>
 </body>
 
 <script class="testbody" type="application/javascript"><![CDATA[
 /** Test for Bug 602962 **/
-var scrollbox, content;
+var scrollbox, sbo, content;
 var scrollX = 0, scrollY = 0;
 
 var oldWidth = 0, oldHeight = 0;
 var win = null;
 
 function openWindow() {
   win = window.open("chrome://mochitests/content/chrome/dom/events/test/bug602962.xul", "_blank", "width=600,height=600");
 }
 
 function doTest() {
   scrollbox = win.document.getElementById("page-scrollbox");
+  sbo = scrollbox.boxObject;
   content = win.document.getElementById("page-box");
   content.style.width = 400 + "px";
-
+  
   win.addEventListener("resize", function() {
     win.removeEventListener("resize", arguments.callee, false);
 
     setTimeout(function(){
-      scrollbox.scrollBy(200,0);
+      sbo.scrollBy(200, 0);
       resize();
     },0);
   }, false);
 
   oldWidth = win.outerWidth;
   oldHeight = win.outerHeight;
   win.resizeTo(200, 400);
 }
 
 function resize() {
- scrollX = scrollbox.scrollLeft;
- scrollY = scrollbox.scrollTop;
+  scrollX = sbo.positionX;
+  scrollY = sbo.positionY;
 
   win.addEventListener("resize", function() {
     content.style.width = (oldWidth + 400) + "px";
     win.removeEventListener("resize", arguments.callee, true);
-
+    
     setTimeout(function() {
       finish();
     }, 0);
   }, true);
 
   win.resizeTo(oldWidth, oldHeight);
 }
 
 function finish() {
   if (win.outerWidth != oldWidth ||
       win.outerHeight != oldHeight) {
     // We should eventually get back to the original size.
     setTimeout(finish, 0);
     return;
   }
-  scrollbox.scrollBy(scrollX, scrollY);
+  sbo.scrollBy(scrollX, scrollY);
 
-  is(scrollbox.scrollLeft, 200, "Scroll Left should have been restored to the value before the resize");
-  is(scrollbox.scrollTop, 0, "Scroll Top should have been restored to the value before the resize");
+  is(sbo.positionX, 200, "Scroll X should have been restored to the value before the resize");
+  is(sbo.positionY, 0, "Scroll Y should have been restored to the value before the resize");
 
   is(win.outerWidth, oldWidth, "Width should be resized");
   is(win.outerHeight, oldHeight, "Height should be resized");
   win.close();
   SimpleTest.finish();
 }
 
 SimpleTest.waitForExplicitFinish();
--- a/dom/tests/mochitest/general/test_interfaces.js
+++ b/dom/tests/mochitest/general/test_interfaces.js
@@ -1252,18 +1252,16 @@ var interfaceNamesInGlobalScope =
     {name: "XULDocument", insecureContext: true, xbl: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "XULElement", insecureContext: true, xbl: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "XULFrameElement", insecureContext: true, xbl: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "XULPopupElement", insecureContext: true, xbl: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
-    {name: "XULScrollElement", insecureContext: true, xbl: true},
-// IMPORTANT: Do not change this list without review from a DOM peer!
   ];
 // IMPORTANT: Do not change the list above without review from a DOM peer!
 
 function createInterfaceMap(isXBLScope) {
   var interfaceMap = {};
 
   function addInterfaces(interfaces)
   {
rename from dom/chrome-webidl/XULScrollElement.webidl
rename to dom/webidl/ScrollBoxObject.webidl
--- a/dom/chrome-webidl/XULScrollElement.webidl
+++ b/dom/webidl/ScrollBoxObject.webidl
@@ -1,15 +1,45 @@
 /* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/.
  */
 
-[HTMLConstructor, Func="IsChromeOrXBL"]
-interface XULScrollElement : XULElement {
+[NoInterfaceObject]
+interface ScrollBoxObject : BoxObject {
+
+  /**
+   * Scroll to the given coordinates, in css pixels.
+   * (0,0) will put the top left corner of the scrolled element's padding-box
+   * at the top left corner of the scrollport (which is its inner-border-box).
+   * Values will be clamped to legal values.
+   */
+  [Throws]
+  void scrollTo(long x, long y);
+
+  /**
+   * Scroll the given amount of device pixels to the right and down.
+   * Values will be clamped to make the resuling position legal.
+   */
+  [Throws]
+  void scrollBy(long dx, long dy);
+  [Throws]
+  void scrollByIndex(long dindexes);
   [Throws]
   void scrollToElement(Element child);
-  [Throws]
-  void scrollByIndex(long aIndexes);
+
+  /**
+   * Get the current scroll position in css pixels.
+   * @see scrollTo for the definition of x and y.
+   */
+  [Pure, Throws]
+  readonly attribute long positionX;
+  [Pure, Throws]
+  readonly attribute long positionY;
+  [Pure, Throws]
+  readonly attribute long scrolledWidth;
+  [Pure, Throws]
+  readonly attribute long scrolledHeight;
+
   [Throws]
   void ensureElementIsVisible(Element child);
 };
--- a/dom/webidl/moz.build
+++ b/dom/webidl/moz.build
@@ -745,16 +745,17 @@ WEBIDL_FILES = [
     'Range.webidl',
     'Request.webidl',
     'Response.webidl',
     'RTCStatsReport.webidl',
     'Screen.webidl',
     'ScreenOrientation.webidl',
     'ScriptProcessorNode.webidl',
     'ScrollAreaEvent.webidl',
+    'ScrollBoxObject.webidl',
     'Selection.webidl',
     'ServiceWorker.webidl',
     'ServiceWorkerContainer.webidl',
     'ServiceWorkerGlobalScope.webidl',
     'ServiceWorkerRegistration.webidl',
     'ShadowRoot.webidl',
     'SharedWorker.webidl',
     'SharedWorkerGlobalScope.webidl',
--- a/dom/xul/moz.build
+++ b/dom/xul/moz.build
@@ -17,32 +17,30 @@ MOCHITEST_CHROME_MANIFESTS += ['test/chr
 if CONFIG['MOZ_XUL']:
     EXPORTS += [
         'nsXULElement.h',
     ]
 
     EXPORTS.mozilla.dom += [
         'XULFrameElement.h',
         'XULPopupElement.h',
-        'XULScrollElement.h',
     ]
 
     UNIFIED_SOURCES += [
         'nsXULCommandDispatcher.cpp',
         'nsXULContentSink.cpp',
         'nsXULContentUtils.cpp',
         'nsXULElement.cpp',
         'nsXULPopupListener.cpp',
         'nsXULPrototypeCache.cpp',
         'nsXULPrototypeDocument.cpp',
         'nsXULSortService.cpp',
         'XULDocument.cpp',
         'XULFrameElement.cpp',
         'XULPopupElement.cpp',
-        'XULScrollElement.cpp',
     ]
 
 XPIDL_SOURCES += [
     'nsIController.idl',
     'nsIControllers.idl',
     'nsIXULSortService.idl',
 ]
 
--- a/dom/xul/nsXULElement.cpp
+++ b/dom/xul/nsXULElement.cpp
@@ -70,17 +70,16 @@
 #include "nsXULTooltipListener.h"
 #include "mozilla/EventDispatcher.h"
 #include "mozAutoDocUpdate.h"
 #include "nsCCUncollectableMarker.h"
 #include "nsICSSDeclaration.h"
 #include "nsLayoutUtils.h"
 #include "XULFrameElement.h"
 #include "XULPopupElement.h"
-#include "XULScrollElement.h"
 
 #include "mozilla/dom/XULElementBinding.h"
 #include "mozilla/dom/BoxObject.h"
 #include "mozilla/dom/MouseEventBinding.h"
 #include "mozilla/dom/MutationEventBinding.h"
 #include "mozilla/dom/XULCommandEvent.h"
 
 using namespace mozilla;
@@ -153,21 +152,16 @@ nsXULElement* nsXULElement::Construct(al
 
   if (nodeInfo->Equals(nsGkAtoms::iframe) ||
       nodeInfo->Equals(nsGkAtoms::browser) ||
       nodeInfo->Equals(nsGkAtoms::editor)) {
     already_AddRefed<mozilla::dom::NodeInfo> frameni = nodeInfo.forget();
     return new XULFrameElement(frameni);
   }
 
-  if (nodeInfo->Equals(nsGkAtoms::scrollbox)) {
-    already_AddRefed<mozilla::dom::NodeInfo> scrollni = nodeInfo.forget();
-    return new XULScrollElement(scrollni);
-  }
-
   return NS_NewBasicXULElement(nodeInfo.forget());
 }
 
 /* static */
 already_AddRefed<nsXULElement>
 nsXULElement::CreateFromPrototype(nsXULPrototypeElement* aPrototype,
                                   mozilla::dom::NodeInfo *aNodeInfo,
                                   bool aIsScriptable,
--- a/layout/build/nsLayoutCID.h
+++ b/layout/build/nsLayoutCID.h
@@ -18,16 +18,20 @@
 // {D750A964-2D14-484c-B3AA-8ED7823B5C7B}
 #define NS_BOXOBJECT_CID \
 { 0xd750a964, 0x2d14, 0x484c, { 0xb3, 0xaa, 0x8e, 0xd7, 0x82, 0x3b, 0x5c, 0x7b } }
 
 // {C2710D40-6F4D-4b7f-9778-76AE5166648C}
 #define NS_LISTBOXOBJECT_CID \
 { 0xc2710d40, 0x6f4d, 0x4b7f, { 0x97, 0x78, 0x76, 0xae, 0x51, 0x66, 0x64, 0x8c } }
 
+// {56E2ADA8-4631-11d4-BA11-001083023C1E}
+#define NS_SCROLLBOXOBJECT_CID \
+{ 0x56e2ada8, 0x4631, 0x11d4, { 0xba, 0x11, 0x0, 0x10, 0x83, 0x2, 0x3c, 0x1e } }
+
 // {AA40253B-4C42-4056-8132-37BCD07862FD}
 #define NS_MENUBOXOBJECT_CID \
 { 0xaa40253b, 0x4c42, 0x4056, { 0x81, 0x32, 0x37, 0xbc, 0xd0, 0x78, 0x62, 0xfd } }
 
 // {3B581FD4-3497-426c-8F61-3658B971CB80}
 #define NS_TREEBOXOBJECT_CID \
 { 0x3b581fd4, 0x3497, 0x426c, { 0x8f, 0x61, 0x36, 0x58, 0xb9, 0x71, 0xcb, 0x80 } }
 
--- a/layout/build/nsLayoutModule.cpp
+++ b/layout/build/nsLayoutModule.cpp
@@ -296,16 +296,17 @@ Shutdown()
 #ifdef DEBUG
 nsresult NS_NewLayoutDebugger(nsILayoutDebugger** aResult);
 #endif
 
 nsresult NS_NewBoxObject(nsIBoxObject** aResult);
 
 #ifdef MOZ_XUL
 nsresult NS_NewListBoxObject(nsIBoxObject** aResult);
+nsresult NS_NewScrollBoxObject(nsIBoxObject** aResult);
 nsresult NS_NewMenuBoxObject(nsIBoxObject** aResult);
 nsresult NS_NewTreeBoxObject(nsIBoxObject** aResult);
 #endif
 
 nsresult NS_CreateFrameTraversal(nsIFrameTraversal** aResult);
 
 already_AddRefed<nsIContentViewer> NS_NewContentViewer();
 nsresult NS_NewContentDocumentLoaderFactory(nsIDocumentLoaderFactory** aResult);
@@ -358,16 +359,17 @@ MAKE_CTOR(CreateNewLayoutDebugger,      
 #endif
 
 MAKE_CTOR(CreateNewFrameTraversal,      nsIFrameTraversal,      NS_CreateFrameTraversal)
 MAKE_CTOR(CreateNewBoxObject,           nsIBoxObject,           NS_NewBoxObject)
 
 #ifdef MOZ_XUL
 MAKE_CTOR(CreateNewListBoxObject,       nsIBoxObject,           NS_NewListBoxObject)
 MAKE_CTOR(CreateNewMenuBoxObject,       nsIBoxObject,           NS_NewMenuBoxObject)
+MAKE_CTOR(CreateNewScrollBoxObject,     nsIBoxObject,           NS_NewScrollBoxObject)
 MAKE_CTOR(CreateNewTreeBoxObject,       nsIBoxObject,           NS_NewTreeBoxObject)
 #endif // MOZ_XUL
 
 NS_GENERIC_FACTORY_CONSTRUCTOR(inDeepTreeWalker)
 
 MAKE_CTOR2(CreateContentViewer,           nsIContentViewer,            NS_NewContentViewer)
 MAKE_CTOR(CreateHTMLDocument,             nsIDocument,                 NS_NewHTMLDocument)
 MAKE_CTOR(CreateXMLDocument,              nsIDocument,                 NS_NewXMLDocument)
@@ -500,16 +502,17 @@ Construct_nsIScriptSecurityManager(nsISu
 #ifdef DEBUG
 NS_DEFINE_NAMED_CID(NS_LAYOUT_DEBUGGER_CID);
 #endif
 NS_DEFINE_NAMED_CID(NS_FRAMETRAVERSAL_CID);
 NS_DEFINE_NAMED_CID(NS_BOXOBJECT_CID);
 #ifdef MOZ_XUL
 NS_DEFINE_NAMED_CID(NS_LISTBOXOBJECT_CID);
 NS_DEFINE_NAMED_CID(NS_MENUBOXOBJECT_CID);
+NS_DEFINE_NAMED_CID(NS_SCROLLBOXOBJECT_CID);
 NS_DEFINE_NAMED_CID(NS_TREEBOXOBJECT_CID);
 #endif // MOZ_XUL
 NS_DEFINE_NAMED_CID(IN_DEEPTREEWALKER_CID);
 NS_DEFINE_NAMED_CID(NS_CONTENT_VIEWER_CID);
 NS_DEFINE_NAMED_CID(NS_HTMLDOCUMENT_CID);
 NS_DEFINE_NAMED_CID(NS_XMLDOCUMENT_CID);
 NS_DEFINE_NAMED_CID(NS_SVGDOCUMENT_CID);
 NS_DEFINE_NAMED_CID(NS_IMAGEDOCUMENT_CID);
@@ -741,16 +744,17 @@ static const mozilla::Module::CIDEntry k
 #ifdef DEBUG
   { &kNS_LAYOUT_DEBUGGER_CID, false, nullptr, CreateNewLayoutDebugger },
 #endif
   { &kNS_FRAMETRAVERSAL_CID, false, nullptr, CreateNewFrameTraversal },
   { &kNS_BOXOBJECT_CID, false, nullptr, CreateNewBoxObject },
 #ifdef MOZ_XUL
   { &kNS_LISTBOXOBJECT_CID, false, nullptr, CreateNewListBoxObject },
   { &kNS_MENUBOXOBJECT_CID, false, nullptr, CreateNewMenuBoxObject },
+  { &kNS_SCROLLBOXOBJECT_CID, false, nullptr, CreateNewScrollBoxObject },
   { &kNS_TREEBOXOBJECT_CID, false, nullptr, CreateNewTreeBoxObject },
 #endif // MOZ_XUL
   { &kIN_DEEPTREEWALKER_CID, false, nullptr, inDeepTreeWalkerConstructor },
   { &kNS_CONTENT_VIEWER_CID, false, nullptr, CreateContentViewer },
   { &kNS_HTMLDOCUMENT_CID, false, nullptr, CreateHTMLDocument },
   { &kNS_XMLDOCUMENT_CID, false, nullptr, CreateXMLDocument },
   { &kNS_SVGDOCUMENT_CID, false, nullptr, CreateSVGDocument },
   { &kNS_IMAGEDOCUMENT_CID, false, nullptr, CreateImageDocument },
@@ -848,16 +852,17 @@ static const mozilla::Module::CIDEntry k
 };
 
 static const mozilla::Module::ContractIDEntry kLayoutContracts[] = {
   XPCONNECT_CONTRACTS
   { "@mozilla.org/layout/xul-boxobject;1", &kNS_BOXOBJECT_CID },
 #ifdef MOZ_XUL
   { "@mozilla.org/layout/xul-boxobject-listbox;1", &kNS_LISTBOXOBJECT_CID },
   { "@mozilla.org/layout/xul-boxobject-menu;1", &kNS_MENUBOXOBJECT_CID },
+  { "@mozilla.org/layout/xul-boxobject-scrollbox;1", &kNS_SCROLLBOXOBJECT_CID },
   { "@mozilla.org/layout/xul-boxobject-tree;1", &kNS_TREEBOXOBJECT_CID },
 #endif // MOZ_XUL
   { "@mozilla.org/inspector/deep-tree-walker;1", &kIN_DEEPTREEWALKER_CID },
   { "@mozilla.org/xml/xml-document;1", &kNS_XMLDOCUMENT_CID },
   { "@mozilla.org/svg/svg-document;1", &kNS_SVGDOCUMENT_CID },
   { "@mozilla.org/content/post-content-iterator;1", &kNS_CONTENTITERATOR_CID },
   { "@mozilla.org/content/pre-content-iterator;1", &kNS_PRECONTENTITERATOR_CID },
   { "@mozilla.org/content/subtree-content-iterator;1", &kNS_SUBTREEITERATOR_CID },
--- a/layout/reftests/invalidation/540247-1-ref.xul
+++ b/layout/reftests/invalidation/540247-1-ref.xul
@@ -4,18 +4,18 @@
         xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
 
 <script><![CDATA[
 document.addEventListener("MozReftestInvalidate", runtest, false);
 
 function runtest() {
   document.getElementById('b').height = '100';
 
-  var scrollbox = document.getElementById('s');
-  scrollbox.scrollTo(0, 1000);
+  var sbo = document.getElementById('s').boxObject;
+  sbo.scrollTo(0, 1000);
 
   document.documentElement.className = "";
 }
 ]]>
 </script>
 
   <scrollbox id="s" height="200" style="overflow: scroll;">
     <vbox>
--- a/layout/reftests/invalidation/540247-1.xul
+++ b/layout/reftests/invalidation/540247-1.xul
@@ -5,18 +5,18 @@
 
 <script><![CDATA[
 document.addEventListener("MozReftestInvalidate", runtest, false);
 
 function runtest() {
   // Make sure that the effects of the scroll are painted to the screen before
   // shrinking the size of the scrolled frame.
   window.addEventListener("MozAfterPaint", finish, false);
-  var scrollbox = document.getElementById('s');
-  scrollbox.scrollTo(0, 1000);
+  var sbo = document.getElementById('s').boxObject;
+  sbo.scrollTo(0, 1000);
 }
 
 function finish() {
   document.getElementById('b').height = '100';
   document.documentElement.className = "";
 }
 ]]>
 </script>
rename from dom/xul/XULScrollElement.cpp
rename to layout/xul/ScrollBoxObject.cpp
--- a/dom/xul/XULScrollElement.cpp
+++ b/layout/xul/ScrollBoxObject.cpp
@@ -1,200 +1,300 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+#include "mozilla/dom/ScrollBoxObject.h"
+#include "mozilla/dom/ScrollBoxObjectBinding.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/ToJSValue.h"
 #include "nsCOMPtr.h"
 #include "nsIPresShell.h"
 #include "nsIContent.h"
 #include "nsPresContext.h"
 #include "nsBox.h"
 #include "nsIScrollableFrame.h"
-#include "mozilla/dom/XULScrollElement.h"
-#include "mozilla/dom/XULScrollElementBinding.h"
 
 namespace mozilla {
 namespace dom {
 
-JSObject*
-XULScrollElement::WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto)
+ScrollBoxObject::ScrollBoxObject()
 {
-  return XULScrollElement_Binding::Wrap(aCx, this, aGivenProto);
+}
+
+ScrollBoxObject::~ScrollBoxObject()
+{
 }
 
-void
-XULScrollElement::ScrollByIndex(int32_t aIndex, ErrorResult& aRv)
+JSObject* ScrollBoxObject::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
+{
+  return ScrollBoxObject_Binding::Wrap(aCx, this, aGivenProto);
+}
+
+nsIScrollableFrame* ScrollBoxObject::GetScrollFrame()
+{
+  return do_QueryFrame(GetFrame(false));
+}
+
+void ScrollBoxObject::ScrollTo(int32_t x, int32_t y, ErrorResult& aRv)
 {
   nsIScrollableFrame* sf = GetScrollFrame();
   if (!sf) {
     aRv.Throw(NS_ERROR_FAILURE);
     return;
   }
 
-  nsIFrame* scrolledFrame = sf->GetScrolledFrame();
-  if (!scrolledFrame){
-    aRv.Throw(NS_ERROR_FAILURE);
-    return;
-  }
+  sf->ScrollToCSSPixels(CSSIntPoint(x, y));
+}
 
-  nsIFrame* scrolledBox = nsBox::GetChildXULBox(scrolledFrame);
-  if (!scrolledBox){
-    aRv.Throw(NS_ERROR_FAILURE);
+void ScrollBoxObject::ScrollBy(int32_t dx, int32_t dy, ErrorResult& aRv)
+{
+  CSSIntPoint pt;
+  GetPosition(pt, aRv);
+
+  if (aRv.Failed()) {
     return;
   }
 
-  nsRect rect;
-
-  // now get the element's first child
-  nsIFrame* child = nsBox::GetChildXULBox(scrolledBox);
-
-  bool horiz = scrolledBox->IsXULHorizontal();
-  nsPoint cp = sf->GetScrollPosition();
-  nscoord diff = 0;
-  int32_t curIndex = 0;
-  bool isLTR = scrolledBox->IsXULNormalDirection();
+  ScrollTo(pt.x + dx, pt.y + dy, aRv);
+}
 
-  nscoord frameWidth = 0;
-  if (!isLTR && horiz) {
-    nsCOMPtr<nsIDocument> doc = GetComposedDoc();
-
-    nsCOMPtr<nsIPresShell> shell = doc->GetShell();
-    if (!shell) {
-      aRv.Throw(NS_ERROR_UNEXPECTED);
-      return;
-    }
-    nsRect rcFrame = nsLayoutUtils::GetAllInFlowRectsUnion(GetPrimaryFrame(), shell->GetRootFrame());
-    frameWidth = rcFrame.width;
+// XUL <scrollbox> elements have a single box child element.
+// Get a pointer to that box.
+// Note that now that the <scrollbox> is just a regular box
+// with 'overflow:hidden', the boxobject's frame is an nsXULScrollFrame,
+// the <scrollbox>'s box frame is the scrollframe's "scrolled frame", and
+// the <scrollbox>'s child box is a child of that.
+static nsIFrame* GetScrolledBox(BoxObject* aScrollBox) {
+  nsIFrame* frame = aScrollBox->GetFrame(false);
+  if (!frame) {
+    return nullptr;
   }
 
-  // first find out what index we are currently at
-  while(child) {
-    rect = child->GetRect();
-    if (horiz) {
-      // In the left-to-right case we break from the loop when the center of
-      // the current child rect is greater than the scrolled position of
-      // the left edge of the scrollbox
-      // In the right-to-left case we break when the center of the current
-      // child rect is less than the scrolled position of the right edge of
-      // the scrollelement.
-      diff = rect.x + rect.width/2; // use the center, to avoid rounding errors
-      if ((isLTR && diff > cp.x) ||
-          (!isLTR && diff < cp.x + frameWidth)) {
-        break;
-      }
-    } else {
-      diff = rect.y + rect.height/2;// use the center, to avoid rounding errors
-      if (diff > cp.y) {
-        break;
-      }
-    }
-    child = nsBox::GetNextXULBox(child);
-    curIndex++;
+  nsIScrollableFrame* scrollFrame = do_QueryFrame(frame);
+  if (!scrollFrame) {
+    NS_WARNING("ScrollBoxObject attached to something that's not a scroll frame!");
+    return nullptr;
   }
 
-  int32_t count = 0;
-
-  if (aIndex == 0)
-    return;
+  nsIFrame* scrolledFrame = scrollFrame->GetScrolledFrame();
+  if (!scrolledFrame)
+    return nullptr;
+  return nsBox::GetChildXULBox(scrolledFrame);
+}
 
-  if (aIndex > 0) {
-    while(child) {
-      child = nsBox::GetNextXULBox(child);
-      if (child) {
-        rect = child->GetRect();
-      }
-      count++;
-      if (count >= aIndex) {
-        break;
-      }
+void ScrollBoxObject::ScrollByIndex(int32_t dindexes, ErrorResult& aRv)
+{
+    nsIScrollableFrame* sf = GetScrollFrame();
+    if (!sf) {
+      aRv.Throw(NS_ERROR_FAILURE);
+      return;
+    }
+
+    nsIFrame* scrolledBox = GetScrolledBox(this);
+    if (!scrolledBox) {
+      aRv.Throw(NS_ERROR_FAILURE);
+      return;
     }
 
-  } else if (aIndex < 0) {
-    child = nsBox::GetChildXULBox(scrolledBox);
+    nsRect rect;
+
+    // now get the scrolled boxes first child.
+    nsIFrame* child = nsBox::GetChildXULBox(scrolledBox);
+
+    bool horiz = scrolledBox->IsXULHorizontal();
+    nsPoint cp = sf->GetScrollPosition();
+    nscoord diff = 0;
+    int32_t curIndex = 0;
+    bool isLTR = scrolledBox->IsXULNormalDirection();
+
+    int32_t frameWidth = 0;
+    if (!isLTR && horiz) {
+      GetWidth(&frameWidth);
+      nsCOMPtr<nsIPresShell> shell = GetPresShell(false);
+      if (!shell) {
+        aRv.Throw(NS_ERROR_UNEXPECTED);
+        return;
+      }
+      frameWidth = nsPresContext::CSSPixelsToAppUnits(frameWidth);
+    }
+
+    // first find out what index we are currently at
     while(child) {
       rect = child->GetRect();
-      if (count >= curIndex + aIndex) {
-        break;
+      if (horiz) {
+        // In the left-to-right case we break from the loop when the center of
+        // the current child rect is greater than the scrolled position of
+        // the left edge of the scrollbox
+        // In the right-to-left case we break when the center of the current
+        // child rect is less than the scrolled position of the right edge of
+        // the scrollbox.
+        diff = rect.x + rect.width/2; // use the center, to avoid rounding errors
+        if ((isLTR && diff > cp.x) ||
+            (!isLTR && diff < cp.x + frameWidth)) {
+          break;
+        }
+      } else {
+        diff = rect.y + rect.height/2;// use the center, to avoid rounding errors
+        if (diff > cp.y) {
+          break;
+        }
       }
-      count++;
       child = nsBox::GetNextXULBox(child);
-
+      curIndex++;
     }
-  }
+
+    int32_t count = 0;
 
-  nscoord csspixel = nsPresContext::CSSPixelsToAppUnits(1);
-  if (horiz) {
-    // In the left-to-right case we scroll so that the left edge of the
-    // selected child is scrolled to the left edge of the scrollbox.
-    // In the right-to-left case we scroll so that the right edge of the
-    // selected child is scrolled to the right edge of the scrollbox.
+    if (dindexes == 0)
+      return;
 
-    nsPoint pt(isLTR ? rect.x : rect.x + rect.width - frameWidth,
-               cp.y);
+    if (dindexes > 0) {
+      while(child) {
+        child = nsBox::GetNextXULBox(child);
+        if (child) {
+          rect = child->GetRect();
+        }
+        count++;
+        if (count >= dindexes) {
+          break;
+        }
+      }
+
+   } else if (dindexes < 0) {
+      child = nsBox::GetChildXULBox(scrolledBox);
+      while(child) {
+        rect = child->GetRect();
+        if (count >= curIndex + dindexes) {
+          break;
+        }
+
+        count++;
+        child = nsBox::GetNextXULBox(child);
 
-    // Use a destination range that ensures the left edge (or right edge,
-    // for RTL) will indeed be visible. Also ensure that the top edge
-    // is visible.
-    nsRect range(pt.x, pt.y, csspixel, 0);
-    if (isLTR) {
-      range.x -= csspixel;
-    }
-    sf->ScrollTo(pt, nsIScrollableFrame::INSTANT, &range);
-  } else {
-    // Use a destination range that ensures the top edge will be visible.
-    nsRect range(cp.x, rect.y - csspixel, 0, csspixel);
-    sf->ScrollTo(nsPoint(cp.x, rect.y), nsIScrollableFrame::INSTANT, &range);
-  }
+      }
+   }
+
+   nscoord csspixel = nsPresContext::CSSPixelsToAppUnits(1);
+   if (horiz) {
+       // In the left-to-right case we scroll so that the left edge of the
+       // selected child is scrolled to the left edge of the scrollbox.
+       // In the right-to-left case we scroll so that the right edge of the
+       // selected child is scrolled to the right edge of the scrollbox.
+
+       nsPoint pt(isLTR ? rect.x : rect.x + rect.width - frameWidth,
+                  cp.y);
+
+       // Use a destination range that ensures the left edge (or right edge,
+       // for RTL) will indeed be visible. Also ensure that the top edge
+       // is visible.
+       nsRect range(pt.x, pt.y, csspixel, 0);
+       if (isLTR) {
+         range.x -= csspixel;
+       }
+       sf->ScrollTo(pt, nsIScrollableFrame::INSTANT, &range);
+   } else {
+       // Use a destination range that ensures the top edge will be visible.
+       nsRect range(cp.x, rect.y - csspixel, 0, csspixel);
+       sf->ScrollTo(nsPoint(cp.x, rect.y), nsIScrollableFrame::INSTANT, &range);
+   }
 }
 
-void
-XULScrollElement::ScrollToElement(Element& child, ErrorResult& aRv)
+void ScrollBoxObject::ScrollToElement(Element& child, ErrorResult& aRv)
 {
-  nsCOMPtr<nsIDocument> doc = GetComposedDoc();
-  if (!doc){
-    aRv.Throw(NS_ERROR_FAILURE);
-  }
-
-  nsCOMPtr<nsIPresShell> shell = doc->GetShell();
+  nsCOMPtr<nsIPresShell> shell = GetPresShell(false);
   if (!shell) {
     aRv.Throw(NS_ERROR_UNEXPECTED);
     return;
   }
 
   shell->ScrollContentIntoView(&child,
                                nsIPresShell::ScrollAxis(
                                  nsIPresShell::SCROLL_TOP,
                                  nsIPresShell::SCROLL_ALWAYS),
                                nsIPresShell::ScrollAxis(
                                  nsIPresShell::SCROLL_LEFT,
                                  nsIPresShell::SCROLL_ALWAYS),
                                nsIPresShell::SCROLL_FIRST_ANCESTOR_ONLY |
                                nsIPresShell::SCROLL_OVERFLOW_HIDDEN);
 }
 
+int32_t ScrollBoxObject::GetPositionX(ErrorResult& aRv)
+{
+  CSSIntPoint pt;
+  GetPosition(pt, aRv);
+  return pt.x;
+}
 
-void
-XULScrollElement::EnsureElementIsVisible(Element& aChild, ErrorResult& aRv)
+int32_t ScrollBoxObject::GetPositionY(ErrorResult& aRv)
+{
+  CSSIntPoint pt;
+  GetPosition(pt, aRv);
+  return pt.y;
+}
+
+int32_t ScrollBoxObject::GetScrolledWidth(ErrorResult& aRv)
 {
-  nsCOMPtr<nsIDocument> doc = GetComposedDoc();
-  if (!doc){
+  nsRect scrollRect;
+  GetScrolledSize(scrollRect, aRv);
+  return scrollRect.width;
+}
+
+int32_t ScrollBoxObject::GetScrolledHeight(ErrorResult& aRv)
+{
+  nsRect scrollRect;
+  GetScrolledSize(scrollRect, aRv);
+  return scrollRect.height;
+}
+
+/* private helper */
+void ScrollBoxObject::GetPosition(CSSIntPoint& aPos, ErrorResult& aRv)
+{
+  nsIScrollableFrame* sf = GetScrollFrame();
+  if (!sf) {
     aRv.Throw(NS_ERROR_FAILURE);
-  }
-
-  nsCOMPtr<nsIPresShell> shell = doc->GetShell();
-  if (!shell) {
-    aRv.Throw(NS_ERROR_UNEXPECTED);
     return;
   }
 
-  shell->ScrollContentIntoView(&aChild,
-                               nsIPresShell::ScrollAxis(),
-                               nsIPresShell::ScrollAxis(),
-                               nsIPresShell::SCROLL_FIRST_ANCESTOR_ONLY |
-                               nsIPresShell::SCROLL_OVERFLOW_HIDDEN);
+  aPos = sf->GetScrollPositionCSSPixels();
+}
+
+/* private helper */
+void ScrollBoxObject::GetScrolledSize(nsRect& aRect, ErrorResult& aRv)
+{
+    nsIFrame* scrolledBox = GetScrolledBox(this);
+    if (!scrolledBox) {
+      aRv.Throw(NS_ERROR_FAILURE);
+      return;
+    }
+
+    aRect = scrolledBox->GetRect();
+    aRect.width  = nsPresContext::AppUnitsToIntCSSPixels(aRect.width);
+    aRect.height = nsPresContext::AppUnitsToIntCSSPixels(aRect.height);
 }
 
+void ScrollBoxObject::EnsureElementIsVisible(Element& child, ErrorResult& aRv)
+{
+    nsCOMPtr<nsIPresShell> shell = GetPresShell(false);
+    if (!shell) {
+      aRv.Throw(NS_ERROR_UNEXPECTED);
+      return;
+    }
+
+    shell->ScrollContentIntoView(&child,
+                                 nsIPresShell::ScrollAxis(),
+                                 nsIPresShell::ScrollAxis(),
+                                 nsIPresShell::SCROLL_FIRST_ANCESTOR_ONLY |
+                                 nsIPresShell::SCROLL_OVERFLOW_HIDDEN);
+}
 } // namespace dom
 } // namespace mozilla
+
+using namespace mozilla::dom;
+
+nsresult
+NS_NewScrollBoxObject(nsIBoxObject** aResult)
+{
+    NS_ADDREF(*aResult = new ScrollBoxObject());
+    return NS_OK;
+}
rename from dom/xul/XULScrollElement.h
rename to layout/xul/ScrollBoxObject.h
--- a/dom/xul/XULScrollElement.h
+++ b/layout/xul/ScrollBoxObject.h
@@ -1,36 +1,50 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#ifndef XULScrollElement_h__
-#define XULScrollElement_h__
+#ifndef mozilla_dom_ScrollBoxObject_h
+#define mozilla_dom_ScrollBoxObject_h
 
-#include "nsXULElement.h"
+#include "mozilla/dom/BoxObject.h"
 #include "Units.h"
 
+class nsIScrollableFrame;
+struct nsRect;
+
 namespace mozilla {
 namespace dom {
 
-
-class XULScrollElement final : public nsXULElement
+class ScrollBoxObject final : public BoxObject
 {
 public:
-  explicit XULScrollElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo): nsXULElement(aNodeInfo)
-  {
-  }
+  ScrollBoxObject();
+
+  virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
+
+  virtual nsIScrollableFrame* GetScrollFrame();
 
-  MOZ_CAN_RUN_SCRIPT void ScrollByIndex(int32_t aIndex, ErrorResult& aRv);
-  MOZ_CAN_RUN_SCRIPT void EnsureElementIsVisible(Element& aChild, ErrorResult& aRv);
-  MOZ_CAN_RUN_SCRIPT void ScrollToElement(Element& child, ErrorResult& aRv);
+  void ScrollTo(int32_t x, int32_t y, ErrorResult& aRv);
+  void ScrollBy(int32_t dx, int32_t dy, ErrorResult& aRv);
+  void ScrollByIndex(int32_t dindexes, ErrorResult& aRv);
+  void ScrollToElement(Element& child, ErrorResult& aRv);
+  int32_t GetPositionX(ErrorResult& aRv);
+  int32_t GetPositionY(ErrorResult& aRv);
+  int32_t GetScrolledWidth(ErrorResult& aRv);
+  int32_t GetScrolledHeight(ErrorResult& aRv);
+  void EnsureElementIsVisible(Element& child, ErrorResult& aRv);
+
 
 protected:
-  virtual ~XULScrollElement() {}
-  JSObject* WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) final;
+  void GetScrolledSize(nsRect& aRect, ErrorResult& aRv);
+  void GetPosition(CSSIntPoint& aPos, ErrorResult& aRv);
+
+private:
+  ~ScrollBoxObject();
 };
 
 } // namespace dom
 } // namespace mozilla
 
-#endif // XULScrollElement_h
+#endif // mozilla_dom_ScrollBoxObject_h
--- a/layout/xul/moz.build
+++ b/layout/xul/moz.build
@@ -29,16 +29,17 @@ EXPORTS += [
     'nsPIListBoxObject.h',
     'nsXULPopupManager.h',
 ]
 
 EXPORTS.mozilla.dom += [
     'BoxObject.h',
     'ListBoxObject.h',
     'MenuBoxObject.h',
+    'ScrollBoxObject.h',
 ]
 
 UNIFIED_SOURCES += [
     'BoxObject.cpp',
     'nsBox.cpp',
     'nsBoxFrame.cpp',
     'nsBoxLayout.cpp',
     'nsBoxLayoutState.cpp',
@@ -73,16 +74,17 @@ if CONFIG['MOZ_XUL']:
         'nsPopupSetFrame.cpp',
         'nsProgressMeterFrame.cpp',
         'nsResizerFrame.cpp',
         'nsSplitterFrame.cpp',
         'nsTextBoxFrame.cpp',
         'nsTitleBarFrame.cpp',
         'nsXULLabelFrame.cpp',
         'nsXULPopupManager.cpp',
+        'ScrollBoxObject.cpp',
     ]
 
 if CONFIG['MOZ_XUL']:
     DIRS += ['tree', 'grid']
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
 FINAL_LIBRARY = 'xul'
--- a/testing/mochitest/browser-harness.xul
+++ b/testing/mochitest/browser-harness.xul
@@ -319,17 +319,18 @@
       setStatus("Done.");
     }
 
     function scrollTo(id) {
       var line = document.getElementById(id);
       if (!line)
         return;
 
-      line.scrollIntoView();
+      var boxObject = document.getElementById("results").parentNode.boxObject;
+      boxObject.scrollToElement(line);
     }
   ]]></script>
   <button id="runTestsButton" oncommand="runTests();" label="Run All Tests"/>
   <label id="status"/>
   <scrollbox flex="1" style="overflow: auto" align="stretch">
     <div id="results" xmlns="http://www.w3.org/1999/xhtml" flex="1"/>
   </scrollbox>
 </window>
--- a/toolkit/content/tests/chrome/test_mousescroll.xul
+++ b/toolkit/content/tests/chrome/test_mousescroll.xul
@@ -200,33 +200,34 @@ function* testRichListbox(id)
     yield* helper(5, -delta,  0, deltaModes[i]);
     yield* helper(5,  delta,  1, deltaModes[i]);
     yield* helper(5,  delta,  0, deltaModes[i]);
   }
 }
 
 function* testArrowScrollbox(id)
 {
-  var scrollbox = document.getElementById(id)._scrollbox;
+  var scrollbox = document.getElementById(id);
+  var scrollBoxObject = scrollbox.scrollBoxObject;
   var orient = scrollbox.getAttribute("orient");
 
   function* helper(aStart, aDelta, aDeltaMode, aExpected)
   {
     var lineOrPageDelta = (aDeltaMode == WheelEvent.DOM_DELTA_PIXEL) ? aDelta / 10 : aDelta;
     var orientIsHorizontal = (orient == "horizontal");
 
-    scrollbox.scrollTo(aStart, aStart);
+    scrollBoxObject.scrollTo(aStart, aStart);
     for (var i = orientIsHorizontal ? 2 : 0; i >= 0; i--) {
       // Note, vertical mouse scrolling is allowed to scroll horizontal
       // arrowscrollboxes, because many users have no horizontal mouse scroll
       // capability
       let expected = !i ? aExpected : aStart;
       let getPos = ()=>{
-        return orientIsHorizontal ? scrollbox.scrollLeft :
-                                    scrollbox.scrollTop;
+        return orientIsHorizontal ? scrollBoxObject.positionX :
+                                    scrollBoxObject.positionY;
       };
       let oldPos = -1;
       let retry = 0;
       yield sendWheelAndWait(scrollbox, 5, 5,
                              { deltaMode: aDeltaMode, deltaY: aDelta,
                                lineOrPageDeltaY: lineOrPageDelta },
                              ()=>{
                                 if (getPos() == expected) {
@@ -259,24 +260,24 @@ function* testArrowScrollbox(id)
       } else {
         is(getPos(), expected,
            "testArrowScrollbox(" + id +  "): vertical, starting " + aStart +
              " delta " + aDelta + " lineOrPageDelta " + lineOrPageDelta +
              " aDeltaMode " + aDeltaMode);
       }
     }
 
-    scrollbox.scrollTo(aStart, aStart);
+    scrollBoxObject.scrollTo(aStart, aStart);
     for (var i = orientIsHorizontal ? 2 : 0; i >= 0; i--) {
       // horizontal mouse scrolling is never allowed to scroll vertical
       // arrowscrollboxes
       let expected = (!i && orientIsHorizontal) ? aExpected : aStart;
       let getPos = ()=>{
-        return orientIsHorizontal ? scrollbox.scrollLeft :
-                                    scrollbox.scrollTop;
+        return orientIsHorizontal ? scrollBoxObject.positionX :
+                                    scrollBoxObject.positionY;
       };
       let oldPos = -1;
       let retry = 0;
       yield sendWheelAndWait(scrollbox, 5, 5,
                              { deltaMode: aDeltaMode, deltaX: aDelta,
                                lineOrPageDeltaX: lineOrPageDelta },
                              ()=>{
                                 if (getPos() == expected) {
@@ -310,20 +311,20 @@ function* testArrowScrollbox(id)
         is(getPos(), expected,
            "testArrowScrollbox(" + id +  "): horizontal, starting " + aStart +
              " delta " + aDelta + " lineOrPageDelta " + lineOrPageDelta +
              " aDeltaMode " + aDeltaMode);
       }
     }
   }
 
-  var scrolledWidth = scrollbox.scrollWidth;
-  var scrolledHeight = scrollbox.scrollHeight;
-  var scrollMaxX = scrolledWidth - scrollbox.boxObject.width;
-  var scrollMaxY = scrolledHeight - scrollbox.boxObject.height;
+  var scrolledWidth = scrollBoxObject.scrolledWidth;
+  var scrolledHeight = scrollBoxObject.scrolledHeight;
+  var scrollMaxX = scrolledWidth - scrollBoxObject.width;
+  var scrollMaxY = scrolledHeight - scrollBoxObject.height;
   var scrollMax = orient == "horizontal" ? scrollMaxX : scrollMaxY;
 
   for (let i = 0; i < deltaModes.length; i++) {
     yield* helper(50, -1000, deltaModes[i], 0);
     yield* helper(50,  1000, deltaModes[i], scrollMax);
   }
 }
 
--- a/toolkit/content/tests/chrome/test_position.xul
+++ b/toolkit/content/tests/chrome/test_position.xul
@@ -70,23 +70,25 @@ function runTest()
 
   var box2 = $("box2");
   checkPosition("box2", box2, 2, 46, winwidth, 96);
 
   // height is height(box1) = 46 + margin-top(box3) = 1 + margin-top(button1) = 5
   var button1 = $("button1");
   checkPosition("button1", button1, 9, 53, 99, 88);
 
-  box2.scrollTo(7, 16);
+  var sbo = box2.boxObject;
+  sbo.scrollTo(7, 16);
 
   // clientRect height is offset from root so is 16 pixels vertically less
   checkPosition("button1 scrolled", button1, 9, 37, 99, 72);
 
   var box3 = $("box3");
-  box3.scrollTo(1, 2);
+  sbo = box3.boxObject;
+  sbo.scrollTo(1, 2);
 
   checkPosition("button1 scrolled", button1, 9, 35, 99, 70);
 
   $("menu").open = true;
 }
 
 function menuOpened()
 {
--- a/toolkit/content/tests/chrome/test_richlist_direction.xul
+++ b/toolkit/content/tests/chrome/test_richlist_direction.xul
@@ -26,18 +26,18 @@ var richListBox = document.getElementByI
 
 function getScrollIndexAmount(aDirection) {
   return (4 * aDirection + richListBox.currentIndex);
 }
 
 function test_richlistbox()
 {
   richListBox.minHeight = richListBox.maxHeight = richListBox.height =
-    80 + (80 - richListBox._scrollbox.clientHeight);
-  var height = richListBox._scrollbox.clientHeight;
+    80 + (80 - richListBox.scrollBoxObject.height);
+  var height = richListBox.scrollBoxObject.height;
   var item;
   do {
     item = richListBox.appendItem("Test", "");
     item.height = item.minHeight = item.maxHeight = Math.floor(height / 4);
   } while (item.getBoundingClientRect().bottom < (height * 2))
   richListBox.appendItem("Test", "");
   richListBox.firstChild.nextSibling.id = "list-box-first";
   richListBox.lastChild.previousSibling.id = "list-box-last";
--- a/toolkit/content/tests/chrome/window_largemenu.xul
+++ b/toolkit/content/tests/chrome/window_largemenu.xul
@@ -98,17 +98,17 @@ function popupShown()
   if (gTests[gTestIndex] == "menu movement")
     return testPopupMovement();
 
   if (gContextMenuTests)
     return contextMenuPopupShown();
 
   var popup = document.getElementById("popup");
   var rect = popup.getBoundingClientRect();
-  var scrollbox = document.getAnonymousNodes(popup)[0]._scrollbox;
+  var sbo = document.getAnonymousNodes(popup)[0].scrollBoxObject;
   var expectedScrollPos = 0;
 
   if (gTestIndex == 0) {
     // the popup should be in the center of the screen
     // note that if the height is odd, the y-offset will have been rounded
     // down when we pass the fractional value to openPopupAtScreen above.
     is(Math.round(rect.top) + gScreenY, Math.floor(screen.height / 2),
                               gTests[gTestIndex] + " top");
@@ -136,33 +136,33 @@ function popupShown()
     }
   }
   else if (gTestIndex == 2) {
     // the popup is too large so ensure that it is on screen
     ok(Math.round(rect.top) + gScreenY >= screen.top, gTests[gTestIndex] + " top");
     ok(Math.round(rect.bottom) + gScreenY <= screen.height, gTests[gTestIndex] + " bottom");
     ok(gOverflowed && !gUnderflowed, gTests[gTestIndex] + " overflow")
 
-    scrollbox.scrollTo(0, 40);
+    sbo.scrollTo(0, 40);
     expectedScrollPos = 40;
   }
   else if (gTestIndex == 3) {
     expectedScrollPos = 40;
   }
   else if (gTestIndex == 4) {
     // note that if the height is odd, the y-offset will have been rounded
     // down when we pass the fractional value to openPopupAtScreen above.
     is(Math.round(rect.top) + gScreenY, Math.floor(screen.height / 2),
                               gTests[gTestIndex] + " top");
     ok(Math.round(rect.bottom) + gScreenY < screen.height,
                                 gTests[gTestIndex] + " bottom");
     ok(!gOverflowed && gUnderflowed, gTests[gTestIndex] + " overflow");
   }
 
-  is(scrollbox.scrollTop, expectedScrollPos, "menu scroll position")
+  is(sbo.positionY, expectedScrollPos, "menu scroll position");
 
   hidePopup();
 }
 
 function is(l, r, n) { window.opener.wrappedJSObject.SimpleTest.is(l,r,n); }
 function ok(v, n) { window.opener.wrappedJSObject.SimpleTest.ok(v,n); }
 
 var oldx, oldy, waitSteps = 0;
--- a/toolkit/content/widgets/richlistbox.xml
+++ b/toolkit/content/widgets/richlistbox.xml
@@ -18,16 +18,19 @@
         <children/>
       </xul:scrollbox>
     </content>
 
     <implementation>
       <field name="_scrollbox">
         document.getAnonymousElementByAttribute(this, "anonid", "main-box");
       </field>
+      <field name="scrollBoxObject">
+        this._scrollbox.boxObject;
+      </field>
       <constructor>
         <![CDATA[
           // add a template build listener
           if (this.builder)
             this.builder.addListener(this._builderListener);
           else
             this._refreshSelection();
         ]]>
@@ -91,18 +94,17 @@
                 Ci.nsIDOMXULSelectControlItemElement &&
                 (!this._userSelecting || this._canUserSelect(aStartItem))) {
               --aDelta;
               if (aDelta == 0)
                 return aStartItem;
             }
           }
           return null;
-        ]]>
-        </body>
+        ]]></body>
       </method>
 
       <method name="getPreviousItem">
         <parameter name="aStartItem"/>
         <parameter name="aDelta"/>
         <body>
         <![CDATA[
           var prop = this.dir == "reverse" && this._mayReverse ?
@@ -173,16 +175,17 @@
           ]]>
         </body>
       </method>
 
       <method name="ensureIndexIsVisible">
         <parameter name="aIndex"/>
         <body>
           <![CDATA[
+            // work around missing implementation in scrollBoxObject
             return this.ensureElementIsVisible(this.getItemAtIndex(aIndex));
           ]]>
         </body>
       </method>
 
       <method name="ensureElementIsVisible">
         <parameter name="aElement"/>
         <body>
@@ -205,17 +208,17 @@
       </method>
 
       <method name="scrollToIndex">
         <parameter name="aIndex"/>
         <body>
           <![CDATA[
             var item = this.getItemAtIndex(aIndex);
             if (item)
-              this._scrollbox.scrollToElement(item);
+              this.scrollBoxObject.scrollToElement(item);
           ]]>
         </body>
       </method>
 
       <method name="getNumberOfVisibleRows">
         <!-- returns the number of currently visible rows                -->
         <!-- don't rely on this function, if the items' height can vary! -->
         <body>
@@ -265,22 +268,22 @@
             // at the extreme we're moving away from
             if (!this.currentItem)
               return aDirection == -1 ? children.length : 0;
 
             // If the current item is visible, scroll by one page so that
             // the new current item is at approximately the same position as
             // the existing current item.
             if (this._isItemVisible(this.currentItem))
-              this._scrollbox.scrollBy(0, this._scrollbox.boxObject.height * aDirection);
+              this.scrollBoxObject.scrollBy(0, this.scrollBoxObject.height * aDirection);
 
             // Figure out, how many items fully fit into the view port
             // (including the currently selected one), and determine
             // the index of the first one lying (partially) outside
-            var height = this._scrollbox.boxObject.height;
+            var height = this.scrollBoxObject.height;
             var startBorder = this.currentItem.boxObject.y;
             if (aDirection == -1)
               startBorder += this.currentItem.boxObject.height;
 
             var index = this.currentIndex;
             for (var ix = index; 0 <= ix && ix < children.length; ix += aDirection) {
               var boxObject = children[ix].boxObject;
               if (boxObject.height == 0)
@@ -349,17 +352,17 @@
               if (!currentItem && this._currentIndex)
                 currentItem = this.getItemAtIndex(Math.min(
                   this._currentIndex - 1, this.getRowCount()));
               if (currentItem) {
                 this.currentItem = currentItem;
                 if (this.selType != "multiple" && this.selectedCount == 0)
                   this.selectedItem = currentItem;
 
-                if (this._scrollbox.boxObject.height) {
+                if (this.scrollBoxObject.height) {
                   this.ensureElementIsVisible(currentItem);
                 } else {
                   // XXX hack around a bug in ensureElementIsVisible as it will
                   // scroll beyond the last element, bug 493645.
                   var previousElement = this.dir == "reverse" ? currentItem.nextSibling :
                                                                 currentItem.previousSibling;
                   this.ensureElementIsVisible(previousElement);
                 }
@@ -414,21 +417,21 @@
 
       <method name="_isItemVisible">
         <parameter name="aItem"/>
         <body>
           <![CDATA[
             if (!aItem)
               return false;
 
-            var y = this._scrollbox.scrollTop + this._scrollbox.boxObject.y;
+            var y = this.scrollBoxObject.positionY + this.scrollBoxObject.y;
 
             // Partially visible items are also considered visible
             return (aItem.boxObject.y + aItem.boxObject.height > y) &&
-                   (aItem.boxObject.y < y + this._scrollbox.boxObject.height);
+                   (aItem.boxObject.y < y + this.scrollBoxObject.height);
           ]]>
         </body>
       </method>
 
       <field name="_currentIndex">null</field>
 
       <!-- For backwards-compatibility and for convenience.
         Use getIndexOfItem instead. -->
--- a/toolkit/content/widgets/scrollbox.xml
+++ b/toolkit/content/widgets/scrollbox.xml
@@ -10,16 +10,25 @@
    xmlns:xbl="http://www.mozilla.org/xbl">
 
   <binding id="scrollbox" extends="chrome://global/content/bindings/general.xml#basecontrol">
     <content>
       <xul:box class="box-inherit scrollbox-innerbox" xbl:inherits="orient,align,pack,dir" flex="1">
         <children/>
       </xul:box>
     </content>
+
+    <implementation>
+      <method name="scrollByIndex">
+        <parameter name="index"/>
+        <body>
+          this.boxObject.scrollByIndex(index);
+        </body>
+      </method>
+    </implementation>
   </binding>
 
   <binding id="arrowscrollbox" extends="chrome://global/content/bindings/general.xml#basecontrol">
     <resources>
       <stylesheet src="chrome://global/skin/scrollbox.css"/>
     </resources>
 
     <content>
@@ -101,16 +110,26 @@
           return this.getAttribute("smoothscroll") == "true";
         ]]></getter>
         <setter><![CDATA[
           this.setAttribute("smoothscroll", !!val);
           return val;
         ]]></setter>
       </property>
 
+      <field name="_scrollBoxObject">null</field>
+      <property name="scrollBoxObject" readonly="true">
+        <getter><![CDATA[
+          if (!this._scrollBoxObject) {
+            this._scrollBoxObject = this._scrollbox.boxObject;
+          }
+          return this._scrollBoxObject;
+        ]]></getter>
+      </property>
+
       <property name="scrollClientRect" readonly="true">
         <getter><![CDATA[
           return this._scrollbox.getBoundingClientRect();
         ]]></getter>
       </property>
 
       <property name="scrollClientSize" readonly="true">
         <getter><![CDATA[