merge mozilla-inbound to mozilla-central a=merge
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Mon, 04 Apr 2016 11:55:29 +0200
changeset 291476 9bd90088875399347b05d87c67d3709e31539dcd
parent 291475 fc4a988ca8d61e809bb30ef635ad50e189f742b3 (current diff)
parent 291469 30aaf3805b5682774b3053528c3e675c73967b0b (diff)
child 291477 ce5563c23140345450d63a68dced497d0f5c14e5
child 291581 784b84d126d848ef43e8495f52279b994c0d3e0d
child 291696 e99061fde28a93d29e0032903d56cc6bec451557
push id74584
push usercbook@mozilla.com
push dateMon, 04 Apr 2016 10:02:26 +0000
treeherdermozilla-inbound@ce5563c23140 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone48.0a1
first release with
nightly linux32
9bd900888753 / 48.0a1 / 20160404030231 / files
nightly linux64
9bd900888753 / 48.0a1 / 20160404030231 / files
nightly mac
9bd900888753 / 48.0a1 / 20160404030231 / files
nightly win32
9bd900888753 / 48.0a1 / 20160404030231 / files
nightly win64
9bd900888753 / 48.0a1 / 20160404030231 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
merge mozilla-inbound to mozilla-central a=merge
--- a/dom/animation/AnimationEffectTiming.cpp
+++ b/dom/animation/AnimationEffectTiming.cpp
@@ -26,17 +26,23 @@ PostSpecifiedTimingUpdated(KeyframeEffec
   if (aEffect) {
     aEffect->NotifySpecifiedTimingUpdated();
   }
 }
 
 void
 AnimationEffectTiming::SetDelay(double aDelay)
 {
-  // TODO: Bug 1244633 - implement AnimationEffectTiming delay
+  TimeDuration delay = TimeDuration::FromMilliseconds(aDelay);
+  if (mTiming.mDelay == delay) {
+    return;
+  }
+  mTiming.mDelay = delay;
+
+  PostSpecifiedTimingUpdated(mEffect);
 }
 
 void
 AnimationEffectTiming::SetEndDelay(double aEndDelay)
 {
   TimeDuration endDelay = TimeDuration::FromMilliseconds(aEndDelay);
   if (mTiming.mEndDelay == endDelay) {
     return;
--- a/dom/animation/test/chrome/test_animation_observers.html
+++ b/dom/animation/test/chrome/test_animation_observers.html
@@ -1631,16 +1631,57 @@ addAsyncAnimTest("change_iterations",
                  "records after animation restarted");
 
   anim.cancel();
   yield await_frame();
   assert_records([{ added: [], changed: [], removed: [anim] }],
                  "records after animation end");
 });
 
+addAsyncAnimTest("change_delay",
+                 { observe: div, subtree: true }, function*() {
+  var anim = div.animate({ opacity: [ 0, 1 ] }, 100000);
+
+  yield await_frame();
+  assert_records([{ added: [anim], changed: [], removed: [] }],
+                 "records after animation is added");
+
+  anim.effect.timing.delay = 100;
+  yield await_frame();
+  assert_records([{ added: [], changed: [anim], removed: [] }],
+                 "records after delay is changed");
+
+  anim.effect.timing.delay = 100;
+  yield await_frame();
+  assert_records([], "records after assigning same value");
+
+  anim.effect.timing.delay = -100000;
+  yield await_frame();
+  assert_records([{ added: [], changed: [], removed: [anim] }],
+                 "records after animation end");
+
+  anim.effect.timing.delay = 0;
+  yield await_frame();
+  assert_records([{ added: [anim], changed: [], removed: [] }],
+                 "records after animation restarted");
+
+  anim.cancel();
+  yield await_frame();
+  assert_records([{ added: [], changed: [], removed: [anim] }],
+                 "records after animation end");
+});
+
+addAsyncAnimTest("negative_delay_in_constructor",
+                 { observe: div, subtree: true }, function*() {
+  var anim = div.animate({ opacity: [ 0, 1 ] },
+                         { duration: 100, delay: -100 });
+  yield await_frame();
+  assert_records([], "records after assigning negative value");
+});
+
 addAsyncAnimTest("exclude_animations_targeting_pseudo_elements",
                  { observe: div, subtree: false }, function*() {
   var anim = div.animate({ opacity: [ 0, 1 ] },
                          { duration: 100 * MS_PER_SEC });
   var pAnim = pseudoTarget.animate({ opacity: [ 0, 1 ] },
                                    { duration: 100 * MS_PER_SEC });
 
   yield await_frame();
--- a/dom/html/test/browser_content_contextmenu_userinput.js
+++ b/dom/html/test/browser_content_contextmenu_userinput.js
@@ -1,45 +1,18 @@
 "use strict";
 
-function frameScript() {
-  let Ci = Components.interfaces;
-  let windowUtils = content.QueryInterface(Ci.nsIInterfaceRequestor)
-                           .getInterface(Ci.nsIDOMWindowUtils);
-  let menuitem = content.document.getElementById("menuitem");
-  menuitem.addEventListener("click", function() {
-    sendAsyncMessage("Test:ContextMenuClick", windowUtils.isHandlingUserInput);
-  });
-}
-
-var gMessageManager;
-
-function listenOneMessage(aMsg, aListener) {
-  function listener({ data }) {
-    gMessageManager.removeMessageListener(aMsg, listener);
-    aListener(data);
-  }
-  gMessageManager.addMessageListener(aMsg, listener);
-}
-
-function promiseOneMessage(aMsg) {
-  return new Promise(resolve => listenOneMessage(aMsg, resolve));
-}
-
 const kPage = "http://example.org/browser/" +
               "dom/html/test/file_content_contextmenu.html";
 
 add_task(function* () {
   yield BrowserTestUtils.withNewTab({
     gBrowser,
     url: kPage
   }, function*(aBrowser) {
-    gMessageManager = aBrowser.messageManager;
-    ContentTask.spawn(aBrowser, null, frameScript);
-
     let contextMenu = document.getElementById("contentAreaContextMenu");
     ok(contextMenu, "Got context menu");
 
     info("Open context menu");
     is(contextMenu.state, "closed", "Should not have opened context menu");
     let popupShownPromise = promiseWaitForEvent(window, "popupshown");
     EventUtils.synthesizeMouse(aBrowser, window.innerWidth / 3,
                                window.innerHeight / 3,
@@ -48,14 +21,25 @@ add_task(function* () {
     is(contextMenu.state, "open", "Should have opened context menu");
 
     let pageMenuSep = document.getElementById("page-menu-separator");
     ok(pageMenuSep && !pageMenuSep.hidden,
        "Page menu separator should be shown");
     let testMenuItem = pageMenuSep.previousSibling;
     is(testMenuItem.label, "Test Context Menu Click", "Got context menu item");
 
-    let promiseContextMenuClick = promiseOneMessage("Test:ContextMenuClick");
+    let promiseCtxMenuClick = ContentTask.spawn(aBrowser, null, function*() {
+      yield new Promise(resolve => {
+        let Ci = Components.interfaces;
+        let windowUtils = content.QueryInterface(Ci.nsIInterfaceRequestor)
+                                 .getInterface(Ci.nsIDOMWindowUtils);
+        let menuitem = content.document.getElementById("menuitem");
+        menuitem.addEventListener("click", function() {
+          Assert.ok(windowUtils.isHandlingUserInput,
+                    "Content menu click should be a user input");
+          resolve();
+        });
+      });
+    });
     EventUtils.synthesizeMouseAtCenter(testMenuItem, {}, window);
-    let isUserInput = yield promiseContextMenuClick;
-    ok(isUserInput, "Content menu click should be a user input");
+    yield promiseCtxMenuClick;
   });
 });
--- a/layout/reftests/css-parsing/reftest.list
+++ b/layout/reftests/css-parsing/reftest.list
@@ -1,8 +1,9 @@
 == at-rule-013.html at-rule-013-ref.html
 == invalid-url-handling.xhtml invalid-url-handling-ref.xhtml
 == pseudo-elements-1.html pseudo-elements-1-ref.html
 == invalid-attr-1.html invalid-attr-1-ref.html
 == at-rule-error-handling-import-1.html at-rule-error-handling-ref.html
 == at-rule-error-handling-media-1.html at-rule-error-handling-ref.html
 == invalid-font-face-descriptor-1.html invalid-font-face-descriptor-1-ref.html
 == two-dash-identifiers.html two-dash-identifiers-ref.html
+== supports-moz-bool-pref.html supports-moz-bool-pref-ref.html
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-parsing/supports-moz-bool-pref-ref.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<!-- Any copyright is dedicated to the Public Domain.
+   - http://creativecommons.org/publicdomain/zero/1.0/ -->
+
+<html>
+  <body>
+    <p>This text should not have background color.</p>
+  </body>
+</html>
+
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-parsing/supports-moz-bool-pref.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<!-- Any copyright is dedicated to the Public Domain.
+   - http://creativecommons.org/publicdomain/zero/1.0/ -->
+
+<html>
+  <style>
+  /* This is not a user agent style sheet, so the style will be ignored.
+     "testing.supports.moz-bool-pref" is set to true in
+     layout/tools/reftest/reftest-preferences.js. */
+  @supports -moz-bool-pref("testing.supports.moz-bool-pref") {
+    p {
+      background-color: red;
+    }
+  }
+  </style>
+  <body>
+    <p>This text should not have background color.</p>
+  </body>
+</html>
+
--- a/layout/style/nsCSSParser.cpp
+++ b/layout/style/nsCSSParser.cpp
@@ -689,16 +689,17 @@ protected:
   bool ParseKeyframesRule(RuleAppendFunc aAppendFunc, void* aProcessData);
   already_AddRefed<nsCSSKeyframeRule> ParseKeyframeRule();
   bool ParseKeyframeSelectorList(InfallibleTArray<float>& aSelectorList);
 
   bool ParseSupportsRule(RuleAppendFunc aAppendFunc, void* aProcessData);
   bool ParseSupportsCondition(bool& aConditionMet);
   bool ParseSupportsConditionNegation(bool& aConditionMet);
   bool ParseSupportsConditionInParens(bool& aConditionMet);
+  bool ParseSupportsMozBoolPrefName(bool& aConditionMet);
   bool ParseSupportsConditionInParensInsideParens(bool& aConditionMet);
   bool ParseSupportsConditionTerms(bool& aConditionMet);
   enum SupportsConditionTermOperator { eAnd, eOr };
   bool ParseSupportsConditionTermsAfterOperator(
                                        bool& aConditionMet,
                                        SupportsConditionTermOperator aOperator);
 
   bool ParseCounterStyleRule(RuleAppendFunc aAppendFunc, void* aProcessData);
@@ -4521,31 +4522,38 @@ CSSParserImpl::ParseSupportsConditionNeg
     return true;
   }
 
   return false;
 }
 
 // supports_condition_in_parens
 //   : '(' S* supports_condition_in_parens_inside_parens ')' S*
+//   | supports_condition_pref
 //   | general_enclosed
 //   ;
 bool
 CSSParserImpl::ParseSupportsConditionInParens(bool& aConditionMet)
 {
   if (!GetToken(true)) {
     REPORT_UNEXPECTED_EOF(PESupportsConditionInParensStartEOF);
     return false;
   }
 
   if (mToken.mType == eCSSToken_URL) {
     aConditionMet = false;
     return true;
   }
 
+  if (AgentRulesEnabled() &&
+      mToken.mType == eCSSToken_Function &&
+      mToken.mIdent.LowerCaseEqualsLiteral("-moz-bool-pref")) {
+    return ParseSupportsMozBoolPrefName(aConditionMet);
+  }
+
   if (mToken.mType == eCSSToken_Function ||
       mToken.mType == eCSSToken_Bad_URL) {
     if (!SkipUntil(')')) {
       REPORT_UNEXPECTED_EOF(PESupportsConditionInParensEOF);
       return false;
     }
     aConditionMet = false;
     return true;
@@ -4570,16 +4578,42 @@ CSSParserImpl::ParseSupportsConditionInP
     SkipUntil(')');
     aConditionMet = false;
     return true;
   }
 
   return true;
 }
 
+// supports_condition_pref
+//   : '-moz-bool-pref(' bool_pref_name ')'
+//   ;
+bool
+CSSParserImpl::ParseSupportsMozBoolPrefName(bool& aConditionMet)
+{
+  if (!GetToken(true)) {
+    return false;
+  }
+
+  if (mToken.mType != eCSSToken_String) {
+    SkipUntil(')');
+    return false;
+  }
+
+  aConditionMet = Preferences::GetBool(
+    NS_ConvertUTF16toUTF8(mToken.mIdent).get());
+
+  if (!ExpectSymbol(')', true)) {
+    SkipUntil(')');
+    return false;
+  }
+
+  return true;
+}
+
 // supports_condition_in_parens_inside_parens
 //   : core_declaration
 //   | supports_condition_negation
 //   | supports_condition_in_parens supports_condition_terms
 //   ;
 bool
 CSSParserImpl::ParseSupportsConditionInParensInsideParens(bool& aConditionMet)
 {
--- a/layout/style/nsLayoutStylesheetCache.cpp
+++ b/layout/style/nsLayoutStylesheetCache.cpp
@@ -129,16 +129,21 @@ nsLayoutStylesheetCache::UASheet()
   }
 
   return mUASheet;
 }
 
 StyleSheetHandle
 nsLayoutStylesheetCache::HTMLSheet()
 {
+  if (!mHTMLSheet) {
+    LoadSheetURL("resource://gre-resources/html.css",
+                 mHTMLSheet, eAgentSheetFeatures);
+  }
+
   return mHTMLSheet;
 }
 
 StyleSheetHandle
 nsLayoutStylesheetCache::MinimalXULSheet()
 {
   return mMinimalXULSheet;
 }
@@ -314,18 +319,16 @@ nsLayoutStylesheetCache::nsLayoutStylesh
   }
 
   InitFromProfile();
 
   // And make sure that we load our UA sheets.  No need to do this
   // per-profile, since they're profile-invariant.
   LoadSheetURL("resource://gre-resources/counterstyles.css",
                mCounterStylesSheet, eAgentSheetFeatures);
-  LoadSheetURL("resource://gre-resources/html.css",
-               mHTMLSheet, eAgentSheetFeatures);
   LoadSheetURL("chrome://global/content/minimal-xul.css",
                mMinimalXULSheet, eAgentSheetFeatures);
   LoadSheetURL("resource://gre-resources/quirk.css",
                mQuirkSheet, eAgentSheetFeatures);
   LoadSheetURL("resource://gre/res/svg.css",
                mSVGSheet, eAgentSheetFeatures);
   LoadSheetURL("chrome://global/content/xul.css",
                mXULSheet, eAgentSheetFeatures);
@@ -370,16 +373,18 @@ nsLayoutStylesheetCache::For(StyleBacken
     // For each pref that controls a CSS feature that a UA style sheet depends
     // on (such as a pref that enables a property that a UA style sheet uses),
     // register DependentPrefChanged as a callback to ensure that the relevant
     // style sheets will be re-parsed.
     // Preferences::RegisterCallback(&DependentPrefChanged,
     //                               "layout.css.example-pref.enabled");
     Preferences::RegisterCallback(&DependentPrefChanged,
                                   "layout.css.grid.enabled");
+    Preferences::RegisterCallback(&DependentPrefChanged,
+                                  "dom.details_element.enabled");
   }
 
   return cache;
 }
 
 void
 nsLayoutStylesheetCache::InitFromProfile()
 {
@@ -813,16 +818,17 @@ nsLayoutStylesheetCache::DependentPrefCh
   // then setting our cached sheet pointer to null.  This will only work for
   // sheets that are loaded lazily.
 
 #define INVALIDATE(sheet_) \
   InvalidateSheet(gStyleCache_Gecko ? &gStyleCache_Gecko->sheet_ : nullptr, \
                   gStyleCache_Servo ? &gStyleCache_Servo->sheet_ : nullptr);
 
   INVALIDATE(mUASheet);  // for layout.css.grid.enabled
+  INVALIDATE(mHTMLSheet); // for dom.details_element.enabled
 
 #undef INVALIDATE
 }
 
 /* static */ void
 nsLayoutStylesheetCache::InvalidatePreferenceSheets()
 {
   if (gStyleCache_Gecko) {
--- a/layout/style/res/html.css
+++ b/layout/style/res/html.css
@@ -766,23 +766,25 @@ audio:not([controls]) {
 }
 
 video > .caption-box {
   position: relative;
   overflow: hidden;
 }
 
 /* details & summary */
+/* Need to revert Bug 1259889 Part 2 when removing details preference. */
+@supports -moz-bool-pref("dom.details_element.enabled") {
+  details > summary::-moz-list-bullet {
+    list-style-type: disclosure-closed;
+  }
 
-details > summary::-moz-list-bullet {
-  list-style-type: disclosure-closed;
-}
-
-details[open] > summary::-moz-list-bullet {
-  list-style-type: disclosure-open;
+  details[open] > summary::-moz-list-bullet {
+    list-style-type: disclosure-open;
+  }
 }
 
 /* emulation of non-standard HTML <marquee> tag */
 marquee {
   inline-size: -moz-available;
   display: inline-block;
   vertical-align: text-bottom;
   text-align: start;
--- a/layout/tools/reftest/reftest-preferences.js
+++ b/layout/tools/reftest/reftest-preferences.js
@@ -109,8 +109,11 @@ user_pref("browser.tabs.remote.autostart
 user_pref("browser.tabs.remote.autostart.2", false);
 
 user_pref("startup.homepage_welcome_url", "");
 user_pref("startup.homepage_welcome_url.additional", "");
 user_pref("startup.homepage_override_url", "");
 user_pref("browser.usedOnWindows10.introURL", "");
 
 user_pref("media.gmp-manager.url.override", "http://localhost/dummy-gmp-manager.xml");
+
+// A fake bool pref for "@supports -moz-bool-pref" sanify test.
+user_pref("testing.supports.moz-bool-pref", true);
--- a/testing/web-platform/meta/MANIFEST.json
+++ b/testing/web-platform/meta/MANIFEST.json
@@ -28385,16 +28385,20 @@
         "path": "vibration/silent-ignore.html",
         "url": "/vibration/silent-ignore.html"
       },
       {
         "path": "web-animations/animatable/animate.html",
         "url": "/web-animations/animatable/animate.html"
       },
       {
+        "path": "web-animations/animation-effect-timing/delay.html",
+        "url": "/web-animations/animation-effect-timing/delay.html"
+      },
+      {
         "path": "web-animations/animation-effect-timing/duration.html",
         "url": "/web-animations/animation-effect-timing/duration.html"
       },
       {
         "path": "web-animations/animation-effect-timing/endDelay.html",
         "url": "/web-animations/animation-effect-timing/endDelay.html"
       },
       {
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/web-animations/animation-effect-timing/delay.html
@@ -0,0 +1,62 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>delay tests</title>
+<link rel="help" href="https://w3c.github.io/web-animations/#dom-animationeffecttiming-delay">
+<link rel="author" title="Daisuke Akatsuka" href="mailto:daisuke@mozilla-japan.org">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../testcommon.js"></script>
+<link rel="stylesheet" href="/resources/testharness.css">
+<body>
+<div id="log"></div>
+<script>
+'use strict';
+
+test(function(t) {
+  var div = createDiv(t);
+  var anim = div.animate({ opacity: [ 0, 1 ] }, 100);
+  anim.effect.timing.delay = 100;
+  assert_equals(anim.effect.timing.delay, 100, 'set delay 100');
+  assert_equals(anim.effect.getComputedTiming().delay, 100,
+                  'getComputedTiming() after set delay 100');
+}, 'set delay 100');
+
+test(function(t) {
+  var div = createDiv(t);
+  var anim = div.animate({ opacity: [ 0, 1 ] }, 100);
+  anim.effect.timing.delay = -100;
+  assert_equals(anim.effect.timing.delay, -100, 'set delay -100');
+  assert_equals(anim.effect.getComputedTiming().delay, -100,
+                'getComputedTiming() after set delay -100');
+}, 'set delay -100');
+
+test(function(t) {
+  var div = createDiv(t);
+  var anim = div.animate({ opacity: [ 0, 1 ] }, 100);
+  anim.effect.timing.delay = 100;
+  assert_equals(anim.effect.getComputedTiming().progress, null);
+  assert_equals(anim.effect.getComputedTiming().currentIteration, null);
+}, 'Test adding a positive delay to an animation without a backwards fill ' +
+   'makes it no longer active');
+
+test(function(t) {
+  var div = createDiv(t);
+  var anim = div.animate({ opacity: [ 0, 1 ] },
+                         { fill: 'both',
+                           duration: 100 });
+  anim.effect.timing.delay = -50;
+  assert_equals(anim.effect.getComputedTiming().progress, 0.5);
+}, 'Test seeking an animation by setting a negative delay');
+
+test(function(t) {
+  var div = createDiv(t);
+  var anim = div.animate({ opacity: [ 0, 1 ] },
+                         { fill: 'both',
+                           duration: 100 });
+  anim.effect.timing.delay = -100;
+  assert_equals(anim.effect.getComputedTiming().progress, 1);
+  assert_equals(anim.effect.getComputedTiming().currentIteration, 0);
+}, 'Test finishing an animation using a large negative delay');
+
+</script>
+</body>
--- a/widget/gtk/gtk3drawing.cpp
+++ b/widget/gtk/gtk3drawing.cpp
@@ -36,19 +36,19 @@ static GtkWidget* gComboBoxArrowWidget;
 static GtkWidget* gComboBoxSeparatorWidget;
 static GtkWidget* gComboBoxEntryWidget;
 static GtkWidget* gComboBoxEntryTextareaWidget;
 static GtkWidget* gComboBoxEntryButtonWidget;
 static GtkWidget* gComboBoxEntryArrowWidget;
 static GtkWidget* gHandleBoxWidget;
 static GtkWidget* gToolbarWidget;
 static GtkWidget* gFrameWidget;
-static GtkWidget* gStatusbarWidget;
 static GtkWidget* gProgressWidget;
 static GtkWidget* gTabWidget;
+static GtkWidget* gTextViewWidget;
 static GtkWidget* gTooltipWidget;
 static GtkWidget* gMenuBarWidget;
 static GtkWidget* gMenuBarItemWidget;
 static GtkWidget* gMenuPopupWidget;
 static GtkWidget* gMenuItemWidget;
 static GtkWidget* gImageMenuItemWidget;
 static GtkWidget* gCheckMenuItemWidget;
 static GtkWidget* gTreeViewWidget;
@@ -533,33 +533,21 @@ ensure_progress_widget()
     if (!gProgressWidget) {
         gProgressWidget = gtk_progress_bar_new();
         setup_widget_prototype(gProgressWidget);
     }
     return MOZ_GTK_SUCCESS;
 }
 
 static gint
-ensure_statusbar_widget()
-{
-    if (!gStatusbarWidget) {
-      gStatusbarWidget = gtk_statusbar_new();
-      setup_widget_prototype(gStatusbarWidget);
-    }
-    return MOZ_GTK_SUCCESS;
-}
-
-static gint
 ensure_frame_widget()
 {
     if (!gFrameWidget) {
-        ensure_statusbar_widget();
         gFrameWidget = gtk_frame_new(NULL);
-        gtk_container_add(GTK_CONTAINER(gStatusbarWidget), gFrameWidget);
-        gtk_widget_realize(gFrameWidget);
+        setup_widget_prototype(gFrameWidget);
     }
     return MOZ_GTK_SUCCESS;
 }
 
 static gint
 ensure_menu_bar_widget()
 {
     if (!gMenuBarWidget) {
@@ -716,16 +704,27 @@ ensure_scrolled_window_widget()
 {
     if (!gScrolledWindowWidget) {
         gScrolledWindowWidget = gtk_scrolled_window_new(NULL, NULL);
         setup_widget_prototype(gScrolledWindowWidget);
     }
     return MOZ_GTK_SUCCESS;
 }
 
+static void
+ensure_text_view_widget()
+{
+    if (gTextViewWidget)
+        return;
+
+    gTextViewWidget = gtk_text_view_new();
+    ensure_scrolled_window_widget();
+    gtk_container_add(GTK_CONTAINER(gScrolledWindowWidget), gTextViewWidget);
+}
+
 gint
 moz_gtk_init()
 {
     if (is_initialized)
         return MOZ_GTK_SUCCESS;
 
     is_initialized = TRUE;
     have_arrow_scaling = (gtk_major_version > 2 ||
@@ -1959,21 +1958,26 @@ moz_gtk_tooltip_paint(cairo_t *cr, GdkRe
 
 static gint
 moz_gtk_resizer_paint(cairo_t *cr, GdkRectangle* rect,
                       GtkWidgetState* state,
                       GtkTextDirection direction)
 {
     GtkStyleContext* style;
 
-    ensure_frame_widget();
-    gtk_widget_set_direction(gStatusbarWidget, GTK_TEXT_DIR_LTR);
-
-    style = gtk_widget_get_style_context(gStatusbarWidget);
+    // gtk_render_handle() draws a background, so use GtkTextView and its
+    // GTK_STYLE_CLASS_VIEW to match the background with textarea elements.
+    // The resizer is drawn with shaded variants of the background color, and
+    // so a transparent background would lead to a transparent resizer.
+    ensure_text_view_widget();
+    gtk_widget_set_direction(gTextViewWidget, GTK_TEXT_DIR_LTR);
+
+    style = gtk_widget_get_style_context(gTextViewWidget);
     gtk_style_context_save(style);
+    gtk_style_context_add_class(style, GTK_STYLE_CLASS_VIEW);
     gtk_style_context_add_class(style, GTK_STYLE_CLASS_GRIP);
     gtk_style_context_set_state(style, GetStateFlagsFromGtkWidgetState(state));
 
     // Workaround unico not respecting the text direction for resizers.
     // See bug 1174248.
     cairo_save(cr);
     if (direction == GTK_TEXT_DIR_RTL) {
       cairo_matrix_t mat;
@@ -3436,20 +3440,20 @@ moz_gtk_shutdown()
     gComboBoxSeparatorWidget = NULL;
     gComboBoxArrowWidget = NULL;
     gComboBoxEntryWidget = NULL;
     gComboBoxEntryButtonWidget = NULL;
     gComboBoxEntryArrowWidget = NULL;
     gComboBoxEntryTextareaWidget = NULL;
     gHandleBoxWidget = NULL;
     gToolbarWidget = NULL;
-    gStatusbarWidget = NULL;
     gFrameWidget = NULL;
     gProgressWidget = NULL;
     gTabWidget = NULL;
+    gTextViewWidget = nullptr;
     gTooltipWidget = NULL;
     gMenuBarWidget = NULL;
     gMenuBarItemWidget = NULL;
     gMenuPopupWidget = NULL;
     gMenuItemWidget = NULL;
     gImageMenuItemWidget = NULL;
     gCheckMenuItemWidget = NULL;
     gTreeViewWidget = NULL;