Backed out 9 changesets (bug 1542454, bug 1533562) for failing at /browser/browser_ext_webNavigation_onCreatedNavigationTarget_contextmenu.js on a CLOSED TREE.
authorGurzau Raul <rgurzau@mozilla.com>
Tue, 23 Apr 2019 01:09:12 +0300
changeset 470415 73e8dcb8be078f4d66058019f70c2cd0e4526a6b
parent 470414 9509ae2baf1cecd227c949c74bc4022b79e89459
child 470416 89f6237ef760987f550fa6a96d92268152cbbe0c
push id35905
push userdvarga@mozilla.com
push dateTue, 23 Apr 2019 09:53:27 +0000
treeherdermozilla-central@831918f009f6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1542454, 1533562
milestone68.0a1
backs out88756a3099686f41f7601e3b7de48080a03e292a
37bd2819d3a7dc23f7661c494486a93f1d1a4977
19f0462bb47a3ae0e301b7ff2723407a9c178c2d
b4440b1833ece120e7680763b55168913b18e0d4
8970cdb3c04a11ddbe86c53d5e759685949a0cb3
498cd34eea782ce73c1eaffd193427add877117c
3a3b4d52e10a17f947717e0e468665d2bdcfb57e
5fae2f233aa03481685694cfa013c9b4d50d5608
e3fc54ebcd15b682f7f9117c86a0d049fc28c6b0
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 9 changesets (bug 1542454, bug 1533562) for failing at /browser/browser_ext_webNavigation_onCreatedNavigationTarget_contextmenu.js on a CLOSED TREE. Backed out changeset 88756a309968 (bug 1542454) Backed out changeset 37bd2819d3a7 (bug 1542454) Backed out changeset 19f0462bb47a (bug 1542454) Backed out changeset b4440b1833ec (bug 1533562) Backed out changeset 8970cdb3c04a (bug 1533562) Backed out changeset 498cd34eea78 (bug 1533562) Backed out changeset 3a3b4d52e10a (bug 1533562) Backed out changeset 5fae2f233aa0 (bug 1533562) Backed out changeset e3fc54ebcd15 (bug 1533562)
widget/cocoa/nsChildView.h
widget/cocoa/nsChildView.mm
widget/cocoa/nsCocoaWindow.h
widget/cocoa/nsCocoaWindow.mm
widget/cocoa/nsWindowMap.mm
--- a/widget/cocoa/nsChildView.h
+++ b/widget/cocoa/nsChildView.h
@@ -364,17 +364,17 @@ class nsChildView final : public nsBaseW
 
   virtual int32_t RoundsWidgetCoordinatesTo() override;
 
   virtual void Invalidate(const LayoutDeviceIntRect& aRect) override;
 
   virtual void* GetNativeData(uint32_t aDataType) override;
   virtual nsresult ConfigureChildren(const nsTArray<Configuration>& aConfigurations) override;
   virtual LayoutDeviceIntPoint WidgetToScreenOffset() override;
-  virtual bool ShowsResizeIndicator(LayoutDeviceIntRect* aResizerRect) override { return false; }
+  virtual bool ShowsResizeIndicator(LayoutDeviceIntRect* aResizerRect) override;
 
   static bool ConvertStatus(nsEventStatus aStatus) {
     return aStatus == nsEventStatus_eConsumeNoDefault;
   }
   virtual nsresult DispatchEvent(mozilla::WidgetGUIEvent* aEvent, nsEventStatus& aStatus) override;
 
   virtual bool WidgetTypeSupportsAcceleration() override;
   virtual bool ShouldUseOffMainThreadCompositing() override;
@@ -435,18 +435,16 @@ class nsChildView final : public nsBaseW
                                               nsIObserver* aObserver) override;
 
   // Mac specific methods
 
   virtual bool DispatchWindowEvent(mozilla::WidgetGUIEvent& event);
 
   void WillPaintWindow();
   bool PaintWindow(LayoutDeviceIntRegion aRegion);
-  bool PaintWindowInDrawTarget(mozilla::gfx::DrawTarget* aDT, const LayoutDeviceIntRegion& aRegion,
-                               const mozilla::gfx::IntSize& aSurfaceSize);
   bool PaintWindowInContext(CGContextRef aContext, const LayoutDeviceIntRegion& aRegion,
                             mozilla::gfx::IntSize aSurfaceSize);
 
 #ifdef ACCESSIBILITY
   already_AddRefed<mozilla::a11y::Accessible> GetDocumentAccessible();
 #endif
 
   virtual void CreateCompositor() override;
@@ -550,16 +548,17 @@ class nsChildView final : public nsBaseW
 
   void ConfigureAPZCTreeManager() override;
   void ConfigureAPZControllerThread() override;
 
   void DoRemoteComposition(const LayoutDeviceIntRect& aRenderRect);
 
   // Overlay drawing functions for OpenGL drawing
   void DrawWindowOverlay(mozilla::layers::GLManager* aManager, LayoutDeviceIntRect aRect);
+  void MaybeDrawResizeIndicator(mozilla::layers::GLManager* aManager);
   void MaybeDrawRoundedCorners(mozilla::layers::GLManager* aManager,
                                const LayoutDeviceIntRect& aRect);
   void MaybeDrawTitlebar(mozilla::layers::GLManager* aManager);
 
   // Redraw the contents of mTitlebarCGContext on the main thread, as
   // determined by mDirtyTitlebarRegion.
   void UpdateTitlebarCGContext();
 
@@ -595,28 +594,32 @@ class nsChildView final : public nsBaseW
   // Protects the view from being teared down while a composition is in
   // progress on the compositor thread.
   mozilla::Mutex mViewTearDownLock;
 
   mozilla::Mutex mEffectsLock;
 
   // May be accessed from any thread, protected
   // by mEffectsLock.
+  bool mShowsResizeIndicator;
+  LayoutDeviceIntRect mResizeIndicatorRect;
+  bool mHasRoundedBottomCorners;
   int mDevPixelCornerRadius;
   bool mIsCoveringTitlebar;
   bool mIsFullscreen;
   bool mIsOpaque;
   LayoutDeviceIntRect mTitlebarRect;
 
   // The area of mTitlebarCGContext that needs to be redrawn during the next
   // transaction. Accessed from any thread, protected by mEffectsLock.
   LayoutDeviceIntRegion mUpdatedTitlebarRegion;
   CGContextRef mTitlebarCGContext;
 
   // Compositor thread only
+  mozilla::UniquePtr<mozilla::widget::RectTextureImage> mResizerImage;
   mozilla::UniquePtr<mozilla::widget::RectTextureImage> mCornerMaskImage;
   mozilla::UniquePtr<mozilla::widget::RectTextureImage> mTitlebarImage;
   mozilla::UniquePtr<mozilla::widget::RectTextureImage> mBasicCompositorImage;
 
   // Main thread + webrender only
   mozilla::Maybe<mozilla::wr::ImageKey> mTitlebarImageKey;
   mozilla::gfx::IntSize mTitlebarImageSize;
 
--- a/widget/cocoa/nsChildView.mm
+++ b/widget/cocoa/nsChildView.mm
@@ -181,16 +181,17 @@ static NSMutableDictionary* sNativeKeyEv
                           toGeckoEvent:(WidgetMouseEvent*)outGeckoEvent;
 - (NSMenu*)contextMenu;
 
 - (BOOL)isRectObscuredBySubview:(NSRect)inRect;
 
 - (LayoutDeviceIntRegion)nativeDirtyRegionWithBoundingRect:(NSRect)aRect;
 - (BOOL)isUsingOpenGL;
 
+- (BOOL)hasRoundedBottomCorners;
 - (CGFloat)cornerRadius;
 - (void)clearCorners;
 
 - (void)setGLOpaque:(BOOL)aOpaque;
 
 // Overlay drawing functions for traditional CGContext drawing
 - (void)drawTitleString;
 - (void)maskTopCornersInContext:(CGContextRef)aContext;
@@ -317,16 +318,18 @@ struct SwipeEventQueue {
 
 nsChildView::nsChildView()
     : nsBaseWidget(),
       mView(nullptr),
       mParentView(nil),
       mParentWidget(nullptr),
       mViewTearDownLock("ChildViewTearDown"),
       mEffectsLock("WidgetEffects"),
+      mShowsResizeIndicator(false),
+      mHasRoundedBottomCorners(false),
       mDevPixelCornerRadius{0},
       mIsCoveringTitlebar(false),
       mIsFullscreen(false),
       mIsOpaque(false),
       mTitlebarCGContext(nullptr),
       mBackingScaleFactor(0.0),
       mVisible(false),
       mDrawing(false),
@@ -904,16 +907,37 @@ void nsChildView::Resize(double aX, doub
     ReportMoveEvent();
     if (mOnDestroyCalled) return;
   }
   if (isResizing) ReportSizeEvent();
 
   NS_OBJC_END_TRY_ABORT_BLOCK;
 }
 
+static const int32_t resizeIndicatorWidth = 15;
+static const int32_t resizeIndicatorHeight = 15;
+bool nsChildView::ShowsResizeIndicator(LayoutDeviceIntRect* aResizerRect) {
+  NSView *topLevelView = mView, *superView = nil;
+  while ((superView = [topLevelView superview])) topLevelView = superView;
+
+  if (![[topLevelView window] showsResizeIndicator] ||
+      !([[topLevelView window] styleMask] & NSResizableWindowMask))
+    return false;
+
+  if (aResizerRect) {
+    NSSize bounds = [topLevelView bounds].size;
+    NSPoint corner = NSMakePoint(bounds.width, [topLevelView isFlipped] ? bounds.height : 0);
+    corner = [topLevelView convertPoint:corner toView:mView];
+    aResizerRect->SetRect(NSToIntRound(corner.x) - resizeIndicatorWidth,
+                          NSToIntRound(corner.y) - resizeIndicatorHeight, resizeIndicatorWidth,
+                          resizeIndicatorHeight);
+  }
+  return true;
+}
+
 nsresult nsChildView::SynthesizeNativeKeyEvent(int32_t aNativeKeyboardLayout,
                                                int32_t aNativeKeyCode, uint32_t aModifierFlags,
                                                const nsAString& aCharacters,
                                                const nsAString& aUnmodifiedCharacters,
                                                nsIObserver* aObserver) {
   AutoObserverNotifier notifier(aObserver, "keyevent");
   return mTextInputHandler->SynthesizeNativeKeyEvent(
       aNativeKeyboardLayout, aNativeKeyCode, aModifierFlags, aCharacters, aUnmodifiedCharacters);
@@ -1212,16 +1236,21 @@ bool nsChildView::WidgetTypeSupportsAcce
 
 bool nsChildView::ShouldUseOffMainThreadCompositing() {
   // Don't use OMTC for transparent windows or for popup windows.
   if (!WidgetTypeSupportsAcceleration()) return false;
 
   return nsBaseWidget::ShouldUseOffMainThreadCompositing();
 }
 
+inline uint16_t COLOR8TOCOLOR16(uint8_t color8) {
+  // return (color8 == 0xFF ? 0xFFFF : (color8 << 8));
+  return (color8 << 8) | color8; /* (color8 * 257) == (color8 * 0x0101) */
+}
+
 #pragma mark -
 
 nsresult nsChildView::ConfigureChildren(const nsTArray<Configuration>& aConfigurations) {
   return NS_OK;
 }
 
 // Invokes callback and ProcessEvent methods on Event Listener object
 nsresult nsChildView::DispatchEvent(WidgetGUIEvent* event, nsEventStatus& aStatus) {
@@ -1304,55 +1333,48 @@ bool nsChildView::PaintWindow(LayoutDevi
   if (listener) {
     listener->DidPaintWindow();
   }
 
   mIsDispatchPaint = oldDispatchPaint;
   return returnValue;
 }
 
-bool nsChildView::PaintWindowInDrawTarget(gfx::DrawTarget* aDT,
-                                          const LayoutDeviceIntRegion& aRegion,
-                                          const gfx::IntSize& aSurfaceSize) {
-  RefPtr<gfxContext> targetContext = gfxContext::CreateOrNull(aDT);
-  MOZ_ASSERT(targetContext);
-
-  // Set up the clip region and clear existing contents in the backing surface.
-  targetContext->NewPath();
-  for (auto iter = aRegion.RectIter(); !iter.Done(); iter.Next()) {
-    const LayoutDeviceIntRect& r = iter.Get();
-    targetContext->Rectangle(gfxRect(r.x, r.y, r.width, r.height));
-    aDT->ClearRect(gfx::Rect(r.ToUnknownRect()));
-  }
-  targetContext->Clip();
-
-  nsAutoRetainCocoaObject kungFuDeathGrip(mView);
-  if (GetLayerManager()->GetBackendType() == LayersBackend::LAYERS_BASIC) {
-    nsBaseWidget::AutoLayerManagerSetup setupLayerManager(this, targetContext,
-                                                          BufferMode::BUFFER_NONE);
-    return PaintWindow(aRegion);
-  }
-  if (GetLayerManager()->GetBackendType() == LayersBackend::LAYERS_CLIENT) {
-    // We only need this so that we actually get DidPaintWindow fired
-    return PaintWindow(aRegion);
-  }
-  return false;
-}
-
 bool nsChildView::PaintWindowInContext(CGContextRef aContext, const LayoutDeviceIntRegion& aRegion,
                                        gfx::IntSize aSurfaceSize) {
   if (!mBackingSurface || mBackingSurface->GetSize() != aSurfaceSize) {
     mBackingSurface = gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(
         aSurfaceSize, gfx::SurfaceFormat::B8G8R8A8);
     if (!mBackingSurface) {
       return false;
     }
   }
 
-  bool painted = PaintWindowInDrawTarget(mBackingSurface, aRegion, aSurfaceSize);
+  RefPtr<gfxContext> targetContext = gfxContext::CreateOrNull(mBackingSurface);
+  MOZ_ASSERT(targetContext);  // already checked the draw target above
+
+  // Set up the clip region and clear existing contents in the backing surface.
+  targetContext->NewPath();
+  for (auto iter = aRegion.RectIter(); !iter.Done(); iter.Next()) {
+    const LayoutDeviceIntRect& r = iter.Get();
+    targetContext->Rectangle(gfxRect(r.x, r.y, r.width, r.height));
+    mBackingSurface->ClearRect(gfx::Rect(r.ToUnknownRect()));
+  }
+  targetContext->Clip();
+
+  nsAutoRetainCocoaObject kungFuDeathGrip(mView);
+  bool painted = false;
+  if (GetLayerManager()->GetBackendType() == LayersBackend::LAYERS_BASIC) {
+    nsBaseWidget::AutoLayerManagerSetup setupLayerManager(this, targetContext,
+                                                          BufferMode::BUFFER_NONE);
+    painted = PaintWindow(aRegion);
+  } else if (GetLayerManager()->GetBackendType() == LayersBackend::LAYERS_CLIENT) {
+    // We only need this so that we actually get DidPaintWindow fired
+    painted = PaintWindow(aRegion);
+  }
 
   uint8_t* data;
   gfx::IntSize size;
   int32_t stride;
   gfx::SurfaceFormat format;
 
   if (!mBackingSurface->LockBits(&data, &size, &stride, &format)) {
     return false;
@@ -1713,16 +1735,18 @@ LayoutDeviceIntRect nsChildView::RectCon
 }
 
 void nsChildView::PrepareWindowEffects() {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
 
   bool canBeOpaque;
   {
     MutexAutoLock lock(mEffectsLock);
+    mShowsResizeIndicator = ShowsResizeIndicator(&mResizeIndicatorRect);
+    mHasRoundedBottomCorners = [mView hasRoundedBottomCorners];
     CGFloat cornerRadius = [mView cornerRadius];
     mDevPixelCornerRadius = cornerRadius * BackingScaleFactor();
     mIsCoveringTitlebar = [mView isCoveringTitlebar];
     NSInteger styleMask = [[mView window] styleMask];
     bool wasFullscreen = mIsFullscreen;
     nsCocoaWindow* windowWidget = GetXULWindowWidget();
     mIsFullscreen =
         (styleMask & NSFullScreenWindowMask) || (windowWidget && windowWidget->InFullScreenMode());
@@ -1742,16 +1766,17 @@ void nsChildView::PrepareWindowEffects()
     mIsOpaque = canBeOpaque;
     [mView setGLOpaque:canBeOpaque];
   }
 
   NS_OBJC_END_TRY_ABORT_BLOCK;
 }
 
 void nsChildView::CleanupWindowEffects() {
+  mResizerImage = nullptr;
   mCornerMaskImage = nullptr;
   mTitlebarImage = nullptr;
 }
 
 void nsChildView::AddWindowOverlayWebRenderCommands(layers::WebRenderBridgeChild* aWrBridge,
                                                     wr::DisplayListBuilder& aBuilder,
                                                     wr::IpcResourceUpdateQueue& aResources) {
   PrepareWindowEffects();
@@ -1830,25 +1855,80 @@ void nsChildView::DrawWindowOverlay(Widg
   }
 }
 
 void nsChildView::DrawWindowOverlay(GLManager* aManager, LayoutDeviceIntRect aRect) {
   GLContext* gl = aManager->gl();
   ScopedGLState scopedScissorTestState(gl, LOCAL_GL_SCISSOR_TEST, false);
 
   MaybeDrawTitlebar(aManager);
+  MaybeDrawResizeIndicator(aManager);
   MaybeDrawRoundedCorners(aManager, aRect);
 }
 
 static void ClearRegion(gfx::DrawTarget* aDT, LayoutDeviceIntRegion aRegion) {
   gfxUtils::ClipToRegion(aDT, aRegion.ToUnknownRegion());
   aDT->ClearRect(gfx::Rect(0, 0, aDT->GetSize().width, aDT->GetSize().height));
   aDT->PopClip();
 }
 
+static void DrawResizer(CGContextRef aCtx) {
+  CGContextSetShouldAntialias(aCtx, false);
+  CGPoint points[6];
+  points[0] = CGPointMake(13.0f, 4.0f);
+  points[1] = CGPointMake(3.0f, 14.0f);
+  points[2] = CGPointMake(13.0f, 8.0f);
+  points[3] = CGPointMake(7.0f, 14.0f);
+  points[4] = CGPointMake(13.0f, 12.0f);
+  points[5] = CGPointMake(11.0f, 14.0f);
+  CGContextSetRGBStrokeColor(aCtx, 0.00f, 0.00f, 0.00f, 0.15f);
+  CGContextStrokeLineSegments(aCtx, points, 6);
+
+  points[0] = CGPointMake(13.0f, 5.0f);
+  points[1] = CGPointMake(4.0f, 14.0f);
+  points[2] = CGPointMake(13.0f, 9.0f);
+  points[3] = CGPointMake(8.0f, 14.0f);
+  points[4] = CGPointMake(13.0f, 13.0f);
+  points[5] = CGPointMake(12.0f, 14.0f);
+  CGContextSetRGBStrokeColor(aCtx, 0.13f, 0.13f, 0.13f, 0.54f);
+  CGContextStrokeLineSegments(aCtx, points, 6);
+
+  points[0] = CGPointMake(13.0f, 6.0f);
+  points[1] = CGPointMake(5.0f, 14.0f);
+  points[2] = CGPointMake(13.0f, 10.0f);
+  points[3] = CGPointMake(9.0f, 14.0f);
+  points[5] = CGPointMake(13.0f, 13.9f);
+  points[4] = CGPointMake(13.0f, 14.0f);
+  CGContextSetRGBStrokeColor(aCtx, 0.84f, 0.84f, 0.84f, 0.55f);
+  CGContextStrokeLineSegments(aCtx, points, 6);
+}
+
+void nsChildView::MaybeDrawResizeIndicator(GLManager* aManager) {
+  MutexAutoLock lock(mEffectsLock);
+  if (!mShowsResizeIndicator) {
+    return;
+  }
+
+  if (!mResizerImage) {
+    mResizerImage = MakeUnique<RectTextureImage>();
+  }
+
+  LayoutDeviceIntSize size = mResizeIndicatorRect.Size();
+  mResizerImage->UpdateIfNeeded(
+      size, LayoutDeviceIntRegion(),
+      ^(gfx::DrawTarget* drawTarget, const LayoutDeviceIntRegion& updateRegion) {
+        ClearRegion(drawTarget, updateRegion);
+        gfx::BorrowedCGContext borrow(drawTarget);
+        DrawResizer(borrow.cg);
+        borrow.Finish();
+      });
+
+  mResizerImage->Draw(aManager, mResizeIndicatorRect.TopLeft());
+}
+
 static CGContextRef CreateCGContext(const LayoutDeviceIntSize& aSize) {
   CGColorSpaceRef cs = CGColorSpaceCreateDeviceRGB();
   CGContextRef ctx = CGBitmapContextCreate(
       NULL, aSize.width, aSize.height, 8 /* bitsPerComponent */, aSize.width * 4, cs,
       kCGBitmapByteOrder32Host | kCGImageAlphaPremultipliedFirst);
   CGColorSpaceRelease(cs);
 
   CGContextTranslateCTM(ctx, 0, aSize.height);
@@ -2049,17 +2129,17 @@ void nsChildView::MaybeDrawRoundedCorner
   Matrix4x4 flipY = Matrix4x4::Scaling(1, -1, 1);
 
   if (mIsCoveringTitlebar && !mIsFullscreen) {
     // Mask the top corners.
     mCornerMaskImage->Draw(aManager, aRect.TopLeft());
     mCornerMaskImage->Draw(aManager, aRect.TopRight(), flipX);
   }
 
-  if (!mIsFullscreen) {
+  if (mHasRoundedBottomCorners && !mIsFullscreen) {
     // Mask the bottom corners.
     mCornerMaskImage->Draw(aManager, aRect.BottomLeft(), flipY);
     mCornerMaskImage->Draw(aManager, aRect.BottomRight(), flipY * flipX);
   }
 
   // Reset blend mode.
   aManager->gl()->fBlendFuncSeparate(LOCAL_GL_ONE, LOCAL_GL_ONE_MINUS_SRC_ALPHA, LOCAL_GL_ONE,
                                      LOCAL_GL_ONE);
@@ -2340,16 +2420,17 @@ already_AddRefed<gfx::DrawTarget> nsChil
 void nsChildView::EndRemoteDrawing() {
   mBasicCompositorImage->EndUpdate();
   DoRemoteComposition(mBounds);
 }
 
 void nsChildView::CleanupRemoteDrawing() {
   mBasicCompositorImage = nullptr;
   mCornerMaskImage = nullptr;
+  mResizerImage = nullptr;
   mTitlebarImage = nullptr;
   mGLPresenter = nullptr;
 }
 
 bool nsChildView::InitCompositor(Compositor* aCompositor) {
   if (aCompositor->GetBackendType() == LayersBackend::LAYERS_BASIC) {
     if (!mGLPresenter) {
       mGLPresenter = GLPresenter::CreateForWindow(this);
@@ -3256,16 +3337,21 @@ NSEvent* gLastDragMouseDownEvent = nil;
 }
 
 - (BOOL)isUsingOpenGL {
   if (!mGeckoChild || ![self window]) return NO;
 
   return mGLContext || mUsingOMTCompositor;
 }
 
+- (BOOL)hasRoundedBottomCorners {
+  return [[self window] respondsToSelector:@selector(bottomCornerRounded)] &&
+         [[self window] bottomCornerRounded];
+}
+
 - (CGFloat)cornerRadius {
   NSView* frameView = [[[self window] contentView] superview];
   if (!frameView || ![frameView respondsToSelector:@selector(roundedCornerRadius)]) return 4.0f;
   return [frameView roundedCornerRadius];
 }
 
 - (void)setGLOpaque:(BOOL)aOpaque {
   CGLLockContext((CGLContextObj)[mGLContext CGLContextObj]);
@@ -3298,23 +3384,25 @@ NSEvent* gLastDragMouseDownEvent = nil;
   CGFloat w = [self bounds].size.width, h = [self bounds].size.height;
   [[NSColor clearColor] set];
 
   if ([self isCoveringTitlebar]) {
     NSRectFill(NSMakeRect(0, 0, radius, radius));
     NSRectFill(NSMakeRect(w - radius, 0, radius, radius));
   }
 
-  NSRectFill(NSMakeRect(0, h - radius, radius, radius));
-  NSRectFill(NSMakeRect(w - radius, h - radius, radius, radius));
+  if ([self hasRoundedBottomCorners]) {
+    NSRectFill(NSMakeRect(0, h - radius, radius, radius));
+    NSRectFill(NSMakeRect(w - radius, h - radius, radius, radius));
+  }
 }
 
 // This is the analog of nsChildView::MaybeDrawRoundedCorners for CGContexts.
 // We only need to mask the top corners here because Cocoa does the masking
-// for the window's bottom corners automatically.
+// for the window's bottom corners automatically (starting with 10.7).
 - (void)maskTopCornersInContext:(CGContextRef)aContext {
   CGFloat radius = [self cornerRadius];
   int32_t devPixelCornerRadius = mGeckoChild->CocoaPointsToDevPixels(radius);
 
   // First make sure that mTopLeftCornerMask is set up.
   if (!mTopLeftCornerMask || int32_t(CGImageGetWidth(mTopLeftCornerMask)) != devPixelCornerRadius) {
     CGImageRelease(mTopLeftCornerMask);
     CGColorSpaceRef rgb = CGColorSpaceCreateDeviceRGB();
--- a/widget/cocoa/nsCocoaWindow.h
+++ b/widget/cocoa/nsCocoaWindow.h
@@ -54,44 +54,26 @@ typedef struct _nsCocoaWindowList {
   nsTouchBar* mTouchBar;
 }
 
 - (void)importState:(NSDictionary*)aState;
 - (NSMutableDictionary*)exportState;
 - (void)setDrawsContentsIntoWindowFrame:(BOOL)aState;
 - (BOOL)drawsContentsIntoWindowFrame;
 
-// Two methods akin to contentRectForFrameRect and frameRectForContentRect,
-// which convert between the window's frame rect and the rect that our Gecko
-// ChildView would occupy. This is different because we make the window's
-// content view always cover the entire window; when drawsContentsIntoWindowFrame
-// is NO, the content view is larger than the ChildView because it includes the
-// titlebar whereas the ChildView does not.
-- (NSRect)childViewRectForFrameRect:(NSRect)aFrameRect;
-- (NSRect)frameRectForChildViewRect:(NSRect)aChildViewRect;
-
 - (void)mouseEntered:(NSEvent*)aEvent;
 - (void)mouseExited:(NSEvent*)aEvent;
 - (void)mouseMoved:(NSEvent*)aEvent;
 - (void)updateTrackingArea;
 - (NSView*)trackingAreaView;
 
 - (void)setBeingShown:(BOOL)aValue;
 - (BOOL)isBeingShown;
 - (BOOL)isVisibleOrBeingShown;
 
-// Returns an autoreleased NSArray containing the NSViews that we consider the
-// "contents" of this window. All views in the returned array are subviews of
-// this window's content view. However, the array may not include all of the
-// content view's subviews; concretely, the ToolbarWindow implementation will
-// exclude its TitlebarGradientView from the array that is returned here.
-// In the vast majority of cases, the array will only have a single element:
-// this window's mainChildView.
-- (NSArray<NSView*>*)contentViewContents;
-
 - (ChildView*)mainChildView;
 
 - (NSArray*)titlebarControls;
 
 - (void)setWantsTitleDrawn:(BOOL)aDrawTitle;
 - (BOOL)wantsTitleDrawn;
 
 - (void)setUseBrightTitlebarForeground:(BOOL)aBrightForeground;
@@ -112,16 +94,23 @@ typedef struct _nsCocoaWindowList {
 
 // If a window has been explicitly removed from the "window cache" (to
 // deactivate it), it's sometimes necessary to "reset" it to reactivate it
 // (and put it back in the "window cache").  One way to do this, which Apple
 // often uses, is to set the "window number" to '-1' and then back to its
 // original value.
 - (void)_setWindowNumber:(NSInteger)aNumber;
 
+// If we set the window's stylemask to be textured, the corners on the bottom of
+// the window are rounded by default. We use this private method to make
+// the corners square again, a la Safari. Starting with 10.7, all windows have
+// rounded bottom corners, so this call doesn't have any effect there.
+- (void)setBottomCornerRounded:(BOOL)rounded;
+- (BOOL)bottomCornerRounded;
+
 // Present in the same form on OS X since at least OS X 10.5.
 - (NSRect)contentRectForFrameRect:(NSRect)windowFrame styleMask:(NSUInteger)windowStyle;
 - (NSRect)frameRectForContentRect:(NSRect)windowContentRect styleMask:(NSUInteger)windowStyle;
 
 // Present since at least OS X 10.5.  The OS calls this method on NSWindow
 // (and its subclasses) to find out which NSFrameView subclass to instantiate
 // to create its "frame view".
 + (Class)frameViewClassForStyleMask:(NSUInteger)styleMask;
@@ -163,45 +152,38 @@ typedef struct _nsCocoaWindowList {
 - (id)initWithGeckoWindow:(nsCocoaWindow*)geckoWind;
 - (void)windowDidResize:(NSNotification*)aNotification;
 - (nsCocoaWindow*)geckoWidget;
 - (bool)toplevelActiveState;
 - (void)sendToplevelActivateEvents;
 - (void)sendToplevelDeactivateEvents;
 @end
 
-@interface TitlebarGradientView : NSView
-@end
+@class ToolbarWindow;
 
 // NSWindow subclass for handling windows with toolbars.
 @interface ToolbarWindow : BaseWindow {
-  // This window's titlebar gradient view, if present.
-  // Will be nil if drawsContentsIntoWindowFrame is YES.
-  // This view is a subview of the window's content view and gets created and
-  // destroyed by updateTitlebarGradientViewPresence.
-  TitlebarGradientView* mTitlebarGradientView;  // [STRONG]
-
   CGFloat mUnifiedToolbarHeight;
   CGFloat mSheetAttachmentPosition;
   NSRect mWindowButtonsRect;
   NSRect mFullScreenButtonRect;
 }
 - (void)setUnifiedToolbarHeight:(CGFloat)aHeight;
 - (CGFloat)unifiedToolbarHeight;
 - (CGFloat)titlebarHeight;
 - (NSRect)titlebarRect;
-- (void)setTitlebarNeedsDisplay;
+- (void)setTitlebarNeedsDisplayInRect:(NSRect)aRect sync:(BOOL)aSync;
+- (void)setTitlebarNeedsDisplayInRect:(NSRect)aRect;
 - (void)setDrawsContentsIntoWindowFrame:(BOOL)aState;
 - (void)setSheetAttachmentPosition:(CGFloat)aY;
 - (CGFloat)sheetAttachmentPosition;
 - (void)placeWindowButtons:(NSRect)aRect;
 - (void)placeFullScreenButton:(NSRect)aRect;
 - (NSPoint)windowButtonsPositionWithDefaultPosition:(NSPoint)aDefaultPosition;
 - (NSPoint)fullScreenButtonPositionWithDefaultPosition:(NSPoint)aDefaultPosition;
-- (void)windowMainStateChanged;
 @end
 
 class nsCocoaWindow final : public nsBaseWidget, public nsPIWidgetCocoa {
  private:
   typedef nsBaseWidget Inherited;
 
  public:
   nsCocoaWindow();
@@ -253,17 +235,16 @@ class nsCocoaWindow final : public nsBas
   void ReleaseFullscreenTransitionAnimation() {
     MOZ_ASSERT(mFullscreenTransitionAnimation, "Should only be called when there is animation");
     [mFullscreenTransitionAnimation release];
     mFullscreenTransitionAnimation = nil;
   }
 
   virtual void Resize(double aWidth, double aHeight, bool aRepaint) override;
   virtual void Resize(double aX, double aY, double aWidth, double aHeight, bool aRepaint) override;
-  NSRect GetClientCocoaRect();
   virtual LayoutDeviceIntRect GetClientBounds() override;
   virtual LayoutDeviceIntRect GetScreenBounds() override;
   void ReportMoveEvent();
   void ReportSizeEvent();
   virtual void SetCursor(nsCursor aDefaultCursor, imgIContainer* aCursorImage, uint32_t aHotspotX,
                          uint32_t aHotspotY) override;
 
   CGFloat BackingScaleFactor();
--- a/widget/cocoa/nsCocoaWindow.mm
+++ b/widget/cocoa/nsCocoaWindow.mm
@@ -453,17 +453,17 @@ nsresult nsCocoaWindow::CreateNativeWind
   [mWindow setRestorable:NO];
   [mWindow disableSnapshotRestoration];
 
   // setup our notification delegate. Note that setDelegate: does NOT retain.
   mDelegate = [[WindowDelegate alloc] initWithGeckoWindow:this];
   [mWindow setDelegate:mDelegate];
 
   // Make sure that the content rect we gave has been honored.
-  NSRect wantedFrame = [mWindow frameRectForChildViewRect:contentRect];
+  NSRect wantedFrame = [mWindow frameRectForContentRect:contentRect];
   if (!NSEqualRects([mWindow frame], wantedFrame)) {
     // This can happen when the window is not on the primary screen.
     [mWindow setFrame:wantedFrame display:NO];
   }
   UpdateBounds();
 
   if (mWindowType == eWindowType_invisible) {
     [mWindow setLevel:kCGDesktopWindowLevelKey];
@@ -1099,17 +1099,17 @@ void nsCocoaWindow::ConstrainPosition(bo
   NS_OBJC_END_TRY_ABORT_BLOCK;
 }
 
 void nsCocoaWindow::SetSizeConstraints(const SizeConstraints& aConstraints) {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
 
   // Popups can be smaller than (60, 60)
   NSRect rect = (mWindowType == eWindowType_popup) ? NSZeroRect : NSMakeRect(0.0, 0.0, 60, 60);
-  rect = [mWindow frameRectForChildViewRect:rect];
+  rect = [mWindow frameRectForContentRect:rect];
 
   CGFloat scaleFactor = BackingScaleFactor();
 
   SizeConstraints c = aConstraints;
   c.mMinSize.width = std::max(nsCocoaUtils::CocoaPointsToDevPixels(rect.size.width, scaleFactor),
                               c.mMinSize.width);
   c.mMinSize.height = std::max(nsCocoaUtils::CocoaPointsToDevPixels(rect.size.height, scaleFactor),
                                c.mMinSize.height);
@@ -1204,39 +1204,36 @@ void nsCocoaWindow::HideWindowChrome(boo
   // Remove child windows.
   NSArray* childWindows = [mWindow childWindows];
   NSEnumerator* enumerator = [childWindows objectEnumerator];
   NSWindow* child = nil;
   while ((child = [enumerator nextObject])) {
     [mWindow removeChildWindow:child];
   }
 
-  // Remove the views in the old window's content view.
-  // The NSArray is autoreleased and retains its NSViews.
-  NSArray<NSView*>* contentViewContents = [mWindow contentViewContents];
-  for (NSView* view in contentViewContents) {
-    [view removeFromSuperviewWithoutNeedingDisplay];
-  }
+  // Remove the content view.
+  NSView* contentView = [mWindow contentView];
+  [contentView retain];
+  [contentView removeFromSuperviewWithoutNeedingDisplay];
 
   // Save state (like window title).
   NSMutableDictionary* state = [mWindow exportState];
 
   // Recreate the window with the right border style.
   NSRect frameRect = [mWindow frame];
   DestroyNativeWindow();
   nsresult rv = CreateNativeWindow(frameRect, aShouldHide ? eBorderStyle_none : mBorderStyle, true);
   NS_ENSURE_SUCCESS_VOID(rv);
 
   // Re-import state.
   [mWindow importState:state];
 
-  // Add the old content view subviews to the new window's content view.
-  for (NSView* view in contentViewContents) {
-    [[mWindow contentView] addSubview:view];
-  }
+  // Reparent the content view.
+  [mWindow setContentView:contentView];
+  [contentView release];
 
   // Reparent child windows.
   enumerator = [childWindows objectEnumerator];
   while ((child = [enumerator nextObject])) {
     [mWindow addChildWindow:child ordered:NSWindowAbove];
   }
 
   // Show the new window.
@@ -1482,32 +1479,33 @@ void nsCocoaWindow::Resize(double aX, do
 }
 
 // Coordinates are desktop pixels
 void nsCocoaWindow::Resize(double aWidth, double aHeight, bool aRepaint) {
   double invScale = 1.0 / BackingScaleFactor();
   DoResize(mBounds.x * invScale, mBounds.y * invScale, aWidth, aHeight, aRepaint, true);
 }
 
-// Return the area that the Gecko ChildView in our window should cover, as an
-// NSRect in screen coordinates (with 0,0 being the bottom left corner of the
-// primary screen).
-NSRect nsCocoaWindow::GetClientCocoaRect() {
-  if (!mWindow) {
-    return NSZeroRect;
-  }
-
-  return [mWindow childViewRectForFrameRect:[mWindow frame]];
-}
-
 LayoutDeviceIntRect nsCocoaWindow::GetClientBounds() {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
 
   CGFloat scaleFactor = BackingScaleFactor();
-  return nsCocoaUtils::CocoaRectToGeckoRectDevPix(GetClientCocoaRect(), scaleFactor);
+  if (!mWindow) {
+    return nsCocoaUtils::CocoaRectToGeckoRectDevPix(NSZeroRect, scaleFactor);
+  }
+
+  NSRect r;
+  if ([mWindow isKindOfClass:[ToolbarWindow class]] &&
+      [(ToolbarWindow*)mWindow drawsContentsIntoWindowFrame]) {
+    r = [mWindow frame];
+  } else {
+    r = [mWindow contentRectForFrameRect:[mWindow frame]];
+  }
+
+  return nsCocoaUtils::CocoaRectToGeckoRectDevPix(r, scaleFactor);
 
   NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(LayoutDeviceIntRect(0, 0, 0, 0));
 }
 
 void nsCocoaWindow::UpdateBounds() {
   NSRect frame = NSZeroRect;
   if (mWindow) {
     frame = [mWindow frame];
@@ -1853,18 +1851,24 @@ nsresult nsCocoaWindow::SetFocus(bool aS
   }
 
   return NS_OK;
 }
 
 LayoutDeviceIntPoint nsCocoaWindow::WidgetToScreenOffset() {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
 
-  return nsCocoaUtils::CocoaRectToGeckoRectDevPix(GetClientCocoaRect(), BackingScaleFactor())
-      .TopLeft();
+  NSRect rect = NSZeroRect;
+  LayoutDeviceIntRect r;
+  if (mWindow) {
+    rect = [mWindow contentRectForFrameRect:[mWindow frame]];
+  }
+  r = nsCocoaUtils::CocoaRectToGeckoRectDevPix(rect, BackingScaleFactor());
+
+  return r.TopLeft();
 
   NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(LayoutDeviceIntPoint(0, 0));
 }
 
 LayoutDeviceIntPoint nsCocoaWindow::GetClientOffset() {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
 
   LayoutDeviceIntRect clientRect = GetClientBounds();
@@ -1887,18 +1891,18 @@ LayoutDeviceIntSize nsCocoaWindow::Clien
   // i.e. for windows where [mWindow drawsContentsIntoWindowFrame] is NO.
   //
   // So we call frameRectForContentRect on NSWindow here, instead of mWindow, so
   // that we don't run into our override if this window is a window that draws
   // its content into the titlebar.
   //
   // This is the same thing the windows widget does, but we probably should fix
   // that, see bug 1445738.
-  NSUInteger styleMask = [mWindow styleMask];
-  NSRect inflatedRect = [NSWindow frameRectForContentRect:rect styleMask:styleMask];
+  unsigned int features = [mWindow styleMask];
+  NSRect inflatedRect = [NSWindow frameRectForContentRect:rect styleMask:features];
   r = nsCocoaUtils::CocoaRectToGeckoRectDevPix(inflatedRect, backingScale);
   return r.Size();
 
   NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(LayoutDeviceIntSize(0, 0));
 }
 
 nsMenuBarX* nsCocoaWindow::GetMenuBar() { return mMenuBar; }
 
@@ -2403,40 +2407,31 @@ already_AddRefed<nsIWidget> nsIWidget::C
   ChildViewMouseTracker::ReEvaluateMouseEnterState();
 
   // [NSApp _isRunningAppModal] will return true if we're running an OS dialog
   // app modally. If one of those is up then we want it to retain its menu bar.
   if ([NSApp _isRunningAppModal]) return;
   NSWindow* window = [aNotification object];
   if (window) [WindowDelegate paintMenubarForWindow:window];
 
-  if ([window isKindOfClass:[ToolbarWindow class]]) {
-    [(ToolbarWindow*)window windowMainStateChanged];
-  }
-
   NS_OBJC_END_TRY_ABORT_BLOCK;
 }
 
 - (void)windowDidResignMain:(NSNotification*)aNotification {
   RollUpPopups();
   ChildViewMouseTracker::ReEvaluateMouseEnterState();
 
   // [NSApp _isRunningAppModal] will return true if we're running an OS dialog
   // app modally. If one of those is up then we want it to retain its menu bar.
   if ([NSApp _isRunningAppModal]) return;
   RefPtr<nsMenuBarX> hiddenWindowMenuBar = nsMenuUtilsX::GetHiddenWindowMenuBar();
   if (hiddenWindowMenuBar) {
     // printf("painting hidden window menu bar due to window losing main status\n");
     hiddenWindowMenuBar->Paint();
   }
-
-  NSWindow* window = [aNotification object];
-  if ([window isKindOfClass:[ToolbarWindow class]]) {
-    [(ToolbarWindow*)window windowMainStateChanged];
-  }
 }
 
 - (void)windowDidBecomeKey:(NSNotification*)aNotification {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
 
   RollUpPopups();
   ChildViewMouseTracker::ReEvaluateMouseEnterState();
 
@@ -2585,16 +2580,17 @@ already_AddRefed<nsIWidget> nsIWidget::C
 }
 
 @end
 
 @interface NSView (FrameViewMethodSwizzling)
 - (NSPoint)FrameView__closeButtonOrigin;
 - (NSPoint)FrameView__fullScreenButtonOrigin;
 - (BOOL)FrameView__wantsFloatingTitlebar;
+- (NSRect)FrameView__unifiedToolbarFrame;
 @end
 
 @implementation NSView (FrameViewMethodSwizzling)
 
 - (NSPoint)FrameView__closeButtonOrigin {
   NSPoint defaultPosition = [self FrameView__closeButtonOrigin];
   if ([[self window] isKindOfClass:[ToolbarWindow class]]) {
     return [(ToolbarWindow*)[self window] windowButtonsPositionWithDefaultPosition:defaultPosition];
@@ -2610,16 +2606,28 @@ already_AddRefed<nsIWidget> nsIWidget::C
   }
   return defaultPosition;
 }
 
 - (BOOL)FrameView__wantsFloatingTitlebar {
   return NO;
 }
 
+- (NSRect)FrameView__unifiedToolbarFrame {
+  NSRect defaultFrame = [self FrameView__unifiedToolbarFrame];
+  if ([[self window] isKindOfClass:[ToolbarWindow class]]) {
+    CGFloat unifiedToolbarHeight = [(ToolbarWindow*)[self window] unifiedToolbarHeight];
+    CGFloat topEdge = NSMaxY(defaultFrame);
+    CGFloat bottomEdge = topEdge - unifiedToolbarHeight;
+    return NSMakeRect(defaultFrame.origin.x, bottomEdge, defaultFrame.size.width,
+                      unifiedToolbarHeight);
+  }
+  return defaultFrame;
+}
+
 @end
 
 static NSMutableSet* gSwizzledFrameViewClasses = nil;
 
 @interface NSWindow (PrivateSetNeedsDisplayInRectMethod)
 - (void)_setNeedsDisplayInRect:(NSRect)aRect;
 @end
 
@@ -2658,16 +2666,17 @@ static NSMutableSet* gSwizzledFrameViewC
 
 @interface NSView (NSVisualEffectViewSetMaskImage)
 - (void)setMaskImage:(NSImage*)image;
 @end
 
 @interface BaseWindow (Private)
 - (void)removeTrackingArea;
 - (void)cursorUpdated:(NSEvent*)aEvent;
+- (void)updateContentViewSize;
 - (void)reflowTitlebarElements;
 @end
 
 @implementation BaseWindow
 
 // The frame of a window is implemented using undocumented NSView subclasses.
 // We offset the window buttons by overriding the methods _closeButtonOrigin
 // and _fullScreenButtonOrigin on these frame view classes. The class which is
@@ -2688,16 +2697,18 @@ static NSMutableSet* gSwizzledFrameViewC
   }
 
   static IMP our_closeButtonOrigin =
       class_getMethodImplementation([NSView class], @selector(FrameView__closeButtonOrigin));
   static IMP our_fullScreenButtonOrigin =
       class_getMethodImplementation([NSView class], @selector(FrameView__fullScreenButtonOrigin));
   static IMP our_wantsFloatingTitlebar =
       class_getMethodImplementation([NSView class], @selector(FrameView__wantsFloatingTitlebar));
+  static IMP our_unifiedToolbarFrame =
+      class_getMethodImplementation([NSView class], @selector(FrameView__unifiedToolbarFrame));
 
   if (![gSwizzledFrameViewClasses containsObject:frameViewClass]) {
     // Either of these methods might be implemented in both a subclass of
     // NSFrameView and one of its own subclasses.  Which means that if we
     // aren't careful we might end up swizzling the same method twice.
     // Since method swizzling involves swapping pointers, this would break
     // things.
     IMP _closeButtonOrigin =
@@ -2713,16 +2724,22 @@ static NSMutableSet* gSwizzledFrameViewC
                                 @selector(FrameView__fullScreenButtonOrigin));
     }
     IMP _wantsFloatingTitlebar =
         class_getMethodImplementation(frameViewClass, @selector(_wantsFloatingTitlebar));
     if (_wantsFloatingTitlebar && _wantsFloatingTitlebar != our_wantsFloatingTitlebar) {
       nsToolkit::SwizzleMethods(frameViewClass, @selector(_wantsFloatingTitlebar),
                                 @selector(FrameView__wantsFloatingTitlebar));
     }
+    IMP _unifiedToolbarFrame =
+        class_getMethodImplementation(frameViewClass, @selector(_unifiedToolbarFrame));
+    if (_unifiedToolbarFrame && _unifiedToolbarFrame != our_unifiedToolbarFrame) {
+      nsToolkit::SwizzleMethods(frameViewClass, @selector(_unifiedToolbarFrame),
+                                @selector(FrameView__unifiedToolbarFrame));
+    }
     [gSwizzledFrameViewClasses addObject:frameViewClass];
   }
 
   return frameViewClass;
 }
 
 - (id)initWithContentRect:(NSRect)aContentRect
                 styleMask:(NSUInteger)aStyle
@@ -2855,40 +2872,28 @@ static const NSString* kStateCollectionB
             forKey:kStateCollectionBehavior];
   return state;
 }
 
 - (void)setDrawsContentsIntoWindowFrame:(BOOL)aState {
   bool changed = (aState != mDrawsIntoWindowFrame);
   mDrawsIntoWindowFrame = aState;
   if (changed) {
+    [self updateContentViewSize];
     [self reflowTitlebarElements];
+    if ([self respondsToSelector:@selector(setTitlebarAppearsTransparent:)]) {
+      [self setTitlebarAppearsTransparent:mDrawsIntoWindowFrame];
+    }
   }
 }
 
 - (BOOL)drawsContentsIntoWindowFrame {
   return mDrawsIntoWindowFrame;
 }
 
-- (NSRect)childViewRectForFrameRect:(NSRect)aFrameRect {
-  if (mDrawsIntoWindowFrame) {
-    return aFrameRect;
-  }
-  NSUInteger styleMask = [self styleMask];
-  return [NSWindow contentRectForFrameRect:aFrameRect styleMask:styleMask];
-}
-
-- (NSRect)frameRectForChildViewRect:(NSRect)aChildViewRect {
-  if (mDrawsIntoWindowFrame) {
-    return aChildViewRect;
-  }
-  NSUInteger styleMask = [self styleMask];
-  return [NSWindow frameRectForContentRect:aChildViewRect styleMask:styleMask];
-}
-
 - (void)setWantsTitleDrawn:(BOOL)aDrawTitle {
   mDrawTitle = aDrawTitle;
   if ([self respondsToSelector:@selector(setTitleVisibility:)]) {
     [self setTitleVisibility:mDrawTitle ? NSWindowTitleVisible : NSWindowTitleHidden];
   }
 }
 
 - (BOOL)wantsTitleDrawn {
@@ -2904,20 +2909,16 @@ static const NSString* kStateCollectionB
   return mBrightTitlebarForeground;
 }
 
 - (NSView*)trackingAreaView {
   NSView* contentView = [self contentView];
   return [contentView superview] ? [contentView superview] : contentView;
 }
 
-- (NSArray<NSView*>*)contentViewContents {
-  return [[[[self contentView] subviews] copy] autorelease];
-}
-
 - (ChildView*)mainChildView {
   NSView* contentView = [self contentView];
   NSView* lastView = [[contentView subviews] lastObject];
   if ([lastView isKindOfClass:[ChildView class]]) {
     return (ChildView*)lastView;
   }
   return nil;
 }
@@ -2971,39 +2972,68 @@ static const NSString* kStateCollectionB
 }
 
 - (NSRect)getAndResetNativeDirtyRect {
   NSRect dirtyRect = mDirtyRect;
   mDirtyRect = NSZeroRect;
   return dirtyRect;
 }
 
+- (void)updateContentViewSize {
+  NSRect rect = [self contentRectForFrameRect:[self frame]];
+  [[self contentView] setFrameSize:rect.size];
+}
+
 // Possibly move the titlebar buttons.
 - (void)reflowTitlebarElements {
   NSView* frameView = [[self contentView] superview];
   if ([frameView respondsToSelector:@selector(_tileTitlebarAndRedisplay:)]) {
     [frameView _tileTitlebarAndRedisplay:NO];
   }
 }
 
 // Override methods that translate between content rect and frame rect.
 - (NSRect)contentRectForFrameRect:(NSRect)aRect {
-  return aRect;
+  if ([self drawsContentsIntoWindowFrame]) {
+    return aRect;
+  }
+  return [super contentRectForFrameRect:aRect];
 }
 
 - (NSRect)contentRectForFrameRect:(NSRect)aRect styleMask:(NSUInteger)aMask {
-  return aRect;
+  if ([self drawsContentsIntoWindowFrame]) {
+    return aRect;
+  }
+  // Call the instance method on super, if it exists (it's undocumented so we
+  // shouldn't rely on it), or fall back to the (documented) class method.
+  if ([NSWindow instancesRespondToSelector:@selector(contentRectForFrameRect:styleMask:)]) {
+    return [super contentRectForFrameRect:aRect styleMask:aMask];
+  } else {
+    return [NSWindow contentRectForFrameRect:aRect styleMask:aMask];
+  }
 }
 
 - (NSRect)frameRectForContentRect:(NSRect)aRect {
-  return aRect;
+  if ([self drawsContentsIntoWindowFrame]) {
+    return aRect;
+  }
+  return [super frameRectForContentRect:aRect];
 }
 
 - (NSRect)frameRectForContentRect:(NSRect)aRect styleMask:(NSUInteger)aMask {
-  return aRect;
+  if ([self drawsContentsIntoWindowFrame]) {
+    return aRect;
+  }
+  // Call the instance method on super, if it exists (it's undocumented so we
+  // shouldn't rely on it), or fall back to the (documented) class method.
+  if ([NSWindow instancesRespondToSelector:@selector(frameRectForContentRect:styleMask:)]) {
+    return [super frameRectForContentRect:aRect styleMask:aMask];
+  } else {
+    return [NSWindow frameRectForContentRect:aRect styleMask:aMask];
+  }
 }
 
 - (void)setContentView:(NSView*)aView {
   [super setContentView:aView];
 
   // Now move the contentView to the bottommost layer so that it's guaranteed
   // to be under the window buttons.
   NSView* frameView = [aView superview];
@@ -3092,193 +3122,121 @@ static const NSString* kStateCollectionB
 }
 
 - (void)releaseJSObjects {
   [mTouchBar releaseJSObjects];
 }
 
 @end
 
-@interface NSView (NSThemeFrame)
-- (void)_drawTitleStringInClip:(NSRect)aRect;
-- (void)_maskCorners:(NSUInteger)aFlags clipRect:(NSRect)aRect;
-@end
-
-@implementation TitlebarGradientView
-
-- (void)drawRect:(NSRect)aRect {
-  CGContextRef ctx = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
-  ToolbarWindow* window = (ToolbarWindow*)[self window];
-  nsNativeThemeCocoa::DrawNativeTitlebar(ctx, NSRectToCGRect([self bounds]),
-                                         [window unifiedToolbarHeight], [window isMainWindow], NO);
-
-  // The following is only necessary because we're not using
-  // NSFullSizeContentViewWindowMask yet: We need to mask our drawing to the
-  // rounded top corners of the window, and we need to draw the title string
-  // on top. That's because the title string is drawn as part of the frame view
-  // and this view covers that drawing up.
-  // Once we use NSFullSizeContentViewWindowMask and remove our override of
-  // _wantsFloatingTitlebar, Cocoa will draw the title string as part of a
-  // separate view which sits on top of the window's content view, and we'll be
-  // able to remove the code below.
-
-  NSView* frameView = [[[self window] contentView] superview];
-  if (!frameView || ![frameView respondsToSelector:@selector(_maskCorners:clipRect:)] ||
-      ![frameView respondsToSelector:@selector(_drawTitleStringInClip:)]) {
-    return;
-  }
-
-  NSPoint offsetToFrameView = [self convertPoint:NSZeroPoint toView:frameView];
-  NSRect clipRect = {offsetToFrameView, [self bounds].size};
-
-  // Both this view and frameView return NO from isFlipped. Switch into
-  // frameView's coordinate system using a translation by the offset.
-  CGContextSaveGState(ctx);
-  CGContextTranslateCTM(ctx, -offsetToFrameView.x, -offsetToFrameView.y);
-
-  [frameView _maskCorners:2 clipRect:clipRect];
-  [frameView _drawTitleStringInClip:clipRect];
-
-  CGContextRestoreGState(ctx);
-}
-
-- (BOOL)isOpaque {
-  return YES;
-}
-
-- (NSView*)hitTest:(NSPoint)aPoint {
-  return nil;
-}
-
-@end
-
-// This class allows us to exercise control over the window's title bar. It is
-// used for all windows with titlebars.
+// This class allows us to exercise control over the window's title bar. This
+// allows for a "unified toolbar" look without having to extend the content
+// area into the title bar.
 //
-// ToolbarWindow supports two modes:
-//  - drawsContentsIntoWindowFrame mode: In this mode, the Gecko ChildView is
-//    sized to cover the entire window frame and manages titlebar drawing.
-//  - separate titlebar mode, with support for unified toolbars: In this mode,
-//    the Gecko ChildView does not extend into the titlebar. However, this
-//    window's content view (which is the ChildView's superview) *does* extend
-//    into the titlebar. Moreover, in this mode, we place a TitlebarGradientView
-//    in the content view, as a sibling of the ChildView.
-//
-// The "separate titlebar mode" supports the "unified toolbar" look:
-// If there's a toolbar right below the titlebar, the two can "connect" and
-// form a single gradient without a separator line in between.
-//
-// The following mechanism communicates the height of the unified toolbar to
-// the ToolbarWindow:
-//
+// Drawing the unified gradient in the titlebar and the toolbar works like this:
 // 1) In the style sheet we set the toolbar's -moz-appearance to toolbar.
 // 2) When the toolbar is visible and we paint the application chrome
 //    window, the array that Gecko passes nsChildView::UpdateThemeGeometries
 //    will contain an entry for the widget type StyleAppearance::Toolbar.
-// 3) nsChildView::UpdateThemeGeometries passes the toolbar's height, plus the
-//    titlebar height, to -[ToolbarWindow setUnifiedToolbarHeight:].
+// 3) nsChildView::UpdateThemeGeometries finds the toolbar frame's ToolbarWindow
+//    and passes the toolbar frame's height to setUnifiedToolbarHeight.
+// 4) If the toolbar height has changed, a titlebar redraw is triggered and the
+//    upper part of the unified gradient is drawn in the titlebar.
+// 5) The lower part of the unified gradient in the toolbar is drawn during
+//    normal window content painting in nsNativeThemeCocoa::DrawUnifiedToolbar.
 //
-// The actual drawing of the gradient happens in two parts: The titlebar part
-// (i.e. the top 22 pixels of the gradient) is drawn by the TitlebarGradientView,
-// which is a subview of the window's content view and a sibling of the ChildView.
-// The rest of the gradient is drawn by Gecko into the ChildView, as part of the
-// -moz-appearance rendering of the toolbar.
+// Whenever the unified gradient is drawn in the titlebar or the toolbar, both
+// titlebar height and toolbar height must be known in order to construct the
+// correct gradient. But you can only get from the toolbar frame
+// to the containing window - the other direction doesn't work. That's why the
+// toolbar height is cached in the ToolbarWindow but nsNativeThemeCocoa can simply
+// query the window for its titlebar height when drawing the toolbar.
+//
+// Note that in drawsContentsIntoWindowFrame mode, titlebar drawing works in a
+// completely different way: In that mode, the window's mainChildView will
+// cover the titlebar completely and nothing that happens in the window
+// background will reach the screen.
 @implementation ToolbarWindow
 
 - (id)initWithContentRect:(NSRect)aContentRect
                 styleMask:(NSUInteger)aStyle
                   backing:(NSBackingStoreType)aBufferingType
                     defer:(BOOL)aFlag {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
 
   if ((self = [super initWithContentRect:aContentRect
                                styleMask:aStyle
                                  backing:aBufferingType
                                    defer:aFlag])) {
-    mTitlebarGradientView = nil;
     mUnifiedToolbarHeight = 22.0f;
     mSheetAttachmentPosition = aContentRect.size.height;
     mWindowButtonsRect = NSZeroRect;
     mFullScreenButtonRect = NSZeroRect;
 
-    if ([self respondsToSelector:@selector(setTitlebarAppearsTransparent:)]) {
-      [self setTitlebarAppearsTransparent:YES];
-    }
-
-    [self updateTitlebarGradientViewPresence];
+    // setBottomCornerRounded: is a private API call, so we check to make sure
+    // we respond to it just in case.
+    if ([self respondsToSelector:@selector(setBottomCornerRounded:)])
+      [self setBottomCornerRounded:YES];
   }
   return self;
 
   NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
 }
 
-- (void)dealloc {
-  [mTitlebarGradientView release];
-  [super dealloc];
-}
-
-- (NSArray<NSView*>*)contentViewContents {
-  NSMutableArray<NSView*>* contents = [[[self contentView] subviews] mutableCopy];
-  if (mTitlebarGradientView) {
-    // Do not include the titlebar gradient view in the returned array.
-    [contents removeObject:mTitlebarGradientView];
-  }
-  return [contents autorelease];
+- (void)setTitlebarNeedsDisplayInRect:(NSRect)aRect {
+  [self setTitlebarNeedsDisplayInRect:aRect sync:NO];
 }
 
-- (void)updateTitlebarGradientViewPresence {
-  BOOL needTitlebarView = ![self drawsContentsIntoWindowFrame];
-  if (needTitlebarView && !mTitlebarGradientView) {
-    mTitlebarGradientView = [[TitlebarGradientView alloc] initWithFrame:[self titlebarRect]];
-    mTitlebarGradientView.autoresizingMask = NSViewWidthSizable | NSViewMinYMargin;
-    [self.contentView addSubview:mTitlebarGradientView];
-  } else if (!needTitlebarView && mTitlebarGradientView) {
-    [mTitlebarGradientView removeFromSuperview];
-    [mTitlebarGradientView release];
-    mTitlebarGradientView = nil;
+- (void)setTitlebarNeedsDisplayInRect:(NSRect)aRect sync:(BOOL)aSync {
+  NSRect titlebarRect = [self titlebarRect];
+  NSRect rect = NSIntersectionRect(titlebarRect, aRect);
+  if (NSIsEmptyRect(rect)) return;
+
+  NSView* borderView = [[self contentView] superview];
+  if (!borderView) return;
+
+  if (aSync) {
+    [borderView displayRect:rect];
+  } else {
+    [borderView setNeedsDisplayInRect:rect];
   }
 }
 
-- (void)windowMainStateChanged {
-  [self setTitlebarNeedsDisplay];
-}
-
-- (void)setTitlebarNeedsDisplay {
-  [mTitlebarGradientView setNeedsDisplay:YES];
-}
-
 - (NSRect)titlebarRect {
   CGFloat titlebarHeight = [self titlebarHeight];
   return NSMakeRect(0, [self frame].size.height - titlebarHeight, [self frame].size.width,
                     titlebarHeight);
 }
 
 // Returns the unified height of titlebar + toolbar.
 - (CGFloat)unifiedToolbarHeight {
   return mUnifiedToolbarHeight;
 }
 
 - (CGFloat)titlebarHeight {
   // We use the original content rect here, not what we return from
   // [self contentRectForFrameRect:], because that would give us a
-  // titlebarHeight of zero.
+  // titlebarHeight of zero in drawsContentsIntoWindowFrame mode.
   NSRect frameRect = [self frame];
-  NSUInteger styleMask = [self styleMask];
-  NSRect originalContentRect = [NSWindow contentRectForFrameRect:frameRect styleMask:styleMask];
+  NSRect originalContentRect = [NSWindow contentRectForFrameRect:frameRect
+                                                       styleMask:[self styleMask]];
   return NSMaxY(frameRect) - NSMaxY(originalContentRect);
 }
 
 // Stores the complete height of titlebar + toolbar.
 - (void)setUnifiedToolbarHeight:(CGFloat)aHeight {
   if (aHeight == mUnifiedToolbarHeight) return;
 
   mUnifiedToolbarHeight = aHeight;
 
   if (![self drawsContentsIntoWindowFrame]) {
-    [self setTitlebarNeedsDisplay];
+    // Redraw the title bar. If we're inside painting, we'll do it right now,
+    // otherwise we'll just invalidate it.
+    BOOL needSyncRedraw = ([NSView focusView] != nil);
+    [self setTitlebarNeedsDisplayInRect:[self titlebarRect] sync:needSyncRedraw];
   }
 }
 
 // Extending the content area into the title bar works by resizing the
 // mainChildView so that it covers the titlebar.
 - (void)setDrawsContentsIntoWindowFrame:(BOOL)aState {
   BOOL stateChanged = ([self drawsContentsIntoWindowFrame] != aState);
   [super setDrawsContentsIntoWindowFrame:aState];
@@ -3296,23 +3254,21 @@ static const NSString* kStateCollectionB
 
     // Resizing the content area causes a reflow which would send a synthesized
     // mousemove event to the old mouse position relative to the top left
     // corner of the content area. But the mouse has shifted relative to the
     // content area, so that event would have wrong position information. So
     // we'll send a mouse move event with the correct new position.
     ChildViewMouseTracker::ResendLastMouseMoveEvent();
   }
-
-  [self updateTitlebarGradientViewPresence];
 }
 
 - (void)setWantsTitleDrawn:(BOOL)aDrawTitle {
   [super setWantsTitleDrawn:aDrawTitle];
-  [self setTitlebarNeedsDisplay];
+  [self setTitlebarNeedsDisplayInRect:[self titlebarRect]];
 }
 
 - (void)setSheetAttachmentPosition:(CGFloat)aY {
   mSheetAttachmentPosition = aY;
 }
 
 - (CGFloat)sheetAttachmentPosition {
   return mSheetAttachmentPosition;
--- a/widget/cocoa/nsWindowMap.mm
+++ b/widget/cocoa/nsWindowMap.mm
@@ -225,27 +225,31 @@
   NSWindow* window = (NSWindow*)[inNotification object];
 
   id delegate = [window delegate];
   if (!delegate || ![delegate isKindOfClass:[WindowDelegate class]]) {
     [TopLevelWindowData activateInWindowViews:window];
   } else if ([window isSheet]) {
     [TopLevelWindowData activateInWindow:window];
   }
+
+  [[window contentView] setNeedsDisplay:YES];
 }
 
 - (void)windowResignedKey:(NSNotification*)inNotification {
   NSWindow* window = (NSWindow*)[inNotification object];
 
   id delegate = [window delegate];
   if (!delegate || ![delegate isKindOfClass:[WindowDelegate class]]) {
     [TopLevelWindowData deactivateInWindowViews:window];
   } else if ([window isSheet]) {
     [TopLevelWindowData deactivateInWindow:window];
   }
+
+  [[window contentView] setNeedsDisplay:YES];
 }
 
 // The appearance of a top-level window depends on its main state (not its key
 // state).  So (for non-embedders) we need to ensure that a top-level window
 // is main when an NS_ACTIVATE event is sent to Gecko for it.
 - (void)windowBecameMain:(NSNotification*)inNotification {
   NSWindow* window = (NSWindow*)[inNotification object];