Merge mozilla-central to fx-team
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Mon, 04 Apr 2016 12:03:02 +0200
changeset 291581 784b84d126d848ef43e8495f52279b994c0d3e0d
parent 291580 2ffa9007ef203a29052b6db3e1dd63c0349cc70e (current diff)
parent 291476 9bd90088875399347b05d87c67d3709e31539dcd (diff)
child 291582 7c857bd36bc2e008a861b4296892fd7a0fcb74e4
push id74626
push userryanvm@gmail.com
push dateTue, 05 Apr 2016 03:39:41 +0000
treeherdermozilla-inbound@e02a9b1b4931 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone48.0a1
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
Merge mozilla-central to fx-team
--- 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;