Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/widget/cocoa/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 164 kB image not shown  

SSL nsChildView.mm   Sprache: unbekannt

 
/* -*- Mode: objc; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "mozilla/ArrayUtils.h"

#include "mozilla/Logging.h"
#include "mozilla/Unused.h"

#include <unistd.h>
#include <math.h>

#include "nsChildView.h"
#include "nsCocoaWindow.h"

#include "mozilla/Maybe.h"
#include "mozilla/MiscEvents.h"
#include "mozilla/MouseEvents.h"
#include "mozilla/NativeKeyBindingsType.h"
#include "mozilla/PresShell.h"
#include "mozilla/SwipeTracker.h"
#include "mozilla/TextEventDispatcher.h"
#include "mozilla/TextEvents.h"
#include "mozilla/TouchEvents.h"
#include "mozilla/WheelHandlingHelper.h"  // for WheelDeltaAdjustmentStrategy
#include "mozilla/WritingModes.h"
#include "mozilla/dom/DataTransfer.h"
#include "mozilla/dom/MouseEventBinding.h"
#include "mozilla/dom/SimpleGestureEventBinding.h"
#include "mozilla/dom/WheelEventBinding.h"
#include "mozilla/layers/CompositorBridgeChild.h"

#include "nsArrayUtils.h"
#include "nsExceptionHandler.h"
#include "nsObjCExceptions.h"
#include "nsCOMPtr.h"
#include "nsThreadUtils.h"
#include "nsToolkit.h"
#include "nsCRT.h"

#include "nsFontMetrics.h"
#include "nsIRollupListener.h"
#include "nsViewManager.h"
#include "nsIFile.h"
#include "nsILocalFileMac.h"
#include "nsGfxCIID.h"
#include "nsStyleConsts.h"
#include "nsIWidgetListener.h"
#include "nsIScreen.h"

#include "nsDragService.h"
#include "nsClipboard.h"
#include "nsCursorManager.h"
#include "nsWindowMap.h"
#include "nsCocoaUtils.h"
#include "nsMenuUtilsX.h"
#include "nsMenuBarX.h"
#include "NativeKeyBindings.h"
#include "MacThemeGeometryType.h"

#include "gfxContext.h"
#include "gfxQuartzSurface.h"
#include "gfxUtils.h"
#include "nsRegion.h"
#include "GfxTexturesReporter.h"
#include "GLTextureImage.h"
#include "GLContextProvider.h"
#include "GLContextCGL.h"
#include "OGLShaderProgram.h"
#include "ScopedGLHelpers.h"
#include "HeapCopyOfStackArray.h"
#include "mozilla/layers/IAPZCTreeManager.h"
#include "mozilla/layers/APZInputBridge.h"
#include "mozilla/layers/APZThreadUtils.h"
#include "mozilla/layers/CompositorOGL.h"
#include "mozilla/layers/CompositorBridgeParent.h"
#include "mozilla/layers/InputAPZContext.h"
#include "mozilla/layers/IpcResourceUpdateQueue.h"
#include "mozilla/layers/NativeLayerCA.h"
#include "mozilla/layers/WebRenderBridgeChild.h"
#include "mozilla/layers/WebRenderLayerManager.h"
#include "mozilla/webrender/WebRenderAPI.h"
#include "mozilla/widget/CompositorWidget.h"
#include "mozilla/widget/Screen.h"
#include "gfxUtils.h"
#include "mozilla/gfx/2D.h"
#include "mozilla/gfx/BorrowedContext.h"
#ifdef ACCESSIBILITY
#  include "nsAccessibilityService.h"
#  include "mozilla/a11y/Platform.h"
#endif

#include "mozilla/Preferences.h"
#include "mozilla/StaticPrefs_apz.h"
#include "mozilla/StaticPrefs_browser.h"
#include "mozilla/StaticPrefs_dom.h"
#include "mozilla/StaticPrefs_general.h"
#include "mozilla/StaticPrefs_gfx.h"
#include "mozilla/StaticPrefs_ui.h"

#include <dlfcn.h>

#include <ApplicationServices/ApplicationServices.h>

#include "GeckoProfiler.h"

#include "mozilla/layers/ChromeProcessController.h"
#include "nsLayoutUtils.h"
#include "InputData.h"
#include "VibrancyManager.h"
#include "nsNativeThemeCocoa.h"
#include "nsIDOMWindowUtils.h"
#include "Units.h"
#include "UnitTransforms.h"
#include "mozilla/UniquePtrExtensions.h"
#include "CustomCocoaEvents.h"
#include "NativeMenuSupport.h"

using namespace mozilla;
using namespace mozilla::layers;
using namespace mozilla::gl;
using namespace mozilla::widget;

using mozilla::gfx::Matrix4x4;

#undef DEBUG_UPDATE
#undef INVALIDATE_DEBUGGING  // flash areas as they are invalidated

// Don't put more than this many rects in the dirty region, just fluff
// out to the bounding-box if there are more
#define MAX_RECTS_IN_REGION 100

LazyLogModule sCocoaLog("nsCocoaWidgets");

extern "C" {
CG_EXTERN void CGContextResetCTM(CGContextRef);
CG_EXTERN void CGContextSetCTM(CGContextRef, CGAffineTransform);
CG_EXTERN void CGContextResetClip(CGContextRef);

typedef CFTypeRef CGSRegionObj;
CGError CGSNewRegionWithRect(const CGRect* rect, CGSRegionObj* outRegion);
CGError CGSNewRegionWithRectList(const CGRect* rects, int rectCount,
                                 CGSRegionObj* outRegion);
}

// defined in nsMenuBarX.mm
extern NSMenu* sApplicationMenu;  // Application menu shared by all menubars

extern nsIArray* gDraggedTransferables;

ChildView* ChildViewMouseTracker::sLastMouseEventView = nil;
NSEvent* ChildViewMouseTracker::sLastMouseMoveEvent = nil;
NSWindow* ChildViewMouseTracker::sWindowUnderMouse = nil;
MOZ_RUNINIT NSPoint ChildViewMouseTracker::sLastScrollEventScreenLocation =
    NSZeroPoint;

#ifdef INVALIDATE_DEBUGGING
static void blinkRect(Rect* r);
static void blinkRgn(RgnHandle rgn);
#endif

bool gUserCancelledDrag = false;

uint32_t nsChildView::sLastInputEventCount = 0;

static bool sIsTabletPointerActivated = false;

static uint32_t sUniqueKeyEventId = 0;

// The view that will do our drawing or host our NSOpenGLContext or Core
// Animation layer.
@interface PixelHostingView : NSView {
}

@end

@interface ChildView (Private)

// sets up our view, attaching it to its owning gecko view
- (id)initWithFrame:(NSRect)inFrame geckoChild:(nsChildView*)inChild;

// set up a gecko mouse event based on a cocoa mouse event
- (void)convertCocoaMouseWheelEvent:(NSEvent*)aMouseEvent
                       toGeckoEvent:(WidgetWheelEvent*)outWheelEvent;
- (void)convertCocoaMouseEvent:(NSEvent*)aMouseEvent
                  toGeckoEvent:(WidgetInputEvent*)outGeckoEvent;
- (void)convertCocoaTabletPointerEvent:(NSEvent*)aMouseEvent
                          toGeckoEvent:(WidgetMouseEvent*)outGeckoEvent;
- (NSMenu*)contextMenu;

- (void)markLayerForDisplay;
- (CALayer*)rootCALayer;
- (void)updateRootCALayer;

#ifdef ACCESSIBILITY
- (id<mozAccessible>)accessible;
#endif

- (LayoutDeviceIntPoint)convertWindowCoordinates:(NSPoint)aPoint;
- (LayoutDeviceIntPoint)convertWindowCoordinatesRoundDown:(NSPoint)aPoint;

- (BOOL)inactiveWindowAcceptsMouseEvent:(NSEvent*)aEvent;
- (void)updateWindowDraggableState;

- (bool)beginOrEndGestureForEventPhase:(NSEvent*)aEvent;

@end

#pragma mark -

// Flips a screen coordinate from a point in the cocoa coordinate system
// (bottom-left rect) to a point that is a "flipped" cocoa coordinate system
// (starts in the top-left).
static inline void FlipCocoaScreenCoordinate(NSPoint& inPoint) {
  inPoint.y = nsCocoaUtils::FlippedScreenY(inPoint.y);
}

#pragma mark -

nsChildView::nsChildView()
    : mView(nullptr),
      mParentView(nil),
      mCompositingLock("ChildViewCompositing"),
      mBackingScaleFactor(0.0),
      mVisible(false),
      mSizeMode(nsSizeMode_Normal),
      mDrawing(false),
      mIsDispatchPaint(false) {}

nsChildView::~nsChildView() {
  RemoveAllChildren();

  NS_WARNING_ASSERTION(
      mOnDestroyCalled,
      "nsChildView object destroyed without calling Destroy()");

  if (mContentLayer) {
    mNativeLayerRoot->RemoveLayer(mContentLayer);  // safe if already removed
  }

  DestroyCompositor();

  // An nsChildView object that was in use can be destroyed without Destroy()
  // ever being called on it.  So we also need to do a quick, safe cleanup
  // here (it's too late to just call Destroy(), which can cause crashes).
  // It's particularly important to make sure widgetDestroyed is called on our
  // mView -- this method NULLs mView's mGeckoChild, and NULL checks on
  // mGeckoChild are used throughout the ChildView class to tell if it's safe
  // to use a ChildView object.
  [mView widgetDestroyed];  // Safe if mView is nil.
  ClearParent();
  TearDownView();  // Safe if called twice.
}

nsresult nsChildView::Create(nsIWidget* aParent,
                             const LayoutDeviceIntRect& aRect,
                             widget::InitData* aInitData) {
  NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;

  // Because the hidden window is created outside of an event loop,
  // we need to provide an autorelease pool to avoid leaking cocoa objects
  // (see bug 559075).
  nsAutoreleasePool localPool;

  mBounds = aRect;

  // Ensure that the toolkit is created.
  nsToolkit::GetToolkit();

  BaseCreate(aParent, aInitData);

  mParentView =
      mParent ? (NSView*)mParent->GetNativeData(NS_NATIVE_WIDGET) : nullptr;

  // create our parallel NSView and hook it up to our parent. Recall
  // that NS_NATIVE_WIDGET is the NSView.
  CGFloat scaleFactor = nsCocoaUtils::GetBackingScaleFactor(mParentView);
  NSRect r = nsCocoaUtils::DevPixelsToCocoaPoints(mBounds, scaleFactor);
  mView = [[ChildView alloc] initWithFrame:r geckoChild:this];

  mNativeLayerRoot = NativeLayerRootCA::CreateForCALayer([mView rootCALayer]);
  mNativeLayerRoot->SetBackingScale(scaleFactor);

  // If this view was created in a Gecko view hierarchy, the initial state
  // is hidden.  If the view is attached only to a native NSView but has
  // no Gecko parent (as in embedding), the initial state is visible.
  if (mParent) {
    [mView setHidden:YES];
  } else {
    mVisible = true;
  }

  // Hook it up in the NSView hierarchy.
  if (mParentView) {
    [mParentView addSubview:mView];
  }

  // if this is a ChildView, make sure that our per-window data
  // is set up
  if ([mView isKindOfClass:[ChildView class]]) {
    [[WindowDataMap sharedWindowDataMap] ensureDataForWindow:[mView window]];
  }

  NS_ASSERTION(!mTextInputHandler, "mTextInputHandler has already existed");
  mTextInputHandler = new TextInputHandler(this, mView);

  return NS_OK;

  NS_OBJC_END_TRY_BLOCK_RETURN(NS_ERROR_FAILURE);
}

void nsChildView::TearDownView() {
  NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;

  if (!mView) return;

  NSWindow* win = [mView window];
  NSResponder* responder = [win firstResponder];

  // We're being unhooked from the view hierarchy, don't leave our view
  // or a child view as the window first responder.
  if (responder && [responder isKindOfClass:[NSView class]] &&
      [(NSView*)responder isDescendantOf:mView]) {
    [win makeFirstResponder:[mView superview]];
  }

  // If mView is win's contentView, win (mView's NSWindow) "owns" mView --
  // win has retained mView, and will detach it from the view hierarchy and
  // release it when necessary (when win is itself destroyed (in a call to
  // [win dealloc])).  So all we need to do here is call [mView release] (to
  // match the call to [mView retain] in nsChildView::StandardCreate()).
  // Also calling [mView removeFromSuperviewWithoutNeedingDisplay] causes
  // mView to be released again and dealloced, while remaining win's
  // contentView.  So if we do that here, win will (for a short while) have
  // an invalid contentView (for the consequences see bmo bugs 381087 and
  // 374260).
  if ([mView isEqual:[win contentView]]) {
    [mView release];
  } else {
    // Stop NSView hierarchy being changed during [ChildView drawRect:]
    [mView performSelectorOnMainThread:@selector(delayedTearDown)
                            withObject:nil
                         waitUntilDone:false];
  }
  mView = nil;

  NS_OBJC_END_TRY_IGNORE_BLOCK;
}

nsCocoaWindow* nsChildView::GetAppWindowWidget() const {
  id windowDelegate = [[mView window] delegate];
  if (windowDelegate && [windowDelegate isKindOfClass:[WindowDelegate class]]) {
    return [(WindowDelegate*)windowDelegate geckoWidget];
  }
  return nullptr;
}

void nsChildView::Destroy() {
  NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;

  if (mOnDestroyCalled) {
    return;
  }
  mOnDestroyCalled = true;

  // Stuff below may delete the last ref to this
  nsCOMPtr<nsIWidget> kungFuDeathGrip(this);

  {
    // Make sure that no composition is in progress while disconnecting
    // ourselves from the view.
    MutexAutoLock lock(mCompositingLock);

    [mView widgetDestroyed];
  }

  nsBaseWidget::Destroy();

  NotifyWindowDestroyed();

  TearDownView();

  nsBaseWidget::OnDestroy();

  NS_OBJC_END_TRY_IGNORE_BLOCK;
}

#pragma mark -

#if 0
static void PrintViewHierarchy(NSView *view)
{
  while (view) {
    NSLog(@"  view is %x, frame %@", view, NSStringFromRect([view frame]));
    view = [view superview];
  }
}
#endif

// Return native data according to aDataType
void* nsChildView::GetNativeData(uint32_t aDataType) {
  NS_OBJC_BEGIN_TRY_BLOCK_RETURN;

  void* retVal = nullptr;

  switch (aDataType) {
    case NS_NATIVE_WIDGET:
      retVal = (void*)mView;
      break;

    case NS_NATIVE_WINDOW:
      retVal = [mView window];
      break;

    case NS_NATIVE_GRAPHIC:
      NS_ERROR("Requesting NS_NATIVE_GRAPHIC on a Mac OS X child view!");
      retVal = nullptr;
      break;

    case NS_NATIVE_OFFSETX:
      retVal = 0;
      break;

    case NS_NATIVE_OFFSETY:
      retVal = 0;
      break;

    case NS_RAW_NATIVE_IME_CONTEXT:
      retVal = GetPseudoIMEContext();
      if (retVal) {
        break;
      }
      retVal = [mView inputContext];
      // If input context isn't available on this widget, we should set |this|
      // instead of nullptr since if this returns nullptr, IMEStateManager
      // cannot manage composition with TextComposition instance.  Although,
      // this case shouldn't occur.
      if (NS_WARN_IF(!retVal)) {
        retVal = this;
      }
      break;

    case NS_NATIVE_WINDOW_WEBRTC_DEVICE_ID: {
      NSWindow* win = [mView window];
      if (win) {
        retVal = (void*)[win windowNumber];
      }
      break;
    }
  }

  return retVal;

  NS_OBJC_END_TRY_BLOCK_RETURN(nullptr);
}

#pragma mark -

void nsChildView::SuppressAnimation(bool aSuppress) {
  if (nsCocoaWindow* widget = GetAppWindowWidget()) {
    widget->SuppressAnimation(aSuppress);
  }
}

bool nsChildView::IsVisible() const {
  NS_OBJC_BEGIN_TRY_BLOCK_RETURN;

  if (!mVisible) {
    return false;
  }

  nsCocoaWindow* widget = GetAppWindowWidget();
  if (NS_WARN_IF(!widget) || !widget->IsVisible()) {
    return false;
  }

  // mVisible does not accurately reflect the state of a hidden tabbed view
  // so verify that the view has a window as well
  // then check native widget hierarchy visibility
  return ([mView window] != nil) && !NSIsEmptyRect([mView visibleRect]);

  NS_OBJC_END_TRY_BLOCK_RETURN(false);
}

// Some NSView methods (e.g. setFrame and setHidden) invalidate the view's
// bounds in our window. However, we don't want these invalidations because
// they are unnecessary and because they actually slow us down since we
// block on the compositor inside drawRect.
// When we actually need something invalidated, there will be an explicit call
// to Invalidate from Gecko, so turning these automatic invalidations off
// won't hurt us in the non-OMTC case.
// The invalidations inside these NSView methods happen via a call to the
// private method -[NSWindow _setNeedsDisplayInRect:]. Our BaseWindow
// implementation of that method is augmented to let us ignore those calls
// using -[BaseWindow disable/enableSetNeedsDisplay].
static void ManipulateViewWithoutNeedingDisplay(NSView* aView,
                                                void (^aCallback)()) {
  BaseWindow* win = nil;
  if ([[aView window] isKindOfClass:[BaseWindow class]]) {
    win = (BaseWindow*)[aView window];
  }
  [win disableSetNeedsDisplay];
  aCallback();
  [win enableSetNeedsDisplay];
}

// Hide or show this component
void nsChildView::Show(bool aState) {
  NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;

  if (aState != mVisible) {
    // Provide an autorelease pool because this gets called during startup
    // on the "hidden window", resulting in cocoa object leakage if there's
    // no pool in place.
    nsAutoreleasePool localPool;

    ManipulateViewWithoutNeedingDisplay(mView, ^{
      [mView setHidden:!aState];
    });

    mVisible = aState;
  }

  NS_OBJC_END_TRY_IGNORE_BLOCK;
}

// Change the parent of this widget
void nsChildView::DidClearParent(nsIWidget*) {
  NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;

  if (mOnDestroyCalled) {
    return;
  }

  // we hold a ref to mView, so this is safe
  [mView removeFromSuperview];

  NS_OBJC_END_TRY_IGNORE_BLOCK;
}

float nsChildView::GetDPI() {
  float dpi = 96.0;
  nsCOMPtr<nsIScreen> screen = GetWidgetScreen();
  if (screen) {
    screen->GetDpi(&dpi);
  }
  return dpi;
}

void nsChildView::Enable(bool aState) {}

bool nsChildView::IsEnabled() const { return true; }

void nsChildView::SetFocus(Raise, mozilla::dom::CallerType aCallerType) {
  NS_OBJC_BEGIN_TRY_BLOCK_RETURN;

  NSWindow* window = [mView window];
  if (window) [window makeFirstResponder:mView];
  NS_OBJC_END_TRY_IGNORE_BLOCK;
}

// Override to set the cursor on the mac
void nsChildView::SetCursor(const Cursor& aCursor) {
  NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;

  if ([mView isDragInProgress]) {
    return;  // Don't change the cursor during dragging.
  }

  nsBaseWidget::SetCursor(aCursor);

  bool forceUpdate = mUpdateCursor;
  mUpdateCursor = false;
  if (mCustomCursorAllowed && NS_SUCCEEDED([[nsCursorManager sharedInstance]
                                    setCustomCursor:aCursor
                                  widgetScaleFactor:BackingScaleFactor()
                                        forceUpdate:forceUpdate])) {
    return;
  }

  [[nsCursorManager sharedInstance] setNonCustomCursor:aCursor];

  NS_OBJC_END_TRY_IGNORE_BLOCK;
}

#pragma mark -

// Get this component dimension
LayoutDeviceIntRect nsChildView::GetBounds() {
  return !mView ? mBounds : CocoaPointsToDevPixels([mView frame]);
}

LayoutDeviceIntRect nsChildView::GetClientBounds() {
  LayoutDeviceIntRect rect = GetBounds();
  if (!mParent) {
    // For top level widgets we want the position on screen, not the position
    // of this view inside the window.
    rect.MoveTo(WidgetToScreenOffset());
  }
  return rect;
}

LayoutDeviceIntRect nsChildView::GetScreenBounds() {
  LayoutDeviceIntRect rect = GetBounds();
  rect.MoveTo(WidgetToScreenOffset());
  return rect;
}

double nsChildView::GetDefaultScaleInternal() { return BackingScaleFactor(); }

CGFloat nsChildView::BackingScaleFactor() const {
  if (mBackingScaleFactor > 0.0) {
    return mBackingScaleFactor;
  }
  if (!mView) {
    return 1.0;
  }
  mBackingScaleFactor = nsCocoaUtils::GetBackingScaleFactor(mView);
  return mBackingScaleFactor;
}

void nsChildView::BackingScaleFactorChanged() {
  CGFloat newScale = nsCocoaUtils::GetBackingScaleFactor(mView);

  // ignore notification if it hasn't really changed (or maybe we have
  // disabled HiDPI mode via prefs)
  if (mBackingScaleFactor == newScale) {
    return;
  }

  SuspendAsyncCATransactions();
  mBackingScaleFactor = newScale;
  NSRect frame = mView.frame;
  mBounds = nsCocoaUtils::CocoaRectToGeckoRectDevPix(frame, newScale);

  mNativeLayerRoot->SetBackingScale(mBackingScaleFactor);

  if (mWidgetListener && !mWidgetListener->GetAppWindow()) {
    if (PresShell* presShell = mWidgetListener->GetPresShell()) {
      presShell->BackingScaleFactorChanged();
    }
  }
}

int32_t nsChildView::RoundsWidgetCoordinatesTo() {
  if (BackingScaleFactor() == 2.0) {
    return 2;
  }
  return 1;
}

// Move this component, aX and aY are in the parent widget coordinate system
void nsChildView::Move(double aX, double aY) {
  NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;

  int32_t x = NSToIntRound(aX);
  int32_t y = NSToIntRound(aY);

  if (!mView || (mBounds.x == x && mBounds.y == y)) return;

  mBounds.x = x;
  mBounds.y = y;

  ManipulateViewWithoutNeedingDisplay(mView, ^{
    [mView setFrame:DevPixelsToCocoaPoints(mBounds)];
  });

  ReportMoveEvent();

  NS_OBJC_END_TRY_IGNORE_BLOCK;
}

void nsChildView::Resize(double aWidth, double aHeight, bool aRepaint) {
  NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;

  int32_t width = NSToIntRound(aWidth);
  int32_t height = NSToIntRound(aHeight);

  if (!mView || (mBounds.width == width && mBounds.height == height)) return;

  SuspendAsyncCATransactions();
  mBounds.width = width;
  mBounds.height = height;

  ManipulateViewWithoutNeedingDisplay(mView, ^{
    mView.frame = DevPixelsToCocoaPoints(mBounds);
  });

  if (mVisible && aRepaint) {
    mView.pixelHostingView.needsDisplay = YES;
  }

  ReportSizeEvent();

  NS_OBJC_END_TRY_IGNORE_BLOCK;
}

void nsChildView::Resize(double aX, double aY, double aWidth, double aHeight,
                         bool aRepaint) {
  NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;

  int32_t x = NSToIntRound(aX);
  int32_t y = NSToIntRound(aY);
  int32_t width = NSToIntRound(aWidth);
  int32_t height = NSToIntRound(aHeight);

  BOOL isMoving = (mBounds.x != x || mBounds.y != y);
  BOOL isResizing = (mBounds.width != width || mBounds.height != height);
  if (!mView || (!isMoving && !isResizing)) return;

  if (isMoving) {
    mBounds.x = x;
    mBounds.y = y;
  }
  if (isResizing) {
    SuspendAsyncCATransactions();
    mBounds.width = width;
    mBounds.height = height;

    CALayer* layer = [mView rootCALayer];
    double scale = BackingScaleFactor();
    layer.bounds = CGRectMake(0, 0, width / scale, height / scale);
  }

  ManipulateViewWithoutNeedingDisplay(mView, ^{
    mView.frame = DevPixelsToCocoaPoints(mBounds);
  });

  if (mVisible && aRepaint) {
    mView.pixelHostingView.needsDisplay = YES;
  }

  if (isMoving) {
    ReportMoveEvent();
    if (mOnDestroyCalled) return;
  }
  if (isResizing) ReportSizeEvent();

  NS_OBJC_END_TRY_IGNORE_BLOCK;
}

// The following three methods are primarily an attempt to avoid glitches during
// window resizing.
// Here's some background on how these glitches come to be:
// CoreAnimation transactions are per-thread. They don't nest across threads.
// If you submit a transaction on the main thread and a transaction on a
// different thread, the two will race to the window server and show up on the
// screen in the order that they happen to arrive in at the window server.
// When the window size changes, there's another event that needs to be
// synchronized with: the window "shape" change. Cocoa has built-in
// synchronization mechanics that make sure that *main thread* window paints
// during window resizes are synchronized properly with the window shape change.
// But no such built-in synchronization exists for CATransactions that are
// triggered on a non-main thread. To cope with this, we define a "danger zone"
// during which we simply avoid triggering any CATransactions on a non-main
// thread (called "async" CATransactions here). This danger zone starts at the
// earliest opportunity at which we know about the size change, which is
// nsChildView::Resize, and ends at a point at which we know for sure that the
// paint has been handled completely, which is when we return to the event loop
// after layer display.
void nsChildView::SuspendAsyncCATransactions() {
  if (mUnsuspendAsyncCATransactionsRunnable) {
    mUnsuspendAsyncCATransactionsRunnable->Cancel();
    mUnsuspendAsyncCATransactionsRunnable = nullptr;
  }

  // Make sure that there actually will be a CATransaction on the main thread
  // during which we get a chance to schedule unsuspension. Otherwise we might
  // accidentally stay suspended indefinitely.
  [mView markLayerForDisplay];

  // Ensure that whatever we are going to do does sync flushes of the
  // rendering pipeline, giving us smooth animation.
  if (mCompositorBridgeChild) {
    mCompositorBridgeChild->SetForceSyncFlushRendering(true);
  }
  mNativeLayerRoot->SuspendOffMainThreadCommits();
}

void nsChildView::MaybeScheduleUnsuspendAsyncCATransactions() {
  if (mNativeLayerRoot->AreOffMainThreadCommitsSuspended() &&
      !mUnsuspendAsyncCATransactionsRunnable) {
    mUnsuspendAsyncCATransactionsRunnable = NewCancelableRunnableMethod(
        "nsChildView::MaybeScheduleUnsuspendAsyncCATransactions", this,
        &nsChildView::UnsuspendAsyncCATransactions);
    NS_DispatchToMainThread(mUnsuspendAsyncCATransactionsRunnable);
  }
}

void nsChildView::UnsuspendAsyncCATransactions() {
  mUnsuspendAsyncCATransactionsRunnable = nullptr;

  if (mNativeLayerRoot->UnsuspendOffMainThreadCommits()) {
    // We need to call mNativeLayerRoot->CommitToScreen() at the next available
    // opportunity.
    // The easiest way to handle this request is to mark the layer as needing
    // display, because this will schedule a main thread CATransaction, during
    // which HandleMainThreadCATransaction will call CommitToScreen().
    [mView markLayerForDisplay];
  }

  // We're done with our critical animation, so allow aysnc flushes again.
  if (mCompositorBridgeChild) {
    mCompositorBridgeChild->SetForceSyncFlushRendering(false);
  }
}

void nsChildView::UpdateFullscreen(bool aFullscreen) {
  if (mNativeLayerRoot) {
    mNativeLayerRoot->SetWindowIsFullscreen(aFullscreen);
  }
}

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);
}

nsresult nsChildView::SynthesizeNativeMouseEvent(
    LayoutDeviceIntPoint aPoint, NativeMouseMessage aNativeMessage,
    MouseButton aButton, nsIWidget::Modifiers aModifierFlags,
    nsIObserver* aObserver) {
  NS_OBJC_BEGIN_TRY_BLOCK_RETURN;

  AutoObserverNotifier notifier(aObserver, "mouseevent");

  NSPoint pt =
      nsCocoaUtils::DevPixelsToCocoaPoints(aPoint, BackingScaleFactor());

  // Move the mouse cursor to the requested position and reconnect it to the
  // mouse.
  CGWarpMouseCursorPosition(NSPointToCGPoint(pt));
  CGAssociateMouseAndMouseCursorPosition(true);

  // aPoint is given with the origin on the top left, but convertScreenToBase
  // expects a point in a coordinate system that has its origin on the bottom
  // left.
  NSPoint screenPoint = NSMakePoint(pt.x, nsCocoaUtils::FlippedScreenY(pt.y));
  NSPoint windowPoint =
      nsCocoaUtils::ConvertPointFromScreen([mView window], screenPoint);
  NSEventModifierFlags modifierFlags =
      nsCocoaUtils::ConvertWidgetModifiersToMacModifierFlags(aModifierFlags);

  if (aButton == MouseButton::eX1 || aButton == MouseButton::eX2) {
    // NSEvent has `buttonNumber` for `NSEventTypeOther*`.  However, it seems
    // that there is no way to specify it.  Therefore, we should return error
    // for now.
    return NS_ERROR_INVALID_ARG;
  }

  NSEventType nativeEventType;
  switch (aNativeMessage) {
    case NativeMouseMessage::ButtonDown:
    case NativeMouseMessage::ButtonUp: {
      switch (aButton) {
        case MouseButton::ePrimary:
          nativeEventType = aNativeMessage == NativeMouseMessage::ButtonDown
                                ? NSEventTypeLeftMouseDown
                                : NSEventTypeLeftMouseUp;
          break;
        case MouseButton::eMiddle:
          nativeEventType = aNativeMessage == NativeMouseMessage::ButtonDown
                                ? NSEventTypeOtherMouseDown
                                : NSEventTypeOtherMouseUp;
          break;
        case MouseButton::eSecondary:
          nativeEventType = aNativeMessage == NativeMouseMessage::ButtonDown
                                ? NSEventTypeRightMouseDown
                                : NSEventTypeRightMouseUp;
          break;
        default:
          return NS_ERROR_INVALID_ARG;
      }
      break;
    }
    case NativeMouseMessage::Move:
      nativeEventType = NSEventTypeMouseMoved;
      break;
    case NativeMouseMessage::EnterWindow:
      nativeEventType = NSEventTypeMouseEntered;
      break;
    case NativeMouseMessage::LeaveWindow:
      nativeEventType = NSEventTypeMouseExited;
      break;
  }

  NSEvent* event =
      [NSEvent mouseEventWithType:nativeEventType
                         location:windowPoint
                    modifierFlags:modifierFlags
                        timestamp:[[NSProcessInfo processInfo] systemUptime]
                     windowNumber:[[mView window] windowNumber]
                          context:nil
                      eventNumber:0
                       clickCount:1
                         pressure:0.0];

  if (!event) return NS_ERROR_FAILURE;

  if ([[mView window] isKindOfClass:[BaseWindow class]]) {
    // Tracking area events don't end up in their tracking areas when sent
    // through [NSApp sendEvent:], so pass them directly to the right methods.
    BaseWindow* window = (BaseWindow*)[mView window];
    if (nativeEventType == NSEventTypeMouseEntered) {
      [window mouseEntered:event];
      return NS_OK;
    }
    if (nativeEventType == NSEventTypeMouseExited) {
      [window mouseExited:event];
      return NS_OK;
    }
    if (nativeEventType == NSEventTypeMouseMoved) {
      [window mouseMoved:event];
      return NS_OK;
    }
  }

  [NSApp sendEvent:event];
  return NS_OK;

  NS_OBJC_END_TRY_BLOCK_RETURN(NS_ERROR_FAILURE);
}

nsresult nsChildView::SynthesizeNativeMouseScrollEvent(
    mozilla::LayoutDeviceIntPoint aPoint, uint32_t aNativeMessage,
    double aDeltaX, double aDeltaY, double aDeltaZ, uint32_t aModifierFlags,
    uint32_t aAdditionalFlags, nsIObserver* aObserver) {
  NS_OBJC_BEGIN_TRY_BLOCK_RETURN;

  AutoObserverNotifier notifier(aObserver, "mousescrollevent");

  NSPoint pt =
      nsCocoaUtils::DevPixelsToCocoaPoints(aPoint, BackingScaleFactor());

  // Move the mouse cursor to the requested position and reconnect it to the
  // mouse.
  CGWarpMouseCursorPosition(NSPointToCGPoint(pt));
  CGAssociateMouseAndMouseCursorPosition(true);

  // Mostly copied from http://stackoverflow.com/a/6130349
  CGScrollEventUnit units =
      (aAdditionalFlags & nsIDOMWindowUtils::MOUSESCROLL_SCROLL_LINES)
          ? kCGScrollEventUnitLine
          : kCGScrollEventUnitPixel;
  CGEventRef cgEvent = CGEventCreateScrollWheelEvent(
      NULL, units, 3, (int32_t)aDeltaY, (int32_t)aDeltaX, (int32_t)aDeltaZ);
  if (!cgEvent) {
    return NS_ERROR_FAILURE;
  }

  if (aNativeMessage) {
    CGEventSetIntegerValueField(cgEvent, kCGScrollWheelEventScrollPhase,
                                aNativeMessage);
  }

  // On macOS 10.14 and up CGEventPost won't work because of changes in macOS
  // to improve security. This code makes an NSEvent corresponding to the
  // wheel event and dispatches it directly to the scrollWheel handler. Some
  // fiddling is needed with the coordinates in order to simulate what macOS
  // would do; this code adapted from the Chromium equivalent function at
  // https://chromium.googlesource.com/chromium/src.git/+/62.0.3178.1/ui/events/test/cocoa_test_event_utils.mm#38
  CGPoint location = CGEventGetLocation(cgEvent);
  location.y += NSMinY([[mView window] frame]);
  location.x -= NSMinX([[mView window] frame]);
  CGEventSetLocation(cgEvent, location);

  uint64_t kNanosPerSec = 1000000000L;
  CGEventSetTimestamp(
      cgEvent, [[NSProcessInfo processInfo] systemUptime] * kNanosPerSec);

  NSEvent* event = [NSEvent eventWithCGEvent:cgEvent];
  [event setValue:[mView window] forKey:@"_window"];
  [mView scrollWheel:event];

  CFRelease(cgEvent);
  return NS_OK;

  NS_OBJC_END_TRY_BLOCK_RETURN(NS_ERROR_FAILURE);
}

nsresult nsChildView::SynthesizeNativeTouchPoint(
    uint32_t aPointerId, TouchPointerState aPointerState,
    mozilla::LayoutDeviceIntPoint aPoint, double aPointerPressure,
    uint32_t aPointerOrientation, nsIObserver* aObserver) {
  NS_OBJC_BEGIN_TRY_BLOCK_RETURN;

  AutoObserverNotifier notifier(aObserver, "touchpoint");

  MOZ_ASSERT(NS_IsMainThread());
  if (aPointerState == TOUCH_HOVER) {
    return NS_ERROR_UNEXPECTED;
  }

  if (!mSynthesizedTouchInput) {
    mSynthesizedTouchInput = MakeUnique<MultiTouchInput>();
  }

  LayoutDeviceIntPoint pointInWindow = aPoint - WidgetToScreenOffset();
  MultiTouchInput inputToDispatch = UpdateSynthesizedTouchState(
      mSynthesizedTouchInput.get(), TimeStamp::Now(), aPointerId, aPointerState,
      pointInWindow, aPointerPressure, aPointerOrientation);
  DispatchTouchInput(inputToDispatch);
  return NS_OK;

  NS_OBJC_END_TRY_BLOCK_RETURN(NS_ERROR_FAILURE);
}

nsresult nsChildView::SynthesizeNativeTouchpadDoubleTap(
    mozilla::LayoutDeviceIntPoint aPoint, uint32_t aModifierFlags) {
  NS_OBJC_BEGIN_TRY_BLOCK_RETURN;

  DispatchDoubleTapGesture(TimeStamp::Now(), aPoint - WidgetToScreenOffset(),
                           static_cast<Modifiers>(aModifierFlags));

  return NS_OK;

  NS_OBJC_END_TRY_BLOCK_RETURN(NS_ERROR_FAILURE);
}

bool nsChildView::SendEventToNativeMenuSystem(NSEvent* aEvent) {
  bool handled = false;
  if (nsCocoaWindow* widget = GetAppWindowWidget()) {
    if (nsMenuBarX* mb = widget->GetMenuBar()) {
      // Check if main menu wants to handle the event.
      handled = mb->PerformKeyEquivalent(aEvent);
    }
  }

  if (!handled && sApplicationMenu) {
    // Check if application menu wants to handle the event.
    handled = [sApplicationMenu performKeyEquivalent:aEvent];
  }

  return handled;
}

void nsChildView::PostHandleKeyEvent(mozilla::WidgetKeyboardEvent* aEvent) {
  NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;

  // We always allow keyboard events to propagate to keyDown: but if they are
  // not handled we give menu items a chance to act. This allows for handling of
  // custom shortcuts. Note that existing shortcuts cannot be reassigned yet and
  // will have been handled by keyDown: before we get here.
  NSMutableDictionary* nativeKeyEventsMap = [ChildView sNativeKeyEventsMap];
  NSEvent* cocoaEvent = [nativeKeyEventsMap objectForKey:@(aEvent->mUniqueId)];
  if (!cocoaEvent) {
    return;
  }

  // If the escape key is pressed, the expectations are as follows:
  // 1. If the page is loading, interrupt loading.
  // 2. Give a website an opportunity to handle the event and call
  //    preventDefault() on it.
  // 3. If the browser is fullscreen and the page isn't loading, exit
  //    fullscreen.
  // 4. Ignore.
  // Case 1 and 2 are handled before we get here. Below, we handle case 3.
  if (StaticPrefs::browser_fullscreen_exit_on_escape() &&
      [cocoaEvent keyCode] == kVK_Escape &&
      [[mView window] styleMask] & NSWindowStyleMaskFullScreen) {
    [[mView window] toggleFullScreen:nil];
  }

  if (SendEventToNativeMenuSystem(cocoaEvent)) {
    aEvent->PreventDefault();
  }
  [nativeKeyEventsMap removeObjectForKey:@(aEvent->mUniqueId)];

  NS_OBJC_END_TRY_IGNORE_BLOCK;
}

// Used for testing native menu system structure and event handling.
nsresult nsChildView::ActivateNativeMenuItemAt(const nsAString& indexString) {
  NS_OBJC_BEGIN_TRY_BLOCK_RETURN;

  nsMenuUtilsX::CheckNativeMenuConsistency([NSApp mainMenu]);

  NSString* locationString =
      [NSString stringWithCharacters:reinterpret_cast<const unichar*>(
                                         indexString.BeginReading())
                              length:indexString.Length()];
  NSMenuItem* item = nsMenuUtilsX::NativeMenuItemWithLocation(
      [NSApp mainMenu], locationString, true);
  // We can't perform an action on an item with a submenu, that will raise
  // an obj-c exception.
  if (item && ![item hasSubmenu]) {
    NSMenu* parent = [item menu];
    if (parent) {
      // NSLog(@"Performing action for native menu item titled: %@\n",
      //       [[currentSubmenu itemAtIndex:targetIndex] title]);
      mozilla::AutoRestore<bool> autoRestore(
          nsMenuUtilsX::gIsSynchronouslyActivatingNativeMenuItemDuringTest);
      nsMenuUtilsX::gIsSynchronouslyActivatingNativeMenuItemDuringTest = true;
      [parent performActionForItemAtIndex:[parent indexOfItem:item]];
      return NS_OK;
    }
  }
  return NS_ERROR_FAILURE;

  NS_OBJC_END_TRY_BLOCK_RETURN(NS_ERROR_FAILURE);
}

// Used for testing native menu system structure and event handling.
nsresult nsChildView::ForceUpdateNativeMenuAt(const nsAString& indexString) {
  NS_OBJC_BEGIN_TRY_BLOCK_RETURN;

  if (nsCocoaWindow* widget = GetAppWindowWidget()) {
    if (nsMenuBarX* mb = widget->GetMenuBar()) {
      if (indexString.IsEmpty())
        mb->ForceNativeMenuReload();
      else
        mb->ForceUpdateNativeMenuAt(indexString);
    }
  }
  return NS_OK;

  NS_OBJC_END_TRY_BLOCK_RETURN(NS_ERROR_FAILURE);
}

#pragma mark -

#ifdef INVALIDATE_DEBUGGING

static Boolean KeyDown(const UInt8 theKey) {
  KeyMap map;
  GetKeys(map);
  return ((*((UInt8*)map + (theKey >> 3)) >> (theKey & 7)) & 1) != 0;
}

static Boolean caps_lock() { return KeyDown(0x39); }

static void blinkRect(Rect* r) {
  StRegionFromPool oldClip;
  if (oldClip != NULL) ::GetClip(oldClip);

  ::ClipRect(r);
  ::InvertRect(r);
  UInt32 end = ::TickCount() + 5;
  while (::TickCount() < end);
  ::InvertRect(r);

  if (oldClip != NULL) ::SetClip(oldClip);
}

static void blinkRgn(RgnHandle rgn) {
  StRegionFromPool oldClip;
  if (oldClip != NULL) ::GetClip(oldClip);

  ::SetClip(rgn);
  ::InvertRgn(rgn);
  UInt32 end = ::TickCount() + 5;
  while (::TickCount() < end);
  ::InvertRgn(rgn);

  if (oldClip != NULL) ::SetClip(oldClip);
}

#endif

// Invalidate this component's visible area
void nsChildView::Invalidate(const LayoutDeviceIntRect& aRect) {
  NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;

  if (!mView || !mVisible) return;

  NS_ASSERTION(
      GetWindowRenderer()->GetBackendType() != LayersBackend::LAYERS_WR,
      "Shouldn't need to invalidate with accelerated OMTC layers!");

  EnsureContentLayerForMainThreadPainting();
  mContentLayerInvalidRegion.OrWith(aRect.Intersect(GetBounds()));
  [mView markLayerForDisplay];

  NS_OBJC_END_TRY_IGNORE_BLOCK;
}

bool nsChildView::WidgetTypeSupportsAcceleration() {
  // All widget types support acceleration.
  return true;
}

bool nsChildView::ShouldUseOffMainThreadCompositing() {
  // We need to enable OMTC in popups which contain remote layer
  // trees, since the remote content won't be rendered at all otherwise.
  if (HasRemoteContent()) {
    return true;
  }

  // Don't use OMTC for popup windows, because we do not want context menus to
  // pay the overhead of starting up a compositor. With the OpenGL compositor,
  // new windows are expensive because of shader re-compilation, and with
  // WebRender, new windows are expensive because they create their own threads
  // and texture caches.
  // Using OMTC with BasicCompositor for context menus would probably be fine
  // but isn't a well-tested configuration.
  if ([mView window] && [[mView window] isKindOfClass:[PopupWindow class]]) {
    // Use main-thread BasicLayerManager for drawing menus.
    return false;
  }

  return nsBaseWidget::ShouldUseOffMainThreadCompositing();
}

#pragma mark -

// Invokes callback and ProcessEvent methods on Event Listener object
nsresult nsChildView::DispatchEvent(WidgetGUIEvent* event,
                                    nsEventStatus& aStatus) {
  RefPtr<nsChildView> kungFuDeathGrip(this);

#ifdef DEBUG
  debug_DumpEvent(stdout, event->mWidget, event, "something", 0);
#endif

  if (event->mFlags.mIsSynthesizedForTests) {
    WidgetKeyboardEvent* keyEvent = event->AsKeyboardEvent();
    if (keyEvent) {
      nsresult rv = mTextInputHandler->AttachNativeKeyEvent(*keyEvent);
      NS_ENSURE_SUCCESS(rv, rv);
    }
  }

  aStatus = nsEventStatus_eIgnore;

  nsIWidgetListener* listener = mWidgetListener;

  // If the listener is NULL, check if the parent is a popup. If it is, then
  // this child is the popup content view attached to a popup. Get the
  // listener from the parent popup instead.
  nsCOMPtr<nsIWidget> parentWidget = mParent;
  if (!listener && parentWidget) {
    if (parentWidget->GetWindowType() == WindowType::Popup) {
      // Check just in case event->mWidget isn't this widget
      if (event->mWidget) {
        listener = event->mWidget->GetWidgetListener();
      }
      if (!listener) {
        event->mWidget = parentWidget;
        listener = parentWidget->GetWidgetListener();
      }
    }
  }

  if (listener) aStatus = listener->HandleEvent(event, mUseAttachedEvents);

  return NS_OK;
}

nsIWidget* nsChildView::GetWidgetForListenerEvents() {
  // If there is no listener, use the parent popup's listener if that exists.
  if (!mWidgetListener && mParent &&
      mParent->GetWindowType() == WindowType::Popup) {
    return mParent;
  }
  return this;
}

void nsChildView::WillPaintWindow() {
  nsCOMPtr<nsIWidget> widget = GetWidgetForListenerEvents();

  nsIWidgetListener* listener = widget->GetWidgetListener();
  if (listener) {
    listener->WillPaintWindow(widget);
  }
}

bool nsChildView::PaintWindow(LayoutDeviceIntRegion aRegion) {
  nsCOMPtr<nsIWidget> widget = GetWidgetForListenerEvents();

  nsIWidgetListener* listener = widget->GetWidgetListener();
  if (!listener) return false;

  bool returnValue = false;
  bool oldDispatchPaint = mIsDispatchPaint;
  mIsDispatchPaint = true;
  returnValue = listener->PaintWindow(widget, aRegion);

  listener = widget->GetWidgetListener();
  if (listener) {
    listener->DidPaintWindow();
  }

  mIsDispatchPaint = oldDispatchPaint;
  return returnValue;
}

bool nsChildView::PaintWindowInDrawTarget(gfx::DrawTarget* aDT,
                                          const LayoutDeviceIntRegion& aRegion,
                                          const gfx::IntSize& aSurfaceSize) {
  if (!aDT || !aDT->IsValid()) {
    return false;
  }
  gfxContext targetContext(aDT);

  // 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 (GetWindowRenderer()->GetBackendType() == LayersBackend::LAYERS_NONE) {
    nsBaseWidget::AutoLayerManagerSetup setupLayerManager(
        this, &targetContext, BufferMode::BUFFER_NONE);
    return PaintWindow(aRegion);
  }
  return false;
}

void nsChildView::EnsureContentLayerForMainThreadPainting() {
  // Ensure we have an mContentLayer of the correct size.
  // The content layer gets created on demand for BasicLayers windows. We do
  // not create it during widget creation because, for non-BasicLayers windows,
  // the compositing layer manager will create any layers it needs.
  gfx::IntSize size = GetBounds().Size().ToUnknownSize();
  if (mContentLayer && mContentLayer->GetSize() != size) {
    mNativeLayerRoot->RemoveLayer(mContentLayer);
    mContentLayer = nullptr;
  }
  if (!mContentLayer) {
    mPoolHandle = SurfacePool::Create(0)->GetHandleForGL(nullptr);
    RefPtr<NativeLayer> contentLayer =
        mNativeLayerRoot->CreateLayer(size, false, mPoolHandle);
    mNativeLayerRoot->AppendLayer(contentLayer);
    mContentLayer = contentLayer->AsNativeLayerCA();
    mContentLayer->SetSurfaceIsFlipped(false);
    mContentLayerInvalidRegion = GetBounds();
  }
}

void nsChildView::PaintWindowInContentLayer() {
  EnsureContentLayerForMainThreadPainting();
  mPoolHandle->OnBeginFrame();
  RefPtr<DrawTarget> dt = mContentLayer->NextSurfaceAsDrawTarget(
      gfx::IntRect({}, mContentLayer->GetSize()),
      mContentLayerInvalidRegion.ToUnknownRegion(), gfx::BackendType::SKIA);
  if (!dt) {
    return;
  }

  PaintWindowInDrawTarget(dt, mContentLayerInvalidRegion, dt->GetSize());
  mContentLayer->NotifySurfaceReady();
  mContentLayerInvalidRegion.SetEmpty();
  mPoolHandle->OnEndFrame();
}

void nsChildView::HandleMainThreadCATransaction() {
  WillPaintWindow();

  if (GetWindowRenderer()->GetBackendType() == LayersBackend::LAYERS_NONE) {
    // We're in BasicLayers mode, i.e. main thread software compositing.
    // Composite the window into our layer's surface.
    PaintWindowInContentLayer();
  } else {
    // Trigger a synchronous OMTC composite. This will call NextSurface and
    // NotifySurfaceReady on the compositor thread to update mNativeLayerRoot's
    // contents, and the main thread (this thread) will wait inside PaintWindow
    // during that time.
    PaintWindow(LayoutDeviceIntRegion(GetBounds()));
  }

  {
    // Apply the changes inside mNativeLayerRoot to the underlying CALayers. Now
    // is a good time to call this because we know we're currently inside a main
    // thread CATransaction, and the lock makes sure that no composition is
    // currently in progress, so we won't present half-composited state to the
    // screen.
    MutexAutoLock lock(mCompositingLock);
    mNativeLayerRoot->CommitToScreen();
  }

  MaybeScheduleUnsuspendAsyncCATransactions();
}

#pragma mark -

void nsChildView::ReportMoveEvent() { NotifyWindowMoved(mBounds.x, mBounds.y); }

void nsChildView::ReportSizeEvent() {
  if (mWidgetListener)
    mWidgetListener->WindowResized(this, mBounds.width, mBounds.height);
}

#pragma mark -

LayoutDeviceIntPoint nsChildView::GetClientOffset() {
  NS_OBJC_BEGIN_TRY_BLOCK_RETURN;

  NSPoint origin = [mView convertPoint:NSMakePoint(0, 0) toView:nil];
  origin.y = [[mView window] frame].size.height - origin.y;
  return CocoaPointsToDevPixels(origin);

  NS_OBJC_END_TRY_BLOCK_RETURN(LayoutDeviceIntPoint(0, 0));
}

//    Return the offset between this child view and the screen.
//    @return       -- widget origin in device-pixel coords
LayoutDeviceIntPoint nsChildView::WidgetToScreenOffset() {
  NS_OBJC_BEGIN_TRY_BLOCK_RETURN;

  NSPoint origin = NSMakePoint(0, 0);

  // 1. First translate view origin point into window coords.
  // The returned point is in bottom-left coordinates.
  origin = [mView convertPoint:origin toView:nil];

  // 2. We turn the window-coord rect's origin into screen (still bottom-left)
  // coords.
  origin = nsCocoaUtils::ConvertPointToScreen([mView window], origin);

  // 3. Since we're dealing in bottom-left coords, we need to make it top-left
  // coords
  //    before we pass it back to Gecko.
  FlipCocoaScreenCoordinate(origin);

  // convert to device pixels
  return CocoaPointsToDevPixels(origin);

  NS_OBJC_END_TRY_BLOCK_RETURN(LayoutDeviceIntPoint(0, 0));
}

nsresult nsChildView::SetTitle(const nsAString& title) {
  // child views don't have titles
  return NS_OK;
}

nsresult nsChildView::GetAttention(int32_t aCycleCount) {
  NS_OBJC_BEGIN_TRY_BLOCK_RETURN;

  [NSApp requestUserAttention:NSInformationalRequest];
  return NS_OK;

  NS_OBJC_END_TRY_BLOCK_RETURN(NS_ERROR_FAILURE);
}

/* static */
bool nsChildView::DoHasPendingInputEvent() {
  return sLastInputEventCount != GetCurrentInputEventCount();
}

/* static */
uint32_t nsChildView::GetCurrentInputEventCount() {
  // Can't use kCGAnyInputEventType because that updates too rarely for us (and
  // always in increments of 30+!) and because apparently it's sort of broken
  // on Tiger.  So just go ahead and query the counters we care about.
  static const CGEventType eventTypes[] = {kCGEventLeftMouseDown,
                                           kCGEventLeftMouseUp,
                                           kCGEventRightMouseDown,
                                           kCGEventRightMouseUp,
                                           kCGEventMouseMoved,
                                           kCGEventLeftMouseDragged,
                                           kCGEventRightMouseDragged,
                                           kCGEventKeyDown,
                                           kCGEventKeyUp,
                                           kCGEventScrollWheel,
                                           kCGEventTabletPointer,
                                           kCGEventOtherMouseDown,
                                           kCGEventOtherMouseUp,
                                           kCGEventOtherMouseDragged};

  uint32_t eventCount = 0;
  for (uint32_t i = 0; i < std::size(eventTypes); ++i) {
    eventCount += CGEventSourceCounterForEventType(
        kCGEventSourceStateCombinedSessionState, eventTypes[i]);
  }
  return eventCount;
}

/* static */
void nsChildView::UpdateCurrentInputEventCount() {
  sLastInputEventCount = GetCurrentInputEventCount();
}

bool nsChildView::HasPendingInputEvent() { return DoHasPendingInputEvent(); }

#pragma mark -

void nsChildView::SetInputContext(const InputContext& aContext,
                                  const InputContextAction& aAction) {
  NS_ENSURE_TRUE_VOID(mTextInputHandler);

  if (mTextInputHandler->IsFocused()) {
    if (aContext.IsPasswordEditor()) {
      TextInputHandler::EnableSecureEventInput();
    } else {
      TextInputHandler::EnsureSecureEventInputDisabled();
    }
  }

  // IMEInputHandler::IsEditableContent() returns false when both
  // IsASCIICableOnly() and IsIMEEnabled() return false.  So, be careful
  // when you change the following code.  You might need to change
  // IMEInputHandler::IsEditableContent() too.
  mInputContext = aContext;

  switch (aContext.mIMEState.mEnabled) {
    case IMEEnabled::Enabled:
      mTextInputHandler->SetASCIICapableOnly(false);
      mTextInputHandler->EnableIME(true);
      if (mInputContext.mIMEState.mOpen != IMEState::DONT_CHANGE_OPEN_STATE) {
        mTextInputHandler->SetIMEOpenState(mInputContext.mIMEState.mOpen ==
                                           IMEState::OPEN);
      }
      mTextInputHandler->EnableTextSubstitution(aContext.mAutocorrect);
      break;
    case IMEEnabled::Disabled:
      mTextInputHandler->SetASCIICapableOnly(false);
      mTextInputHandler->EnableIME(false);
      mTextInputHandler->EnableTextSubstitution(false);
      break;
    case IMEEnabled::Password:
      mTextInputHandler->SetASCIICapableOnly(true);
      mTextInputHandler->EnableIME(false);
      mTextInputHandler->EnableTextSubstitution(aContext.mAutocorrect);
      break;
    default:
      NS_ERROR("not implemented!");
  }
}

InputContext nsChildView::GetInputContext() {
  switch (mInputContext.mIMEState.mEnabled) {
    case IMEEnabled::Enabled:
      if (mTextInputHandler) {
        mInputContext.mIMEState.mOpen = mTextInputHandler->IsIMEOpened()
                                            ? IMEState::OPEN
                                            : IMEState::CLOSED;
        break;
      }
      // If mTextInputHandler is null, set CLOSED instead...
      [[fallthrough]];
    default:
      mInputContext.mIMEState.mOpen = IMEState::CLOSED;
      break;
  }
  return mInputContext;
}

TextEventDispatcherListener*
nsChildView::GetNativeTextEventDispatcherListener() {
  if (NS_WARN_IF(!mTextInputHandler)) {
    return nullptr;
  }
  return mTextInputHandler;
}

nsresult nsChildView::AttachNativeKeyEvent(
    mozilla::WidgetKeyboardEvent& aEvent) {
  NS_ENSURE_TRUE(mTextInputHandler, NS_ERROR_NOT_AVAILABLE);
  return mTextInputHandler->AttachNativeKeyEvent(aEvent);
}

bool nsChildView::GetEditCommands(NativeKeyBindingsType aType,
                                  const WidgetKeyboardEvent& aEvent,
                                  nsTArray<CommandInt>& aCommands) {
  // Validate the arguments.
  if (NS_WARN_IF(!nsIWidget::GetEditCommands(aType, aEvent, aCommands))) {
    return false;
  }

  Maybe<WritingMode> writingMode;
  if (aEvent.NeedsToRemapNavigationKey()) {
    if (RefPtr<TextEventDispatcher> dispatcher = GetTextEventDispatcher()) {
      writingMode = dispatcher->MaybeQueryWritingModeAtSelection();
    }
  }

  NativeKeyBindings* keyBindings = NativeKeyBindings::GetInstance(aType);
  keyBindings->GetEditCommands(aEvent, writingMode, aCommands);
  return true;
}

NSView<mozView>* nsChildView::GetEditorView() {
  NSView<mozView>* editorView = mView;
  // We need to get editor's view. E.g., when the focus is in the bookmark
  // dialog, the view is <panel> element of the dialog.  At this time, the key
  // events are processed the parent window's view that has native focus.
  WidgetQueryContentEvent queryContentState(true, eQueryContentState, this);
  // This may be called during creating a menu popup frame due to creating
  // widget synchronously and that causes Cocoa asking current window level.
  // In this case, it's not safe to flush layout on the document and we don't
  // need any layout information right now.
  queryContentState.mNeedsToFlushLayout = false;
  DispatchWindowEvent(queryContentState);
  if (queryContentState.Succeeded() &&
      queryContentState.mReply->mFocusedWidget) {
    NSView<mozView>* view = static_cast<NSView<mozView>*>(
        queryContentState.mReply->mFocusedWidget->GetNativeData(
            NS_NATIVE_WIDGET));
    if (view) editorView = view;
  }
  return editorView;
}

#pragma mark -

void nsChildView::CreateCompositor() {
  nsBaseWidget::CreateCompositor();
  if (mCompositorBridgeChild) {
    [mView setUsingOMTCompositor:true];
  }
}

void nsChildView::ConfigureAPZCTreeManager() {
  nsBaseWidget::ConfigureAPZCTreeManager();
}

void nsChildView::ConfigureAPZControllerThread() {
  nsBaseWidget::ConfigureAPZControllerThread();
}

bool nsChildView::PreRender(WidgetRenderingContext* aContext)
    MOZ_NO_THREAD_SAFETY_ANALYSIS {
  // The lock makes sure that we don't attempt to tear down the view while
  // compositing. That would make us unable to call postRender on it when the
  // composition is done, thus keeping the GL context locked forever.
  mCompositingLock.Lock();

  if (aContext->mGL && gfxPlatform::CanMigrateMacGPUs()) {
    GLContextCGL::Cast(aContext->mGL)->MigrateToActiveGPU();
  }

  return true;
}

void nsChildView::PostRender(WidgetRenderingContext* aContext)
    MOZ_NO_THREAD_SAFETY_ANALYSIS {
  mCompositingLock.Unlock();
}

RefPtr<layers::NativeLayerRoot> nsChildView::GetNativeLayerRoot() {
  return mNativeLayerRoot;
}

static LayoutDeviceIntRect FindFirstRectOfType(
    const nsTArray<nsIWidget::ThemeGeometry>& aThemeGeometries,
    nsITheme::ThemeGeometryType aThemeGeometryType) {
  for (uint32_t i = 0; i < aThemeGeometries.Length(); ++i) {
    const nsIWidget::ThemeGeometry& g = aThemeGeometries[i];
    if (g.mType == aThemeGeometryType) {
      return g.mRect;
    }
  }
  return LayoutDeviceIntRect();
}

void nsChildView::UpdateThemeGeometries(
    const nsTArray<ThemeGeometry>& aThemeGeometries) {
  if (!mView.window) {
    return;
  }

  UpdateVibrancy(aThemeGeometries);

  if (![mView.window isKindOfClass:[ToolbarWindow class]]) {
    return;
  }

  ToolbarWindow* win = (ToolbarWindow*)[mView window];

  // Update titlebar control offsets.
  LayoutDeviceIntRect windowButtonRect =
      FindFirstRectOfType(aThemeGeometries, eThemeGeometryTypeWindowButtons);
  [win placeWindowButtons:[mView convertRect:DevPixelsToCocoaPoints(
                                                 windowButtonRect)
                                      toView:nil]];
}

static Maybe<VibrancyType> ThemeGeometryTypeToVibrancyType(
    nsITheme::ThemeGeometryType aThemeGeometryType) {
  switch (aThemeGeometryType) {
    case eThemeGeometryTypeSidebar:
      return Some(VibrancyType::Sidebar);
    case eThemeGeometryTypeTitlebar:
      return Some(VibrancyType::Titlebar);
    default:
      return Nothing();
  }
}

static EnumeratedArray<VibrancyType, LayoutDeviceIntRegion>
GatherVibrantRegions(Span<const nsIWidget::ThemeGeometry> aThemeGeometries) {
  EnumeratedArray<VibrancyType, LayoutDeviceIntRegion> regions;
  for (const auto& geometry : aThemeGeometries) {
    auto vibrancyType = ThemeGeometryTypeToVibrancyType(geometry.mType);
    if (!vibrancyType) {
      continue;
    }
    regions[*vibrancyType].OrWith(geometry.mRect);
  }
  return regions;
}

// Subtracts parts from regions in such a way that they don't have any overlap.
// Each region in the argument list will have the union of all the regions
// *following* it subtracted from itself. In other words, the arguments are
// treated as low priority to high priority.
static void MakeRegionsNonOverlapping(Span<LayoutDeviceIntRegion> aRegions) {
  LayoutDeviceIntRegion unionOfAll;
  for (auto& region : aRegions) {
    region.SubOut(unionOfAll);
    unionOfAll.OrWith(region);
  }
}

void nsChildView::UpdateVibrancy(
    const nsTArray<ThemeGeometry>& aThemeGeometries) {
  auto regions = GatherVibrantRegions(aThemeGeometries);
  MakeRegionsNonOverlapping(regions);

  auto& vm = EnsureVibrancyManager();
  bool changed = false;

  // EnumeratedArray doesn't have an iterator that also yields the enum type,
  // but we rely on VibrancyType being contiguous and starting at 0, so we can
  // do that manually.
  size_t i = 0;
  for (const auto& region : regions) {
    changed |= vm.UpdateVibrantRegion(VibrancyType(i++), region);
  }

  if (changed) {
    SuspendAsyncCATransactions();
  }
}

mozilla::VibrancyManager& nsChildView::EnsureVibrancyManager() {
  MOZ_ASSERT(mView, "Only call this once we have a view!");
  if (!mVibrancyManager) {
    mVibrancyManager =
        MakeUnique<VibrancyManager>(*this, mView.vibrancyViewsContainer);
  }
  return *mVibrancyManager;
}

void nsChildView::UpdateBoundsFromView() {
  auto oldSize = mBounds.Size();
  mBounds = CocoaPointsToDevPixels([mView frame]);
  if (mBounds.Size() != oldSize) {
    SuspendAsyncCATransactions();
  }
}

@interface NonDraggableView : NSView
@end

@implementation NonDraggableView
- (BOOL)mouseDownCanMoveWindow {
  return NO;
}
- (NSView*)hitTest:(NSPoint)aPoint {
  return nil;
}
- (NSRect)_opaqueRectForWindowMoveWhenInTitlebar {
  // In NSWindows that use NSWindowStyleMaskFullSizeContentView, NSViews which
  // overlap the titlebar do not disable window dragging in the overlapping
  // areas even if they return NO from mouseDownCanMoveWindow. This can have
  // unfortunate effects: For example, dragging tabs in a browser window would
  // move the window if those tabs are in the titlebar.
  // macOS does not seem to offer a documented way to opt-out of the forced
  // window dragging in the titlebar.
  // Overriding _opaqueRectForWindowMoveWhenInTitlebar is an undocumented way
  // of opting out of this behavior. This method was added in 10.11 and is used
  // by some NSControl subclasses to prevent window dragging in the titlebar.
  // The function which assembles the draggable area of the window calls
  // _opaqueRect for the content area and _opaqueRectForWindowMoveWhenInTitlebar
  // for the titlebar area, on all visible NSViews. The default implementation
  // of _opaqueRect returns [self visibleRect], and the default implementation
  // of _opaqueRectForWindowMoveWhenInTitlebar returns NSZeroRect unless it's
  // overridden.
  //
  // Since this view is constructed and used such that its entire bounds is the
  // relevant region, we just return our bounds.
  return self.bounds;
}
@end

void nsChildView::UpdateWindowDraggingRegion(
    const LayoutDeviceIntRegion& aRegion) {
  // mView returns YES from mouseDownCanMoveWindow, so we need to put NSViews
  // that return NO from mouseDownCanMoveWindow in the places that shouldn't
  // be draggable. We can't do it the other way round because returning
  // YES from mouseDownCanMoveWindow doesn't have any effect if there's a
  // superview that returns NO.
  LayoutDeviceIntRegion nonDraggable;
  nonDraggable.Sub(LayoutDeviceIntRect(0, 0, mBounds.width, mBounds.height),
                   aRegion);

  __block bool changed = false;

  // Suppress calls to setNeedsDisplay during NSView geometry changes.
  ManipulateViewWithoutNeedingDisplay(mView, ^() {
    changed = mNonDraggableRegion.UpdateRegion(
        nonDraggable, *this, mView.nonDraggableViewsContainer, ^() {
          return [[NonDraggableView alloc] initWithFrame:NSZeroRect];
        });
  });

  if (changed) {
    // Trigger an update to the window server. This will call
    // mouseDownCanMoveWindow.
    // Doing this manually is only necessary because we're suppressing
    // setNeedsDisplay calls above.
    [[mView window] setMovableByWindowBackground:NO];
    [[mView window] setMovableByWindowBackground:YES];
  }
}

nsEventStatus nsChildView::DispatchAPZInputEvent(InputData& aEvent) {
  APZEventResult result;

  if (mAPZC) {
    result = mAPZC->InputBridge()->ReceiveInputEvent(aEvent);
  }

  if (result.GetStatus() == nsEventStatus_eConsumeNoDefault) {
    return result.GetStatus();
  }

  if (aEvent.mInputType == PINCHGESTURE_INPUT) {
    PinchGestureInput& pinchEvent = aEvent.AsPinchGestureInput();
    WidgetWheelEvent wheelEvent = pinchEvent.ToWidgetEvent(this);
    ProcessUntransformedAPZEvent(&wheelEvent, result);
  } else if (aEvent.mInputType == TAPGESTURE_INPUT) {
    TapGestureInput& tapEvent = aEvent.AsTapGestureInput();
    WidgetSimpleGestureEvent gestureEvent = tapEvent.ToWidgetEvent(this);
    ProcessUntransformedAPZEvent(&gestureEvent, result);
  } else {
    MOZ_ASSERT_UNREACHABLE();
  }

  return result.GetStatus();
}

void nsChildView::DispatchAPZWheelInputEvent(InputData& aEvent) {
  if (mSwipeTracker && aEvent.mInputType == PANGESTURE_INPUT) {
    // Give the swipe tracker a first pass at the event. If a new pan gesture
    // has been started since the beginning of the swipe, the swipe tracker
    // will know to ignore the event.
    nsEventStatus status =
        mSwipeTracker->ProcessEvent(aEvent.AsPanGestureInput());
    if (status == nsEventStatus_eConsumeNoDefault) {
      return;
    }
  }

  WidgetWheelEvent event(true, eWheel, this);

  if (mAPZC) {
    APZEventResult result;

    switch (aEvent.mInputType) {
      case PANGESTURE_INPUT: {
        result = mAPZC->InputBridge()->ReceiveInputEvent(aEvent);
        if (result.GetStatus() == nsEventStatus_eConsumeNoDefault) {
          return;
        }

        event = MayStartSwipeForAPZ(aEvent.AsPanGestureInput(), result);
        break;
      }
      case SCROLLWHEEL_INPUT: {
        // For wheel events on OS X, send it to APZ using the WidgetInputEvent
        // variant of ReceiveInputEvent, because the APZInputBridge version of
        // that function has special handling (for delta multipliers etc.) that
        // we need to run. Using the InputData variant would bypass that and
        // go straight to the APZCTreeManager subclass.
        event = aEvent.AsScrollWheelInput().ToWidgetEvent(this);
        result = mAPZC->InputBridge()->ReceiveInputEvent(event);
        if (result.GetStatus() == nsEventStatus_eConsumeNoDefault) {
          return;
        }
        break;
      };
      default:
        MOZ_CRASH("unsupported event type");
        return;
    }
    if (event.mMessage == eWheel &&
        (event.mDeltaX != 0 || event.mDeltaY != 0)) {
      ProcessUntransformedAPZEvent(&event, result);
    }
    return;
  }

  nsEventStatus status;
  switch (aEvent.mInputType) {
    case PANGESTURE_INPUT: {
      if (MayStartSwipeForNonAPZ(aEvent.AsPanGestureInput())) {
        return;
      }
      event = aEvent.AsPanGestureInput().ToWidgetEvent(this);
      break;
    }
    case SCROLLWHEEL_INPUT: {
      event = aEvent.AsScrollWheelInput().ToWidgetEvent(this);
      break;
    }
    default:
      MOZ_CRASH("unexpected event type");
      return;
  }
  if (event.mMessage == eWheel && (event.mDeltaX != 0 || event.mDeltaY != 0)) {
    DispatchEvent(&event, status);
  }
}

void nsChildView::DispatchDoubleTapGesture(TimeStamp aEventTimeStamp,
                                           LayoutDeviceIntPoint aScreenPosition,
                                           mozilla::Modifiers aModifiers) {
  if (StaticPrefs::apz_mac_enable_double_tap_zoom_touchpad_gesture()) {
    TapGestureInput event{
        TapGestureInput::TAPGESTURE_DOUBLE, aEventTimeStamp,
        ViewAs<ScreenPixel>(
            aScreenPosition,
            PixelCastJustification::LayoutDeviceIsScreenForUntransformedEvent),
        aModifiers};

    DispatchAPZInputEvent(event);
  } else {
    // Setup the "double tap" event.
    WidgetSimpleGestureEvent geckoEvent(true, eTapGesture, this);
    // do what convertCocoaMouseEvent does basically.
    geckoEvent.mRefPoint = aScreenPosition;
    geckoEvent.mModifiers = aModifiers;
    geckoEvent.mTimeStamp = aEventTimeStamp;
    geckoEvent.mClickCount = 1;

    // Send the event.
    DispatchWindowEvent(geckoEvent);
  }
}

void nsChildView::LookUpDictionary(
    const nsAString& aText, const nsTArray<mozilla::FontRange>& aFontRangeArray,
    const bool aIsVertical, const LayoutDeviceIntPoint& aPoint) {
  NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;

  NSMutableAttributedString* attrStr =
      nsCocoaUtils::GetNSMutableAttributedString(
          aText, aFontRangeArray, aIsVertical, BackingScaleFactor());
  NSPoint pt =
      nsCocoaUtils::DevPixelsToCocoaPoints(aPoint, BackingScaleFactor());
  NSDictionary* attributes = [attrStr attributesAtIndex:0 effectiveRange:nil];
  NSFont* font = [attributes objectForKey:NSFontAttributeName];
  if (font) {
    if (aIsVertical) {
      pt.x -= [font descender];
    } else {
      pt.y += [font ascender];
    }
  }

  [mView showDefinitionForAttributedString:attrStr atPoint:pt];

  NS_OBJC_END_TRY_IGNORE_BLOCK;
}

#ifdef ACCESSIBILITY
already_AddRefed<a11y::LocalAccessible> nsChildView::GetDocumentAccessible() {
  if (!mozilla::a11y::ShouldA11yBeEnabled()) return nullptr;

  // mAccessible might be dead if accessibility was previously disabled and is
  // now being enabled again.
  if (mAccessible && mAccessible->IsAlive()) {
    RefPtr<a11y::LocalAccessible> ret;
    CallQueryReferent(mAccessible.get(), static_cast<a11y::LocalAccessible**>(
                                             getter_AddRefs(ret)));
    return ret.forget();
  }

  // need to fetch the accessible anew, because it has gone away.
  // cache the accessible in our weak ptr
  RefPtr<a11y::LocalAccessible> acc = GetRootAccessible();
--> --------------------

--> maximum size reached

--> --------------------

[ Verzeichnis aufwärts0.52unsichere Verbindung  Übersetzung europäischer Sprachen durch Browser  ]