Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  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

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

[ Dauer der Verarbeitung: 0.22 Sekunden  (vorverarbeitet)  ]

                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge