widget/android/nsWindow.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     1 /* -*- Mode: c++; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
     2  * This Source Code Form is subject to the terms of the Mozilla Public
     3  * License, v. 2.0. If a copy of the MPL was not distributed with this
     4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     6 #include <android/log.h>
     7 #include <math.h>
     8 #include <unistd.h>
    10 #include "mozilla/MiscEvents.h"
    11 #include "mozilla/MouseEvents.h"
    12 #include "mozilla/TextEvents.h"
    13 #include "mozilla/TouchEvents.h"
    15 #include "mozilla/dom/ContentParent.h"
    16 #include "mozilla/dom/ContentChild.h"
    17 #include "mozilla/unused.h"
    18 #include "mozilla/Preferences.h"
    19 #include "mozilla/layers/RenderTrace.h"
    20 #include <algorithm>
    22 using mozilla::dom::ContentParent;
    23 using mozilla::dom::ContentChild;
    24 using mozilla::unused;
    26 #include "nsAppShell.h"
    27 #include "nsIdleService.h"
    28 #include "nsWindow.h"
    29 #include "nsIObserverService.h"
    30 #include "nsFocusManager.h"
    31 #include "nsIWidgetListener.h"
    32 #include "nsViewManager.h"
    34 #include "nsRenderingContext.h"
    35 #include "nsIDOMSimpleGestureEvent.h"
    37 #include "nsGkAtoms.h"
    38 #include "nsWidgetsCID.h"
    39 #include "nsGfxCIID.h"
    41 #include "gfxImageSurface.h"
    42 #include "gfxContext.h"
    44 #include "Layers.h"
    45 #include "mozilla/layers/LayerManagerComposite.h"
    46 #include "mozilla/layers/AsyncCompositionManager.h"
    47 #include "mozilla/layers/APZCTreeManager.h"
    48 #include "GLContext.h"
    49 #include "GLContextProvider.h"
    50 #include "ScopedGLHelpers.h"
    51 #include "mozilla/layers/CompositorOGL.h"
    53 #include "nsTArray.h"
    55 #include "AndroidBridge.h"
    56 #include "AndroidBridgeUtilities.h"
    57 #include "android_npapi.h"
    59 #include "imgIEncoder.h"
    61 #include "nsString.h"
    62 #include "GeckoProfiler.h" // For PROFILER_LABEL
    63 #include "nsIXULRuntime.h"
    65 using namespace mozilla;
    66 using namespace mozilla::dom;
    67 using namespace mozilla::widget;
    68 using namespace mozilla::layers;
    70 NS_IMPL_ISUPPORTS_INHERITED0(nsWindow, nsBaseWidget)
    72 // The dimensions of the current android view
    73 static gfxIntSize gAndroidBounds = gfxIntSize(0, 0);
    74 static gfxIntSize gAndroidScreenBounds;
    76 #include "mozilla/layers/AsyncPanZoomController.h"
    77 #include "mozilla/layers/CompositorChild.h"
    78 #include "mozilla/layers/CompositorParent.h"
    79 #include "mozilla/layers/LayerTransactionParent.h"
    80 #include "mozilla/Mutex.h"
    81 #include "nsThreadUtils.h"
    83 class ContentCreationNotifier;
    84 static StaticRefPtr<ContentCreationNotifier> gContentCreationNotifier;
    86 // A helper class to send updates when content processes
    87 // are created. Currently an update for the screen size is sent.
    88 class ContentCreationNotifier MOZ_FINAL : public nsIObserver
    89 {
    90     NS_DECL_ISUPPORTS
    92     NS_IMETHOD Observe(nsISupports* aSubject,
    93                        const char* aTopic,
    94                        const char16_t* aData)
    95     {
    96         if (!strcmp(aTopic, "ipc:content-created")) {
    97             nsCOMPtr<nsIObserver> cpo = do_QueryInterface(aSubject);
    98             ContentParent* cp = static_cast<ContentParent*>(cpo.get());
    99             unused << cp->SendScreenSizeChanged(gAndroidScreenBounds);
   100         } else if (!strcmp(aTopic, "xpcom-shutdown")) {
   101             nsCOMPtr<nsIObserverService>
   102                 obs(do_GetService("@mozilla.org/observer-service;1"));
   103             if (obs) {
   104                 obs->RemoveObserver(static_cast<nsIObserver*>(this),
   105                                     "xpcom-shutdown");
   106                 obs->RemoveObserver(static_cast<nsIObserver*>(this),
   107                                     "ipc:content-created");
   108             }
   109             gContentCreationNotifier = nullptr;
   110         }
   112         return NS_OK;
   113     }
   114 };
   116 NS_IMPL_ISUPPORTS(ContentCreationNotifier,
   117                   nsIObserver)
   119 static bool gMenu;
   120 static bool gMenuConsumed;
   122 // All the toplevel windows that have been created; these are in
   123 // stacking order, so the window at gAndroidBounds[0] is the topmost
   124 // one.
   125 static nsTArray<nsWindow*> gTopLevelWindows;
   127 static bool sFailedToCreateGLContext = false;
   129 // Multitouch swipe thresholds in inches
   130 static const double SWIPE_MAX_PINCH_DELTA_INCHES = 0.4;
   131 static const double SWIPE_MIN_DISTANCE_INCHES = 0.6;
   133 nsWindow*
   134 nsWindow::TopWindow()
   135 {
   136     if (!gTopLevelWindows.IsEmpty())
   137         return gTopLevelWindows[0];
   138     return nullptr;
   139 }
   141 void
   142 nsWindow::LogWindow(nsWindow *win, int index, int indent)
   143 {
   144 #if defined(DEBUG) || defined(FORCE_ALOG)
   145     char spaces[] = "                    ";
   146     spaces[indent < 20 ? indent : 20] = 0;
   147     ALOG("%s [% 2d] 0x%08x [parent 0x%08x] [% 3d,% 3dx% 3d,% 3d] vis %d type %d",
   148          spaces, index, (intptr_t)win, (intptr_t)win->mParent,
   149          win->mBounds.x, win->mBounds.y,
   150          win->mBounds.width, win->mBounds.height,
   151          win->mIsVisible, win->mWindowType);
   152 #endif
   153 }
   155 void
   156 nsWindow::DumpWindows()
   157 {
   158     DumpWindows(gTopLevelWindows);
   159 }
   161 void
   162 nsWindow::DumpWindows(const nsTArray<nsWindow*>& wins, int indent)
   163 {
   164     for (uint32_t i = 0; i < wins.Length(); ++i) {
   165         nsWindow *w = wins[i];
   166         LogWindow(w, i, indent);
   167         DumpWindows(w->mChildren, indent+1);
   168     }
   169 }
   171 nsWindow::nsWindow() :
   172     mIsVisible(false),
   173     mParent(nullptr),
   174     mFocus(nullptr),
   175     mIMEComposing(false),
   176     mIMEMaskSelectionUpdate(false),
   177     mIMEMaskTextUpdate(false),
   178     mIMEMaskEventsCount(1), // Mask IME events since there's no focus yet
   179     mIMERanges(new TextRangeArray()),
   180     mIMEUpdatingContext(false),
   181     mIMESelectionChanged(false)
   182 {
   183 }
   185 nsWindow::~nsWindow()
   186 {
   187     gTopLevelWindows.RemoveElement(this);
   188     nsWindow *top = FindTopLevel();
   189     if (top->mFocus == this)
   190         top->mFocus = nullptr;
   191     ALOG("nsWindow %p destructor", (void*)this);
   192     if (mLayerManager == sLayerManager) {
   193         // If this window was the one that created the global OMTC layer manager
   194         // and compositor, then we should null those out.
   195         SetCompositor(nullptr, nullptr, nullptr);
   196     }
   197 }
   199 bool
   200 nsWindow::IsTopLevel()
   201 {
   202     return mWindowType == eWindowType_toplevel ||
   203         mWindowType == eWindowType_dialog ||
   204         mWindowType == eWindowType_invisible;
   205 }
   207 NS_IMETHODIMP
   208 nsWindow::Create(nsIWidget *aParent,
   209                  nsNativeWidget aNativeParent,
   210                  const nsIntRect &aRect,
   211                  nsDeviceContext *aContext,
   212                  nsWidgetInitData *aInitData)
   213 {
   214     ALOG("nsWindow[%p]::Create %p [%d %d %d %d]", (void*)this, (void*)aParent, aRect.x, aRect.y, aRect.width, aRect.height);
   215     nsWindow *parent = (nsWindow*) aParent;
   216     if (aNativeParent) {
   217         if (parent) {
   218             ALOG("Ignoring native parent on Android window [%p], since parent was specified (%p %p)", (void*)this, (void*)aNativeParent, (void*)aParent);
   219         } else {
   220             parent = (nsWindow*) aNativeParent;
   221         }
   222     }
   224     mBounds = aRect;
   226     // for toplevel windows, bounds are fixed to full screen size
   227     if (!parent) {
   228         mBounds.x = 0;
   229         mBounds.y = 0;
   230         mBounds.width = gAndroidBounds.width;
   231         mBounds.height = gAndroidBounds.height;
   232     }
   234     BaseCreate(nullptr, mBounds, aContext, aInitData);
   236     NS_ASSERTION(IsTopLevel() || parent, "non top level windowdoesn't have a parent!");
   238     if (IsTopLevel()) {
   239         gTopLevelWindows.AppendElement(this);
   240     }
   242     if (parent) {
   243         parent->mChildren.AppendElement(this);
   244         mParent = parent;
   245     }
   247 #ifdef DEBUG_ANDROID_WIDGET
   248     DumpWindows();
   249 #endif
   251     return NS_OK;
   252 }
   254 NS_IMETHODIMP
   255 nsWindow::Destroy(void)
   256 {
   257     nsBaseWidget::mOnDestroyCalled = true;
   259     while (mChildren.Length()) {
   260         // why do we still have children?
   261         ALOG("### Warning: Destroying window %p and reparenting child %p to null!", (void*)this, (void*)mChildren[0]);
   262         mChildren[0]->SetParent(nullptr);
   263     }
   265     if (IsTopLevel())
   266         gTopLevelWindows.RemoveElement(this);
   268     SetParent(nullptr);
   270     nsBaseWidget::OnDestroy();
   272 #ifdef DEBUG_ANDROID_WIDGET
   273     DumpWindows();
   274 #endif
   276     return NS_OK;
   277 }
   279 NS_IMETHODIMP
   280 nsWindow::ConfigureChildren(const nsTArray<nsIWidget::Configuration>& config)
   281 {
   282     for (uint32_t i = 0; i < config.Length(); ++i) {
   283         nsWindow *childWin = (nsWindow*) config[i].mChild;
   284         childWin->Resize(config[i].mBounds.x,
   285                          config[i].mBounds.y,
   286                          config[i].mBounds.width,
   287                          config[i].mBounds.height,
   288                          false);
   289     }
   291     return NS_OK;
   292 }
   294 void
   295 nsWindow::RedrawAll()
   296 {
   297     if (mFocus && mFocus->mWidgetListener) {
   298         mFocus->mWidgetListener->RequestRepaint();
   299     }
   300 }
   302 NS_IMETHODIMP
   303 nsWindow::SetParent(nsIWidget *aNewParent)
   304 {
   305     if ((nsIWidget*)mParent == aNewParent)
   306         return NS_OK;
   308     // If we had a parent before, remove ourselves from its list of
   309     // children.
   310     if (mParent)
   311         mParent->mChildren.RemoveElement(this);
   313     mParent = (nsWindow*)aNewParent;
   315     if (mParent)
   316         mParent->mChildren.AppendElement(this);
   318     // if we are now in the toplevel window's hierarchy, schedule a redraw
   319     if (FindTopLevel() == nsWindow::TopWindow())
   320         RedrawAll();
   322     return NS_OK;
   323 }
   325 NS_IMETHODIMP
   326 nsWindow::ReparentNativeWidget(nsIWidget *aNewParent)
   327 {
   328     NS_PRECONDITION(aNewParent, "");
   329     return NS_OK;
   330 }
   332 nsIWidget*
   333 nsWindow::GetParent()
   334 {
   335     return mParent;
   336 }
   338 float
   339 nsWindow::GetDPI()
   340 {
   341     if (AndroidBridge::Bridge())
   342         return AndroidBridge::Bridge()->GetDPI();
   343     return 160.0f;
   344 }
   346 double
   347 nsWindow::GetDefaultScaleInternal()
   348 {
   349     static double density = 0.0;
   351     if (density != 0.0) {
   352         return density;
   353     }
   355     density = mozilla::widget::android::GeckoAppShell::GetDensity();
   357     if (!density) {
   358         density = 1.0;
   359     }
   361     return density;
   362 }
   364 NS_IMETHODIMP
   365 nsWindow::Show(bool aState)
   366 {
   367     ALOG("nsWindow[%p]::Show %d", (void*)this, aState);
   369     if (mWindowType == eWindowType_invisible) {
   370         ALOG("trying to show invisible window! ignoring..");
   371         return NS_ERROR_FAILURE;
   372     }
   374     if (aState == mIsVisible)
   375         return NS_OK;
   377     mIsVisible = aState;
   379     if (IsTopLevel()) {
   380         // XXX should we bring this to the front when it's shown,
   381         // if it's a toplevel widget?
   383         // XXX we should synthesize a NS_MOUSE_EXIT (for old top
   384         // window)/NS_MOUSE_ENTER (for new top window) since we need
   385         // to pretend that the top window always has focus.  Not sure
   386         // if Show() is the right place to do this, though.
   388         if (aState) {
   389             // It just became visible, so send a resize update if necessary
   390             // and bring it to the front.
   391             Resize(0, 0, gAndroidBounds.width, gAndroidBounds.height, false);
   392             BringToFront();
   393         } else if (nsWindow::TopWindow() == this) {
   394             // find the next visible window to show
   395             unsigned int i;
   396             for (i = 1; i < gTopLevelWindows.Length(); i++) {
   397                 nsWindow *win = gTopLevelWindows[i];
   398                 if (!win->mIsVisible)
   399                     continue;
   401                 win->BringToFront();
   402                 break;
   403             }
   404         }
   405     } else if (FindTopLevel() == nsWindow::TopWindow()) {
   406         RedrawAll();
   407     }
   409 #ifdef DEBUG_ANDROID_WIDGET
   410     DumpWindows();
   411 #endif
   413     return NS_OK;
   414 }
   416 NS_IMETHODIMP
   417 nsWindow::SetModal(bool aState)
   418 {
   419     ALOG("nsWindow[%p]::SetModal %d ignored", (void*)this, aState);
   421     return NS_OK;
   422 }
   424 bool
   425 nsWindow::IsVisible() const
   426 {
   427     return mIsVisible;
   428 }
   430 NS_IMETHODIMP
   431 nsWindow::ConstrainPosition(bool aAllowSlop,
   432                             int32_t *aX,
   433                             int32_t *aY)
   434 {
   435     ALOG("nsWindow[%p]::ConstrainPosition %d [%d %d]", (void*)this, aAllowSlop, *aX, *aY);
   437     // constrain toplevel windows; children we don't care about
   438     if (IsTopLevel()) {
   439         *aX = 0;
   440         *aY = 0;
   441     }
   443     return NS_OK;
   444 }
   446 NS_IMETHODIMP
   447 nsWindow::Move(double aX,
   448                double aY)
   449 {
   450     if (IsTopLevel())
   451         return NS_OK;
   453     return Resize(aX,
   454                   aY,
   455                   mBounds.width,
   456                   mBounds.height,
   457                   true);
   458 }
   460 NS_IMETHODIMP
   461 nsWindow::Resize(double aWidth,
   462                  double aHeight,
   463                  bool aRepaint)
   464 {
   465     return Resize(mBounds.x,
   466                   mBounds.y,
   467                   aWidth,
   468                   aHeight,
   469                   aRepaint);
   470 }
   472 NS_IMETHODIMP
   473 nsWindow::Resize(double aX,
   474                  double aY,
   475                  double aWidth,
   476                  double aHeight,
   477                  bool aRepaint)
   478 {
   479     ALOG("nsWindow[%p]::Resize [%f %f %f %f] (repaint %d)", (void*)this, aX, aY, aWidth, aHeight, aRepaint);
   481     bool needSizeDispatch = aWidth != mBounds.width || aHeight != mBounds.height;
   483     mBounds.x = NSToIntRound(aX);
   484     mBounds.y = NSToIntRound(aY);
   485     mBounds.width = NSToIntRound(aWidth);
   486     mBounds.height = NSToIntRound(aHeight);
   488     if (needSizeDispatch)
   489         OnSizeChanged(gfxIntSize(aWidth, aHeight));
   491     // Should we skip honoring aRepaint here?
   492     if (aRepaint && FindTopLevel() == nsWindow::TopWindow())
   493         RedrawAll();
   495     return NS_OK;
   496 }
   498 void
   499 nsWindow::SetZIndex(int32_t aZIndex)
   500 {
   501     ALOG("nsWindow[%p]::SetZIndex %d ignored", (void*)this, aZIndex);
   502 }
   504 NS_IMETHODIMP
   505 nsWindow::PlaceBehind(nsTopLevelWidgetZPlacement aPlacement,
   506                       nsIWidget *aWidget,
   507                       bool aActivate)
   508 {
   509     return NS_OK;
   510 }
   512 NS_IMETHODIMP
   513 nsWindow::SetSizeMode(int32_t aMode)
   514 {
   515     switch (aMode) {
   516         case nsSizeMode_Minimized:
   517             mozilla::widget::android::GeckoAppShell::MoveTaskToBack();
   518             break;
   519         case nsSizeMode_Fullscreen:
   520             MakeFullScreen(true);
   521             break;
   522     }
   523     return NS_OK;
   524 }
   526 NS_IMETHODIMP
   527 nsWindow::Enable(bool aState)
   528 {
   529     ALOG("nsWindow[%p]::Enable %d ignored", (void*)this, aState);
   530     return NS_OK;
   531 }
   533 bool
   534 nsWindow::IsEnabled() const
   535 {
   536     return true;
   537 }
   539 NS_IMETHODIMP
   540 nsWindow::Invalidate(const nsIntRect &aRect)
   541 {
   542     AndroidGeckoEvent *event = AndroidGeckoEvent::MakeDrawEvent(aRect);
   543     nsAppShell::gAppShell->PostEvent(event);
   544     return NS_OK;
   545 }
   547 nsWindow*
   548 nsWindow::FindTopLevel()
   549 {
   550     nsWindow *toplevel = this;
   551     while (toplevel) {
   552         if (toplevel->IsTopLevel())
   553             return toplevel;
   555         toplevel = toplevel->mParent;
   556     }
   558     ALOG("nsWindow::FindTopLevel(): couldn't find a toplevel or dialog window in this [%p] widget's hierarchy!", (void*)this);
   559     return this;
   560 }
   562 NS_IMETHODIMP
   563 nsWindow::SetFocus(bool aRaise)
   564 {
   565     if (!aRaise) {
   566         ALOG("nsWindow::SetFocus: can't set focus without raising, ignoring aRaise = false!");
   567     }
   569     nsWindow *top = FindTopLevel();
   570     top->mFocus = this;
   571     top->BringToFront();
   573     return NS_OK;
   574 }
   576 void
   577 nsWindow::BringToFront()
   578 {
   579     // If the window to be raised is the same as the currently raised one,
   580     // do nothing. We need to check the focus manager as well, as the first
   581     // window that is created will be first in the window list but won't yet
   582     // be focused.
   583     nsCOMPtr<nsIFocusManager> fm = do_GetService(FOCUSMANAGER_CONTRACTID);
   584     nsCOMPtr<nsIDOMWindow> existingTopWindow;
   585     fm->GetActiveWindow(getter_AddRefs(existingTopWindow));
   586     if (existingTopWindow && FindTopLevel() == nsWindow::TopWindow())
   587         return;
   589     if (!IsTopLevel()) {
   590         FindTopLevel()->BringToFront();
   591         return;
   592     }
   594     nsRefPtr<nsWindow> kungFuDeathGrip(this);
   596     nsWindow *oldTop = nullptr;
   597     nsWindow *newTop = this;
   598     if (!gTopLevelWindows.IsEmpty())
   599         oldTop = gTopLevelWindows[0];
   601     gTopLevelWindows.RemoveElement(this);
   602     gTopLevelWindows.InsertElementAt(0, this);
   604     if (oldTop) {
   605       nsIWidgetListener* listener = oldTop->GetWidgetListener();
   606       if (listener) {
   607           listener->WindowDeactivated();
   608       }
   609     }
   611     if (Destroyed()) {
   612         // somehow the deactivate event handler destroyed this window.
   613         // try to recover by grabbing the next window in line and activating
   614         // that instead
   615         if (gTopLevelWindows.IsEmpty())
   616             return;
   617         newTop = gTopLevelWindows[0];
   618     }
   620     if (mWidgetListener) {
   621         mWidgetListener->WindowActivated();
   622     }
   624     // force a window resize
   625     nsAppShell::gAppShell->ResendLastResizeEvent(newTop);
   626     RedrawAll();
   627 }
   629 NS_IMETHODIMP
   630 nsWindow::GetScreenBounds(nsIntRect &aRect)
   631 {
   632     nsIntPoint p = WidgetToScreenOffset();
   634     aRect.x = p.x;
   635     aRect.y = p.y;
   636     aRect.width = mBounds.width;
   637     aRect.height = mBounds.height;
   639     return NS_OK;
   640 }
   642 nsIntPoint
   643 nsWindow::WidgetToScreenOffset()
   644 {
   645     nsIntPoint p(0, 0);
   646     nsWindow *w = this;
   648     while (w && !w->IsTopLevel()) {
   649         p.x += w->mBounds.x;
   650         p.y += w->mBounds.y;
   652         w = w->mParent;
   653     }
   655     return p;
   656 }
   658 NS_IMETHODIMP
   659 nsWindow::DispatchEvent(WidgetGUIEvent* aEvent,
   660                         nsEventStatus &aStatus)
   661 {
   662     aStatus = DispatchEvent(aEvent);
   663     return NS_OK;
   664 }
   666 nsEventStatus
   667 nsWindow::DispatchEvent(WidgetGUIEvent* aEvent)
   668 {
   669     if (mWidgetListener) {
   670         nsEventStatus status = mWidgetListener->HandleEvent(aEvent, mUseAttachedEvents);
   672         switch (aEvent->message) {
   673         case NS_COMPOSITION_START:
   674             MOZ_ASSERT(!mIMEComposing);
   675             mIMEComposing = true;
   676             break;
   677         case NS_COMPOSITION_END:
   678             MOZ_ASSERT(mIMEComposing);
   679             mIMEComposing = false;
   680             mIMEComposingText.Truncate();
   681             break;
   682         case NS_TEXT_TEXT:
   683             MOZ_ASSERT(mIMEComposing);
   684             mIMEComposingText = aEvent->AsTextEvent()->theText;
   685             break;
   686         }
   687         return status;
   688     }
   689     return nsEventStatus_eIgnore;
   690 }
   692 NS_IMETHODIMP
   693 nsWindow::MakeFullScreen(bool aFullScreen)
   694 {
   695     mozilla::widget::android::GeckoAppShell::SetFullScreen(aFullScreen);
   696     return NS_OK;
   697 }
   699 NS_IMETHODIMP
   700 nsWindow::SetWindowClass(const nsAString& xulWinType)
   701 {
   702     return NS_OK;
   703 }
   705 mozilla::layers::LayerManager*
   706 nsWindow::GetLayerManager(PLayerTransactionChild*, LayersBackend, LayerManagerPersistence,
   707                           bool* aAllowRetaining)
   708 {
   709     if (aAllowRetaining) {
   710         *aAllowRetaining = true;
   711     }
   712     if (mLayerManager) {
   713         return mLayerManager;
   714     }
   715     // for OMTC allow use of the single layer manager/compositor
   716     // shared across all windows
   717     if (ShouldUseOffMainThreadCompositing()) {
   718         return sLayerManager;
   719     }
   720     return nullptr;
   721 }
   723 void
   724 nsWindow::CreateLayerManager(int aCompositorWidth, int aCompositorHeight)
   725 {
   726     if (mLayerManager) {
   727         return;
   728     }
   730     nsWindow *topLevelWindow = FindTopLevel();
   731     if (!topLevelWindow || topLevelWindow->mWindowType == eWindowType_invisible) {
   732         // don't create a layer manager for an invisible top-level window
   733         return;
   734     }
   736     mUseLayersAcceleration = ComputeShouldAccelerate(mUseLayersAcceleration);
   738     if (ShouldUseOffMainThreadCompositing()) {
   739         if (sLayerManager) {
   740             return;
   741         }
   742         CreateCompositor(aCompositorWidth, aCompositorHeight);
   743         if (mLayerManager) {
   744             // for OMTC create a single layer manager and compositor that will be
   745             // used for all windows.
   746             SetCompositor(mLayerManager, mCompositorParent, mCompositorChild);
   747             sCompositorPaused = false;
   748             return;
   749         }
   751         // If we get here, then off main thread compositing failed to initialize.
   752         sFailedToCreateGLContext = true;
   753     }
   755     if (!mUseLayersAcceleration || sFailedToCreateGLContext) {
   756         printf_stderr(" -- creating basic, not accelerated\n");
   757         mLayerManager = CreateBasicLayerManager();
   758     }
   759 }
   761 void
   762 nsWindow::OnGlobalAndroidEvent(AndroidGeckoEvent *ae)
   763 {
   764     nsWindow *win = TopWindow();
   765     if (!win)
   766         return;
   768     switch (ae->Type()) {
   769         case AndroidGeckoEvent::FORCED_RESIZE:
   770             win->mBounds.width = 0;
   771             win->mBounds.height = 0;
   772             // also resize the children
   773             for (uint32_t i = 0; i < win->mChildren.Length(); i++) {
   774                 win->mChildren[i]->mBounds.width = 0;
   775                 win->mChildren[i]->mBounds.height = 0;
   776             }
   777         case AndroidGeckoEvent::SIZE_CHANGED: {
   778             const nsTArray<nsIntPoint>& points = ae->Points();
   779             NS_ASSERTION(points.Length() == 2, "Size changed does not have enough coordinates");
   781             int nw = points[0].x;
   782             int nh = points[0].y;
   784             if (ae->Type() == AndroidGeckoEvent::FORCED_RESIZE || nw != gAndroidBounds.width ||
   785                 nh != gAndroidBounds.height) {
   786                 gAndroidBounds.width = nw;
   787                 gAndroidBounds.height = nh;
   789                 // tell all the windows about the new size
   790                 for (size_t i = 0; i < gTopLevelWindows.Length(); ++i) {
   791                     if (gTopLevelWindows[i]->mIsVisible)
   792                         gTopLevelWindows[i]->Resize(gAndroidBounds.width,
   793                                                     gAndroidBounds.height,
   794                                                     false);
   795                 }
   796             }
   798             int newScreenWidth = points[1].x;
   799             int newScreenHeight = points[1].y;
   801             if (newScreenWidth == gAndroidScreenBounds.width &&
   802                 newScreenHeight == gAndroidScreenBounds.height)
   803                 break;
   805             gAndroidScreenBounds.width = newScreenWidth;
   806             gAndroidScreenBounds.height = newScreenHeight;
   808             if (XRE_GetProcessType() != GeckoProcessType_Default ||
   809                 !BrowserTabsRemote()) {
   810                 break;
   811             }
   813             // Tell the content process the new screen size.
   814             nsTArray<ContentParent*> cplist;
   815             ContentParent::GetAll(cplist);
   816             for (uint32_t i = 0; i < cplist.Length(); ++i)
   817                 unused << cplist[i]->SendScreenSizeChanged(gAndroidScreenBounds);
   819             if (gContentCreationNotifier)
   820                 break;
   822             // If the content process is not created yet, wait until it's
   823             // created and then tell it the screen size.
   824             nsCOMPtr<nsIObserverService> obs = do_GetService("@mozilla.org/observer-service;1");
   825             if (!obs)
   826                 break;
   828             nsRefPtr<ContentCreationNotifier> notifier = new ContentCreationNotifier;
   829             if (NS_SUCCEEDED(obs->AddObserver(notifier, "ipc:content-created", false))) {
   830                 if (NS_SUCCEEDED(obs->AddObserver(notifier, "xpcom-shutdown", false)))
   831                     gContentCreationNotifier = notifier;
   832                 else
   833                     obs->RemoveObserver(notifier, "ipc:content-created");
   834             }
   835             break;
   836         }
   838         case AndroidGeckoEvent::MOTION_EVENT: {
   839             win->UserActivity();
   840             if (!gTopLevelWindows.IsEmpty()) {
   841                 nsIntPoint pt(0,0);
   842                 const nsTArray<nsIntPoint>& points = ae->Points();
   843                 if (points.Length() > 0) {
   844                     pt = points[0];
   845                 }
   846                 pt.x = clamped(pt.x, 0, std::max(gAndroidBounds.width - 1, 0));
   847                 pt.y = clamped(pt.y, 0, std::max(gAndroidBounds.height - 1, 0));
   848                 nsWindow *target = win->FindWindowForPoint(pt);
   849 #if 0
   850                 ALOG("MOTION_EVENT %f,%f -> %p (visible: %d children: %d)", pt.x, pt.y, (void*)target,
   851                      target ? target->mIsVisible : 0,
   852                      target ? target->mChildren.Length() : 0);
   854                 DumpWindows();
   855 #endif
   856                 if (target) {
   857                     bool preventDefaultActions = target->OnMultitouchEvent(ae);
   858                     if (!preventDefaultActions && ae->Count() < 2)
   859                         target->OnMouseEvent(ae);
   860                 }
   861             }
   862             break;
   863         }
   865         case AndroidGeckoEvent::NATIVE_GESTURE_EVENT: {
   866             nsIntPoint pt(0,0);
   867             const nsTArray<nsIntPoint>& points = ae->Points();
   868             if (points.Length() > 0) {
   869                 pt = points[0];
   870             }
   871             pt.x = clamped(pt.x, 0, std::max(gAndroidBounds.width - 1, 0));
   872             pt.y = clamped(pt.y, 0, std::max(gAndroidBounds.height - 1, 0));
   873             nsWindow *target = win->FindWindowForPoint(pt);
   875             target->OnNativeGestureEvent(ae);
   876             break;
   877         }
   879         case AndroidGeckoEvent::KEY_EVENT:
   880             win->UserActivity();
   881             if (win->mFocus)
   882                 win->mFocus->OnKeyEvent(ae);
   883             break;
   885         case AndroidGeckoEvent::DRAW:
   886             layers::renderTraceEventStart("Global draw start", "414141");
   887             win->OnDraw(ae);
   888             layers::renderTraceEventEnd("414141");
   889             break;
   891         case AndroidGeckoEvent::IME_EVENT:
   892             win->UserActivity();
   893             if (win->mFocus) {
   894                 win->mFocus->OnIMEEvent(ae);
   895             } else {
   896                 NS_WARNING("Sending unexpected IME event to top window");
   897                 win->OnIMEEvent(ae);
   898             }
   899             break;
   901         case AndroidGeckoEvent::IME_KEY_EVENT:
   902             // Keys synthesized by Java IME code are saved in the mIMEKeyEvents
   903             // array until the next IME_REPLACE_TEXT event, at which point
   904             // these keys are dispatched in sequence.
   905             if (win->mFocus) {
   906                 win->mFocus->mIMEKeyEvents.AppendElement(*ae);
   907             }
   908             break;
   910         case AndroidGeckoEvent::COMPOSITOR_CREATE:
   911             win->CreateLayerManager(ae->Width(), ae->Height());
   912             break;
   914         case AndroidGeckoEvent::COMPOSITOR_PAUSE:
   915             // The compositor gets paused when the app is about to go into the
   916             // background. While the compositor is paused, we need to ensure that
   917             // no layer tree updates (from draw events) occur, since the compositor
   918             // cannot make a GL context current in order to process updates.
   919             if (sCompositorChild) {
   920                 sCompositorChild->SendPause();
   921             }
   922             sCompositorPaused = true;
   923             break;
   925         case AndroidGeckoEvent::COMPOSITOR_RESUME:
   926             // When we receive this, the compositor has already been told to
   927             // resume. (It turns out that waiting till we reach here to tell
   928             // the compositor to resume takes too long, resulting in a black
   929             // flash.) This means it's now safe for layer updates to occur.
   930             // Since we might have prevented one or more draw events from
   931             // occurring while the compositor was paused, we need to schedule
   932             // a draw event now.
   933             if (!sCompositorPaused) {
   934                 win->RedrawAll();
   935             }
   936             break;
   937     }
   938 }
   940 void
   941 nsWindow::OnAndroidEvent(AndroidGeckoEvent *ae)
   942 {
   943     switch (ae->Type()) {
   944         case AndroidGeckoEvent::DRAW:
   945             OnDraw(ae);
   946             break;
   948         default:
   949             ALOG("Window got targetted android event type %d, but didn't handle!", ae->Type());
   950             break;
   951     }
   952 }
   954 bool
   955 nsWindow::DrawTo(gfxASurface *targetSurface)
   956 {
   957     nsIntRect boundsRect(0, 0, mBounds.width, mBounds.height);
   958     return DrawTo(targetSurface, boundsRect);
   959 }
   961 bool
   962 nsWindow::DrawTo(gfxASurface *targetSurface, const nsIntRect &invalidRect)
   963 {
   964     mozilla::layers::RenderTraceScope trace("DrawTo", "717171");
   965     if (!mIsVisible || !mWidgetListener || !GetLayerManager(nullptr))
   966         return false;
   968     nsRefPtr<nsWindow> kungFuDeathGrip(this);
   969     nsIntRect boundsRect(0, 0, mBounds.width, mBounds.height);
   971     // Figure out if any of our children cover this widget completely
   972     int32_t coveringChildIndex = -1;
   973     for (uint32_t i = 0; i < mChildren.Length(); ++i) {
   974         if (mChildren[i]->mBounds.IsEmpty())
   975             continue;
   977         if (mChildren[i]->mBounds.Contains(boundsRect)) {
   978             coveringChildIndex = int32_t(i);
   979         }
   980     }
   982     // If we have no covering child, then we need to render this.
   983     if (coveringChildIndex == -1) {
   984         nsIntRegion region = invalidRect;
   986         mWidgetListener->WillPaintWindow(this);
   988         switch (GetLayerManager(nullptr)->GetBackendType()) {
   989             case mozilla::layers::LayersBackend::LAYERS_BASIC: {
   991                 nsRefPtr<gfxContext> ctx = new gfxContext(targetSurface);
   993                 {
   994                     mozilla::layers::RenderTraceScope trace2("Basic DrawTo", "727272");
   995                     AutoLayerManagerSetup
   996                       setupLayerManager(this, ctx, mozilla::layers::BufferMode::BUFFER_NONE);
   998                     mWidgetListener->PaintWindow(this, region);
   999                 }
  1000                 break;
  1003             case mozilla::layers::LayersBackend::LAYERS_CLIENT: {
  1004                 mWidgetListener->PaintWindow(this, region);
  1005                 break;
  1008             default:
  1009                 NS_ERROR("Invalid layer manager");
  1012         mWidgetListener->DidPaintWindow();
  1014         // We had no covering child, so make sure we draw all the children,
  1015         // starting from index 0.
  1016         coveringChildIndex = 0;
  1019     gfxPoint offset;
  1021     if (targetSurface)
  1022         offset = targetSurface->GetDeviceOffset();
  1024     for (uint32_t i = coveringChildIndex; i < mChildren.Length(); ++i) {
  1025         if (mChildren[i]->mBounds.IsEmpty() ||
  1026             !mChildren[i]->mBounds.Intersects(boundsRect)) {
  1027             continue;
  1030         if (targetSurface)
  1031             targetSurface->SetDeviceOffset(offset + gfxPoint(mChildren[i]->mBounds.x,
  1032                                                              mChildren[i]->mBounds.y));
  1034         bool ok = mChildren[i]->DrawTo(targetSurface, invalidRect);
  1036         if (!ok) {
  1037             ALOG("nsWindow[%p]::DrawTo child %d[%p] returned FALSE!", (void*) this, i, (void*)mChildren[i]);
  1041     if (targetSurface)
  1042         targetSurface->SetDeviceOffset(offset);
  1044     return true;
  1047 void
  1048 nsWindow::OnDraw(AndroidGeckoEvent *ae)
  1050     if (!IsTopLevel()) {
  1051         ALOG("##### redraw for window %p, which is not a toplevel window -- sending to toplevel!", (void*) this);
  1052         DumpWindows();
  1053         return;
  1056     if (!mIsVisible) {
  1057         ALOG("##### redraw for window %p, which is not visible -- ignoring!", (void*) this);
  1058         DumpWindows();
  1059         return;
  1062     nsRefPtr<nsWindow> kungFuDeathGrip(this);
  1064     AutoLocalJNIFrame jniFrame;
  1066     // We're paused, or we haven't been given a window-size yet, so do nothing
  1067     if (sCompositorPaused || gAndroidBounds.width <= 0 || gAndroidBounds.height <= 0) {
  1068         return;
  1071     int bytesPerPixel = 2;
  1072     gfxImageFormat format = gfxImageFormat::RGB16_565;
  1073     if (AndroidBridge::Bridge()->GetScreenDepth() == 24) {
  1074         bytesPerPixel = 4;
  1075         format = gfxImageFormat::RGB24;
  1078     layers::renderTraceEventStart("Get surface", "424545");
  1079     static unsigned char bits2[32 * 32 * 4];
  1080     nsRefPtr<gfxImageSurface> targetSurface =
  1081         new gfxImageSurface(bits2, gfxIntSize(32, 32), 32 * bytesPerPixel, format);
  1082     layers::renderTraceEventEnd("Get surface", "424545");
  1084     layers::renderTraceEventStart("Widget draw to", "434646");
  1085     if (targetSurface->CairoStatus()) {
  1086         ALOG("### Failed to create a valid surface from the bitmap");
  1087     } else {
  1088         DrawTo(targetSurface, ae->Rect());
  1090     layers::renderTraceEventEnd("Widget draw to", "434646");
  1093 void
  1094 nsWindow::OnSizeChanged(const gfxIntSize& aSize)
  1096     ALOG("nsWindow: %p OnSizeChanged [%d %d]", (void*)this, aSize.width, aSize.height);
  1098     mBounds.width = aSize.width;
  1099     mBounds.height = aSize.height;
  1101     if (mWidgetListener) {
  1102         mWidgetListener->WindowResized(this, aSize.width, aSize.height);
  1106 void
  1107 nsWindow::InitEvent(WidgetGUIEvent& event, nsIntPoint* aPoint)
  1109     if (aPoint) {
  1110         event.refPoint.x = aPoint->x;
  1111         event.refPoint.y = aPoint->y;
  1112     } else {
  1113         event.refPoint.x = 0;
  1114         event.refPoint.y = 0;
  1117     event.time = PR_Now() / 1000;
  1120 gfxIntSize
  1121 nsWindow::GetAndroidScreenBounds()
  1123     if (XRE_GetProcessType() == GeckoProcessType_Content) {
  1124         return ContentChild::GetSingleton()->GetScreenSize();
  1126     return gAndroidScreenBounds;
  1129 void *
  1130 nsWindow::GetNativeData(uint32_t aDataType)
  1132     switch (aDataType) {
  1133         // used by GLContextProviderEGL, nullptr is EGL_DEFAULT_DISPLAY
  1134         case NS_NATIVE_DISPLAY:
  1135             return nullptr;
  1137         case NS_NATIVE_WIDGET:
  1138             return (void *) this;
  1141     return nullptr;
  1144 void
  1145 nsWindow::OnMouseEvent(AndroidGeckoEvent *ae)
  1147     nsRefPtr<nsWindow> kungFuDeathGrip(this);
  1149     WidgetMouseEvent event = ae->MakeMouseEvent(this);
  1150     if (event.message == NS_EVENT_NULL) {
  1151         // invalid event type, abort
  1152         return;
  1155     // XXX add the double-click handling logic here
  1156     DispatchEvent(&event);
  1159 bool nsWindow::OnMultitouchEvent(AndroidGeckoEvent *ae)
  1161     nsRefPtr<nsWindow> kungFuDeathGrip(this);
  1163     // End any composition in progress in case the touch event listener
  1164     // modifies the input field value (see bug 856155)
  1165     RemoveIMEComposition();
  1167     // This is set to true once we have called SetPreventPanning() exactly
  1168     // once for a given sequence of touch events. It is reset on the start
  1169     // of the next sequence.
  1170     static bool sDefaultPreventedNotified = false;
  1171     static bool sLastWasDownEvent = false;
  1173     bool preventDefaultActions = false;
  1174     bool isDownEvent = false;
  1176     WidgetTouchEvent event = ae->MakeTouchEvent(this);
  1177     if (event.message != NS_EVENT_NULL) {
  1178         nsEventStatus status;
  1179         DispatchEvent(&event, status);
  1180         // We check mMultipleActionsPrevented because that's what <input type=range>
  1181         // sets when someone starts dragging the thumb. It doesn't set the status
  1182         // because it doesn't want to prevent the code that gives the input focus
  1183         // from running.
  1184         preventDefaultActions = (status == nsEventStatus_eConsumeNoDefault ||
  1185                                 event.mFlags.mMultipleActionsPrevented);
  1186         isDownEvent = (event.message == NS_TOUCH_START);
  1189     // if the last event we got was a down event, then by now we know for sure whether
  1190     // this block has been default-prevented or not. if we haven't already sent the
  1191     // notification for this block, do so now.
  1192     if (sLastWasDownEvent && !sDefaultPreventedNotified) {
  1193         // if this event is a down event, that means it's the start of a new block, and the
  1194         // previous block should not be default-prevented
  1195         bool defaultPrevented = isDownEvent ? false : preventDefaultActions;
  1196         mozilla::widget::android::GeckoAppShell::NotifyDefaultPrevented(defaultPrevented);
  1197         sDefaultPreventedNotified = true;
  1200     // now, if this event is a down event, then we might already know that it has been
  1201     // default-prevented. if so, we send the notification right away; otherwise we wait
  1202     // for the next event.
  1203     if (isDownEvent) {
  1204         if (preventDefaultActions) {
  1205             mozilla::widget::android::GeckoAppShell::NotifyDefaultPrevented(true);
  1206             sDefaultPreventedNotified = true;
  1207         } else {
  1208             sDefaultPreventedNotified = false;
  1211     sLastWasDownEvent = isDownEvent;
  1213     return preventDefaultActions;
  1216 void
  1217 nsWindow::OnNativeGestureEvent(AndroidGeckoEvent *ae)
  1219   nsIntPoint pt(ae->Points()[0].x,
  1220                 ae->Points()[0].y);
  1221   double delta = ae->X();
  1222   int msg = 0;
  1224   switch (ae->Action()) {
  1225       case AndroidMotionEvent::ACTION_MAGNIFY_START:
  1226           msg = NS_SIMPLE_GESTURE_MAGNIFY_START;
  1227           mStartDist = delta;
  1228           mLastDist = delta;
  1229           break;
  1230       case AndroidMotionEvent::ACTION_MAGNIFY:
  1231           msg = NS_SIMPLE_GESTURE_MAGNIFY_UPDATE;
  1232           delta -= mLastDist;
  1233           mLastDist += delta;
  1234           break;
  1235       case AndroidMotionEvent::ACTION_MAGNIFY_END:
  1236           msg = NS_SIMPLE_GESTURE_MAGNIFY;
  1237           delta -= mStartDist;
  1238           break;
  1239       default:
  1240           return;
  1243   nsRefPtr<nsWindow> kungFuDeathGrip(this);
  1244   DispatchGestureEvent(msg, 0, delta, pt, ae->Time());
  1247 void
  1248 nsWindow::DispatchGestureEvent(uint32_t msg, uint32_t direction, double delta,
  1249                                const nsIntPoint &refPoint, uint64_t time)
  1251     WidgetSimpleGestureEvent event(true, msg, this);
  1253     event.direction = direction;
  1254     event.delta = delta;
  1255     event.modifiers = 0;
  1256     event.time = time;
  1257     event.refPoint = LayoutDeviceIntPoint::FromUntyped(refPoint);
  1259     DispatchEvent(&event);
  1263 void
  1264 nsWindow::DispatchMotionEvent(WidgetInputEvent &event, AndroidGeckoEvent *ae,
  1265                               const nsIntPoint &refPoint)
  1267     nsIntPoint offset = WidgetToScreenOffset();
  1269     event.modifiers = ae->DOMModifiers();
  1270     event.time = ae->Time();
  1272     // XXX possibly bound the range of event.refPoint here.
  1273     //     some code may get confused.
  1274     event.refPoint = LayoutDeviceIntPoint::FromUntyped(refPoint - offset);
  1276     DispatchEvent(&event);
  1279 static unsigned int ConvertAndroidKeyCodeToDOMKeyCode(int androidKeyCode)
  1281     // Special-case alphanumeric keycodes because they are most common.
  1282     if (androidKeyCode >= AKEYCODE_A &&
  1283         androidKeyCode <= AKEYCODE_Z) {
  1284         return androidKeyCode - AKEYCODE_A + NS_VK_A;
  1287     if (androidKeyCode >= AKEYCODE_0 &&
  1288         androidKeyCode <= AKEYCODE_9) {
  1289         return androidKeyCode - AKEYCODE_0 + NS_VK_0;
  1292     switch (androidKeyCode) {
  1293         // KEYCODE_UNKNOWN (0) ... KEYCODE_HOME (3)
  1294         case AKEYCODE_BACK:               return NS_VK_ESCAPE;
  1295         // KEYCODE_CALL (5) ... KEYCODE_POUND (18)
  1296         case AKEYCODE_DPAD_UP:            return NS_VK_UP;
  1297         case AKEYCODE_DPAD_DOWN:          return NS_VK_DOWN;
  1298         case AKEYCODE_DPAD_LEFT:          return NS_VK_LEFT;
  1299         case AKEYCODE_DPAD_RIGHT:         return NS_VK_RIGHT;
  1300         case AKEYCODE_DPAD_CENTER:        return NS_VK_RETURN;
  1301         case AKEYCODE_VOLUME_UP:          return NS_VK_VOLUME_UP;
  1302         case AKEYCODE_VOLUME_DOWN:        return NS_VK_VOLUME_DOWN;
  1303         // KEYCODE_VOLUME_POWER (26) ... KEYCODE_Z (54)
  1304         case AKEYCODE_COMMA:              return NS_VK_COMMA;
  1305         case AKEYCODE_PERIOD:             return NS_VK_PERIOD;
  1306         case AKEYCODE_ALT_LEFT:           return NS_VK_ALT;
  1307         case AKEYCODE_ALT_RIGHT:          return NS_VK_ALT;
  1308         case AKEYCODE_SHIFT_LEFT:         return NS_VK_SHIFT;
  1309         case AKEYCODE_SHIFT_RIGHT:        return NS_VK_SHIFT;
  1310         case AKEYCODE_TAB:                return NS_VK_TAB;
  1311         case AKEYCODE_SPACE:              return NS_VK_SPACE;
  1312         // KEYCODE_SYM (63) ... KEYCODE_ENVELOPE (65)
  1313         case AKEYCODE_ENTER:              return NS_VK_RETURN;
  1314         case AKEYCODE_DEL:                return NS_VK_BACK; // Backspace
  1315         case AKEYCODE_GRAVE:              return NS_VK_BACK_QUOTE;
  1316         // KEYCODE_MINUS (69)
  1317         case AKEYCODE_EQUALS:             return NS_VK_EQUALS;
  1318         case AKEYCODE_LEFT_BRACKET:       return NS_VK_OPEN_BRACKET;
  1319         case AKEYCODE_RIGHT_BRACKET:      return NS_VK_CLOSE_BRACKET;
  1320         case AKEYCODE_BACKSLASH:          return NS_VK_BACK_SLASH;
  1321         case AKEYCODE_SEMICOLON:          return NS_VK_SEMICOLON;
  1322         // KEYCODE_APOSTROPHE (75)
  1323         case AKEYCODE_SLASH:              return NS_VK_SLASH;
  1324         // KEYCODE_AT (77) ... KEYCODE_MEDIA_FAST_FORWARD (90)
  1325         case AKEYCODE_MUTE:               return NS_VK_VOLUME_MUTE;
  1326         case AKEYCODE_PAGE_UP:            return NS_VK_PAGE_UP;
  1327         case AKEYCODE_PAGE_DOWN:          return NS_VK_PAGE_DOWN;
  1328         // KEYCODE_PICTSYMBOLS (94) ... KEYCODE_BUTTON_MODE (110)
  1329         case AKEYCODE_ESCAPE:             return NS_VK_ESCAPE;
  1330         case AKEYCODE_FORWARD_DEL:        return NS_VK_DELETE;
  1331         case AKEYCODE_CTRL_LEFT:          return NS_VK_CONTROL;
  1332         case AKEYCODE_CTRL_RIGHT:         return NS_VK_CONTROL;
  1333         case AKEYCODE_CAPS_LOCK:          return NS_VK_CAPS_LOCK;
  1334         case AKEYCODE_SCROLL_LOCK:        return NS_VK_SCROLL_LOCK;
  1335         // KEYCODE_META_LEFT (117) ... KEYCODE_FUNCTION (119)
  1336         case AKEYCODE_SYSRQ:              return NS_VK_PRINTSCREEN;
  1337         case AKEYCODE_BREAK:              return NS_VK_PAUSE;
  1338         case AKEYCODE_MOVE_HOME:          return NS_VK_HOME;
  1339         case AKEYCODE_MOVE_END:           return NS_VK_END;
  1340         case AKEYCODE_INSERT:             return NS_VK_INSERT;
  1341         // KEYCODE_FORWARD (125) ... KEYCODE_MEDIA_RECORD (130)
  1342         case AKEYCODE_F1:                 return NS_VK_F1;
  1343         case AKEYCODE_F2:                 return NS_VK_F2;
  1344         case AKEYCODE_F3:                 return NS_VK_F3;
  1345         case AKEYCODE_F4:                 return NS_VK_F4;
  1346         case AKEYCODE_F5:                 return NS_VK_F5;
  1347         case AKEYCODE_F6:                 return NS_VK_F6;
  1348         case AKEYCODE_F7:                 return NS_VK_F7;
  1349         case AKEYCODE_F8:                 return NS_VK_F8;
  1350         case AKEYCODE_F9:                 return NS_VK_F9;
  1351         case AKEYCODE_F10:                return NS_VK_F10;
  1352         case AKEYCODE_F11:                return NS_VK_F11;
  1353         case AKEYCODE_F12:                return NS_VK_F12;
  1354         case AKEYCODE_NUM_LOCK:           return NS_VK_NUM_LOCK;
  1355         case AKEYCODE_NUMPAD_0:           return NS_VK_NUMPAD0;
  1356         case AKEYCODE_NUMPAD_1:           return NS_VK_NUMPAD1;
  1357         case AKEYCODE_NUMPAD_2:           return NS_VK_NUMPAD2;
  1358         case AKEYCODE_NUMPAD_3:           return NS_VK_NUMPAD3;
  1359         case AKEYCODE_NUMPAD_4:           return NS_VK_NUMPAD4;
  1360         case AKEYCODE_NUMPAD_5:           return NS_VK_NUMPAD5;
  1361         case AKEYCODE_NUMPAD_6:           return NS_VK_NUMPAD6;
  1362         case AKEYCODE_NUMPAD_7:           return NS_VK_NUMPAD7;
  1363         case AKEYCODE_NUMPAD_8:           return NS_VK_NUMPAD8;
  1364         case AKEYCODE_NUMPAD_9:           return NS_VK_NUMPAD9;
  1365         case AKEYCODE_NUMPAD_DIVIDE:      return NS_VK_DIVIDE;
  1366         case AKEYCODE_NUMPAD_MULTIPLY:    return NS_VK_MULTIPLY;
  1367         case AKEYCODE_NUMPAD_SUBTRACT:    return NS_VK_SUBTRACT;
  1368         case AKEYCODE_NUMPAD_ADD:         return NS_VK_ADD;
  1369         case AKEYCODE_NUMPAD_DOT:         return NS_VK_DECIMAL;
  1370         case AKEYCODE_NUMPAD_COMMA:       return NS_VK_SEPARATOR;
  1371         case AKEYCODE_NUMPAD_ENTER:       return NS_VK_RETURN;
  1372         case AKEYCODE_NUMPAD_EQUALS:      return NS_VK_EQUALS;
  1373         // KEYCODE_NUMPAD_LEFT_PAREN (162) ... KEYCODE_CALCULATOR (210)
  1375         // Needs to confirm the behavior.  If the key switches the open state
  1376         // of Japanese IME (or switches input character between Hiragana and
  1377         // Roman numeric characters), then, it might be better to use
  1378         // NS_VK_KANJI which is used for Alt+Zenkaku/Hankaku key on Windows.
  1379         case AKEYCODE_ZENKAKU_HANKAKU:    return 0;
  1380         case AKEYCODE_EISU:               return NS_VK_EISU;
  1381         case AKEYCODE_MUHENKAN:           return NS_VK_NONCONVERT;
  1382         case AKEYCODE_HENKAN:             return NS_VK_CONVERT;
  1383         case AKEYCODE_KATAKANA_HIRAGANA:  return 0;
  1384         case AKEYCODE_YEN:                return NS_VK_BACK_SLASH; // Same as other platforms.
  1385         case AKEYCODE_RO:                 return NS_VK_BACK_SLASH; // Same as other platforms.
  1386         case AKEYCODE_KANA:               return NS_VK_KANA;
  1387         case AKEYCODE_ASSIST:             return NS_VK_HELP;
  1389         // the A key is the action key for gamepad devices.
  1390         case AKEYCODE_BUTTON_A:          return NS_VK_RETURN;
  1392         default:
  1393             ALOG("ConvertAndroidKeyCodeToDOMKeyCode: "
  1394                  "No DOM keycode for Android keycode %d", androidKeyCode);
  1395         return 0;
  1399 static KeyNameIndex
  1400 ConvertAndroidKeyCodeToKeyNameIndex(AndroidGeckoEvent& aAndroidGeckoEvent)
  1402     int keyCode = aAndroidGeckoEvent.KeyCode();
  1403     // Special-case alphanumeric keycodes because they are most common.
  1404     if (keyCode >= AKEYCODE_A && keyCode <= AKEYCODE_Z) {
  1405         return KEY_NAME_INDEX_USE_STRING;
  1408     if (keyCode >= AKEYCODE_0 && keyCode <= AKEYCODE_9) {
  1409         return KEY_NAME_INDEX_USE_STRING;
  1412     switch (keyCode) {
  1414 #define NS_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX(aNativeKey, aKeyNameIndex) \
  1415         case aNativeKey: return aKeyNameIndex;
  1417 #include "NativeKeyToDOMKeyName.h"
  1419 #undef NS_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX
  1421         // KEYCODE_0 (7) ... KEYCODE_9 (16)
  1422         case AKEYCODE_STAR:               // '*' key
  1423         case AKEYCODE_POUND:              // '#' key
  1425         // KEYCODE_A (29) ... KEYCODE_Z (54)
  1427         case AKEYCODE_COMMA:              // ',' key
  1428         case AKEYCODE_PERIOD:             // '.' key
  1429         case AKEYCODE_SPACE:
  1430         case AKEYCODE_GRAVE:              // '`' key
  1431         case AKEYCODE_MINUS:              // '-' key
  1432         case AKEYCODE_EQUALS:             // '=' key
  1433         case AKEYCODE_LEFT_BRACKET:       // '[' key
  1434         case AKEYCODE_RIGHT_BRACKET:      // ']' key
  1435         case AKEYCODE_BACKSLASH:          // '\' key
  1436         case AKEYCODE_SEMICOLON:          // ';' key
  1437         case AKEYCODE_APOSTROPHE:         // ''' key
  1438         case AKEYCODE_SLASH:              // '/' key
  1439         case AKEYCODE_AT:                 // '@' key
  1440         case AKEYCODE_PLUS:               // '+' key
  1442         case AKEYCODE_NUMPAD_0:
  1443         case AKEYCODE_NUMPAD_1:
  1444         case AKEYCODE_NUMPAD_2:
  1445         case AKEYCODE_NUMPAD_3:
  1446         case AKEYCODE_NUMPAD_4:
  1447         case AKEYCODE_NUMPAD_5:
  1448         case AKEYCODE_NUMPAD_6:
  1449         case AKEYCODE_NUMPAD_7:
  1450         case AKEYCODE_NUMPAD_8:
  1451         case AKEYCODE_NUMPAD_9:
  1452         case AKEYCODE_NUMPAD_DIVIDE:
  1453         case AKEYCODE_NUMPAD_MULTIPLY:
  1454         case AKEYCODE_NUMPAD_SUBTRACT:
  1455         case AKEYCODE_NUMPAD_ADD:
  1456         case AKEYCODE_NUMPAD_DOT:
  1457         case AKEYCODE_NUMPAD_COMMA:
  1458         case AKEYCODE_NUMPAD_EQUALS:
  1459         case AKEYCODE_NUMPAD_LEFT_PAREN:
  1460         case AKEYCODE_NUMPAD_RIGHT_PAREN:
  1462         case AKEYCODE_YEN:                // yen sign key
  1463         case AKEYCODE_RO:                 // Japanese Ro key
  1464             return KEY_NAME_INDEX_USE_STRING;
  1466         case AKEYCODE_SOFT_LEFT:
  1467         case AKEYCODE_SOFT_RIGHT:
  1468         case AKEYCODE_CALL:
  1469         case AKEYCODE_ENDCALL:
  1470         case AKEYCODE_SYM:                // Symbol modifier
  1471         case AKEYCODE_NUM:                // XXX Not sure
  1472         case AKEYCODE_HEADSETHOOK:
  1473         case AKEYCODE_FOCUS:
  1474         case AKEYCODE_NOTIFICATION:       // XXX Not sure
  1475         case AKEYCODE_PICTSYMBOLS:
  1477         case AKEYCODE_BUTTON_A:
  1478         case AKEYCODE_BUTTON_B:
  1479         case AKEYCODE_BUTTON_C:
  1480         case AKEYCODE_BUTTON_X:
  1481         case AKEYCODE_BUTTON_Y:
  1482         case AKEYCODE_BUTTON_Z:
  1483         case AKEYCODE_BUTTON_L1:
  1484         case AKEYCODE_BUTTON_R1:
  1485         case AKEYCODE_BUTTON_L2:
  1486         case AKEYCODE_BUTTON_R2:
  1487         case AKEYCODE_BUTTON_THUMBL:
  1488         case AKEYCODE_BUTTON_THUMBR:
  1489         case AKEYCODE_BUTTON_START:
  1490         case AKEYCODE_BUTTON_SELECT:
  1491         case AKEYCODE_BUTTON_MODE:
  1493         case AKEYCODE_MUTE: // mutes the microphone
  1494         case AKEYCODE_MEDIA_CLOSE:
  1496         case AKEYCODE_ZOOM_IN:
  1497         case AKEYCODE_ZOOM_OUT:
  1498         case AKEYCODE_DVR:
  1499         case AKEYCODE_TV_POWER:
  1500         case AKEYCODE_TV_INPUT:
  1501         case AKEYCODE_STB_POWER:
  1502         case AKEYCODE_STB_INPUT:
  1503         case AKEYCODE_AVR_POWER:
  1504         case AKEYCODE_AVR_INPUT:
  1506         case AKEYCODE_BUTTON_1:
  1507         case AKEYCODE_BUTTON_2:
  1508         case AKEYCODE_BUTTON_3:
  1509         case AKEYCODE_BUTTON_4:
  1510         case AKEYCODE_BUTTON_5:
  1511         case AKEYCODE_BUTTON_6:
  1512         case AKEYCODE_BUTTON_7:
  1513         case AKEYCODE_BUTTON_8:
  1514         case AKEYCODE_BUTTON_9:
  1515         case AKEYCODE_BUTTON_10:
  1516         case AKEYCODE_BUTTON_11:
  1517         case AKEYCODE_BUTTON_12:
  1518         case AKEYCODE_BUTTON_13:
  1519         case AKEYCODE_BUTTON_14:
  1520         case AKEYCODE_BUTTON_15:
  1521         case AKEYCODE_BUTTON_16:
  1523         case AKEYCODE_LANGUAGE_SWITCH:
  1524         case AKEYCODE_MANNER_MODE:
  1525         case AKEYCODE_3D_MODE:
  1526         case AKEYCODE_CONTACTS:
  1527         case AKEYCODE_CALENDAR:
  1528         case AKEYCODE_MUSIC:
  1529         case AKEYCODE_CALCULATOR:
  1531         case AKEYCODE_ZENKAKU_HANKAKU:
  1532         case AKEYCODE_KATAKANA_HIRAGANA:
  1533             return KEY_NAME_INDEX_Unidentified;
  1535         case AKEYCODE_UNKNOWN:
  1536             MOZ_ASSERT(
  1537                 aAndroidGeckoEvent.Action() != AKEY_EVENT_ACTION_MULTIPLE,
  1538                 "Don't call this when action is AKEY_EVENT_ACTION_MULTIPLE!");
  1539             // It's actually an unknown key if the action isn't ACTION_MULTIPLE.
  1540             // However, it might cause text input.  So, let's check the value.
  1541             return aAndroidGeckoEvent.DOMPrintableKeyValue() ?
  1542                 KEY_NAME_INDEX_USE_STRING : KEY_NAME_INDEX_Unidentified;
  1544         default:
  1545             ALOG("ConvertAndroidKeyCodeToKeyNameIndex: "
  1546                  "No DOM key name index for Android keycode %d", keyCode);
  1547             return KEY_NAME_INDEX_Unidentified;
  1551 static void InitPluginEvent(ANPEvent* pluginEvent, ANPKeyActions keyAction,
  1552                             AndroidGeckoEvent& key)
  1554     int androidKeyCode = key.KeyCode();
  1555     uint32_t domKeyCode = ConvertAndroidKeyCodeToDOMKeyCode(androidKeyCode);
  1557     int modifiers = 0;
  1558     if (key.IsAltPressed())
  1559       modifiers |= kAlt_ANPKeyModifier;
  1560     if (key.IsShiftPressed())
  1561       modifiers |= kShift_ANPKeyModifier;
  1563     pluginEvent->inSize = sizeof(ANPEvent);
  1564     pluginEvent->eventType = kKey_ANPEventType;
  1565     pluginEvent->data.key.action = keyAction;
  1566     pluginEvent->data.key.nativeCode = androidKeyCode;
  1567     pluginEvent->data.key.virtualCode = domKeyCode;
  1568     pluginEvent->data.key.unichar = key.UnicodeChar();
  1569     pluginEvent->data.key.modifiers = modifiers;
  1570     pluginEvent->data.key.repeatCount = key.RepeatCount();
  1573 void
  1574 nsWindow::InitKeyEvent(WidgetKeyboardEvent& event, AndroidGeckoEvent& key,
  1575                        ANPEvent* pluginEvent)
  1577     event.mKeyNameIndex = ConvertAndroidKeyCodeToKeyNameIndex(key);
  1578     if (event.mKeyNameIndex == KEY_NAME_INDEX_USE_STRING) {
  1579         int keyValue = key.DOMPrintableKeyValue();
  1580         if (keyValue) {
  1581             event.mKeyValue = static_cast<char16_t>(keyValue);
  1584     uint32_t domKeyCode = ConvertAndroidKeyCodeToDOMKeyCode(key.KeyCode());
  1586     if (event.message == NS_KEY_PRESS) {
  1587         // Android gives us \n, so filter out some control characters.
  1588         int charCode = key.UnicodeChar();
  1589         if (!charCode) {
  1590             charCode = key.BaseUnicodeChar();
  1592         event.isChar = (charCode >= ' ');
  1593         event.charCode = event.isChar ? charCode : 0;
  1594         event.keyCode = (event.charCode > 0) ? 0 : domKeyCode;
  1595         event.pluginEvent = nullptr;
  1596     } else {
  1597 #ifdef DEBUG
  1598         if (event.message != NS_KEY_DOWN && event.message != NS_KEY_UP) {
  1599             ALOG("InitKeyEvent: unexpected event.message %d", event.message);
  1601 #endif // DEBUG
  1603         // Flash will want a pluginEvent for keydown and keyup events.
  1604         ANPKeyActions action = event.message == NS_KEY_DOWN
  1605                              ? kDown_ANPKeyAction
  1606                              : kUp_ANPKeyAction;
  1607         InitPluginEvent(pluginEvent, action, key);
  1609         event.isChar = false;
  1610         event.charCode = 0;
  1611         event.keyCode = domKeyCode;
  1612         event.pluginEvent = pluginEvent;
  1615     event.modifiers = key.DOMModifiers();
  1616     if (gMenu) {
  1617         event.modifiers |= MODIFIER_CONTROL;
  1619     // For keypress, if the unicode char already has modifiers applied, we
  1620     // don't specify extra modifiers. If UnicodeChar() != BaseUnicodeChar()
  1621     // it means UnicodeChar() already has modifiers applied.
  1622     // Note that on Android 4.x, Alt modifier isn't set when the key input
  1623     // causes text input even while right Alt key is pressed.  However, this
  1624     // is necessary for Android 2.3 compatibility.
  1625     if (event.message == NS_KEY_PRESS &&
  1626         key.UnicodeChar() && key.UnicodeChar() != key.BaseUnicodeChar()) {
  1627         event.modifiers &= ~(MODIFIER_ALT | MODIFIER_CONTROL | MODIFIER_META);
  1630     event.mIsRepeat =
  1631         (event.message == NS_KEY_DOWN || event.message == NS_KEY_PRESS) &&
  1632         (!!(key.Flags() & AKEY_EVENT_FLAG_LONG_PRESS) || !!key.RepeatCount());
  1633     event.location = key.DomKeyLocation();
  1634     event.time = key.Time();
  1636     if (gMenu)
  1637         gMenuConsumed = true;
  1640 void
  1641 nsWindow::HandleSpecialKey(AndroidGeckoEvent *ae)
  1643     nsRefPtr<nsWindow> kungFuDeathGrip(this);
  1644     nsCOMPtr<nsIAtom> command;
  1645     bool isDown = ae->Action() == AKEY_EVENT_ACTION_DOWN;
  1646     bool isLongPress = !!(ae->Flags() & AKEY_EVENT_FLAG_LONG_PRESS);
  1647     bool doCommand = false;
  1648     uint32_t keyCode = ae->KeyCode();
  1650     if (isDown) {
  1651         switch (keyCode) {
  1652             case AKEYCODE_BACK:
  1653                 if (isLongPress) {
  1654                     command = nsGkAtoms::Clear;
  1655                     doCommand = true;
  1657                 break;
  1658             case AKEYCODE_MENU:
  1659                 gMenu = true;
  1660                 gMenuConsumed = isLongPress;
  1661                 break;
  1663     } else {
  1664         switch (keyCode) {
  1665             case AKEYCODE_BACK: {
  1666                 // XXX Where is the keydown event for this??
  1667                 WidgetKeyboardEvent pressEvent(true, NS_KEY_PRESS, this);
  1668                 ANPEvent pluginEvent;
  1669                 InitKeyEvent(pressEvent, *ae, &pluginEvent);
  1670                 DispatchEvent(&pressEvent);
  1671                 return;
  1673             case AKEYCODE_MENU:
  1674                 gMenu = false;
  1675                 if (!gMenuConsumed) {
  1676                     command = nsGkAtoms::Menu;
  1677                     doCommand = true;
  1679                 break;
  1680             case AKEYCODE_SEARCH:
  1681                 command = nsGkAtoms::Search;
  1682                 doCommand = true;
  1683                 break;
  1684             default:
  1685                 ALOG("Unknown special key code!");
  1686                 return;
  1689     if (doCommand) {
  1690         WidgetCommandEvent event(true, nsGkAtoms::onAppCommand, command, this);
  1691         InitEvent(event);
  1692         DispatchEvent(&event);
  1696 void
  1697 nsWindow::OnKeyEvent(AndroidGeckoEvent *ae)
  1699     nsRefPtr<nsWindow> kungFuDeathGrip(this);
  1700     RemoveIMEComposition();
  1701     uint32_t msg;
  1702     switch (ae->Action()) {
  1703     case AKEY_EVENT_ACTION_DOWN:
  1704         msg = NS_KEY_DOWN;
  1705         break;
  1706     case AKEY_EVENT_ACTION_UP:
  1707         msg = NS_KEY_UP;
  1708         break;
  1709     case AKEY_EVENT_ACTION_MULTIPLE:
  1710         // Keys with multiple action are handled in Java,
  1711         // and we should never see one here
  1712         MOZ_CRASH("Cannot handle key with multiple action");
  1713     default:
  1714         ALOG("Unknown key action event!");
  1715         return;
  1718     bool firePress = ae->Action() == AKEY_EVENT_ACTION_DOWN;
  1719     switch (ae->KeyCode()) {
  1720     case AKEYCODE_SHIFT_LEFT:
  1721     case AKEYCODE_SHIFT_RIGHT:
  1722     case AKEYCODE_ALT_LEFT:
  1723     case AKEYCODE_ALT_RIGHT:
  1724     case AKEYCODE_CTRL_LEFT:
  1725     case AKEYCODE_CTRL_RIGHT:
  1726         firePress = false;
  1727         break;
  1728     case AKEYCODE_BACK:
  1729     case AKEYCODE_MENU:
  1730     case AKEYCODE_SEARCH:
  1731         HandleSpecialKey(ae);
  1732         return;
  1735     nsEventStatus status;
  1736     WidgetKeyboardEvent event(true, msg, this);
  1737     ANPEvent pluginEvent;
  1738     InitKeyEvent(event, *ae, &pluginEvent);
  1739     DispatchEvent(&event, status);
  1741     if (Destroyed())
  1742         return;
  1743     if (!firePress || status == nsEventStatus_eConsumeNoDefault) {
  1744         return;
  1747     WidgetKeyboardEvent pressEvent(true, NS_KEY_PRESS, this);
  1748     InitKeyEvent(pressEvent, *ae, &pluginEvent);
  1749 #ifdef DEBUG_ANDROID_WIDGET
  1750     __android_log_print(ANDROID_LOG_INFO, "Gecko", "Dispatching key pressEvent with keyCode %d charCode %d shift %d alt %d sym/ctrl %d metamask %d", pressEvent.keyCode, pressEvent.charCode, pressEvent.IsShift(), pressEvent.IsAlt(), pressEvent.IsControl(), ae->MetaState());
  1751 #endif
  1752     DispatchEvent(&pressEvent);
  1755 #ifdef DEBUG_ANDROID_IME
  1756 #define ALOGIME(args...) ALOG(args)
  1757 #else
  1758 #define ALOGIME(args...) ((void)0)
  1759 #endif
  1761 static nscolor
  1762 ConvertAndroidColor(uint32_t argb)
  1764     return NS_RGBA((argb & 0x00ff0000) >> 16,
  1765                    (argb & 0x0000ff00) >> 8,
  1766                    (argb & 0x000000ff),
  1767                    (argb & 0xff000000) >> 24);
  1770 class AutoIMEMask {
  1771 private:
  1772     bool mOldMask, *mMask;
  1773 public:
  1774     AutoIMEMask(bool &mask) : mOldMask(mask), mMask(&mask) {
  1775         mask = true;
  1777     ~AutoIMEMask() {
  1778         *mMask = mOldMask;
  1780 };
  1782 /*
  1783     Remove the composition but leave the text content as-is
  1784 */
  1785 void
  1786 nsWindow::RemoveIMEComposition()
  1788     // Remove composition on Gecko side
  1789     if (!mIMEComposing)
  1790         return;
  1792     nsRefPtr<nsWindow> kungFuDeathGrip(this);
  1793     AutoIMEMask selMask(mIMEMaskSelectionUpdate);
  1794     AutoIMEMask textMask(mIMEMaskTextUpdate);
  1796     WidgetTextEvent textEvent(true, NS_TEXT_TEXT, this);
  1797     InitEvent(textEvent, nullptr);
  1798     textEvent.theText = mIMEComposingText;
  1799     DispatchEvent(&textEvent);
  1801     WidgetCompositionEvent event(true, NS_COMPOSITION_END, this);
  1802     InitEvent(event, nullptr);
  1803     DispatchEvent(&event);
  1806 void
  1807 nsWindow::OnIMEEvent(AndroidGeckoEvent *ae)
  1809     MOZ_ASSERT(!mIMEMaskTextUpdate);
  1810     MOZ_ASSERT(!mIMEMaskSelectionUpdate);
  1811     /*
  1812         Rules for managing IME between Gecko and Java:
  1814         * Gecko controls the text content, and Java shadows the Gecko text
  1815            through text updates
  1816         * Java controls the selection, and Gecko shadows the Java selection
  1817            through set selection events
  1818         * Java controls the composition, and Gecko shadows the Java
  1819            composition through update composition events
  1820     */
  1821     nsRefPtr<nsWindow> kungFuDeathGrip(this);
  1823     if (ae->Action() == AndroidGeckoEvent::IME_ACKNOWLEDGE_FOCUS) {
  1824         MOZ_ASSERT(mIMEMaskEventsCount > 0);
  1825         mIMEMaskEventsCount--;
  1826         if (!mIMEMaskEventsCount) {
  1827             // The focusing handshake sequence is complete, and Java is waiting
  1828             // on Gecko. Now we can notify Java of the newly focused content
  1829             mIMETextChanges.Clear();
  1830             mIMESelectionChanged = false;
  1831             // NotifyIMEOfTextChange also notifies selection
  1832             // Use 'INT32_MAX / 2' here because subsequent text changes might
  1833             // combine with this text change, and overflow might occur if
  1834             // we just use INT32_MAX
  1835             IMENotification notification(NOTIFY_IME_OF_TEXT_CHANGE);
  1836             notification.mTextChangeData.mOldEndOffset =
  1837                 notification.mTextChangeData.mNewEndOffset = INT32_MAX / 2;
  1838             NotifyIMEOfTextChange(notification);
  1839             FlushIMEChanges();
  1841         mozilla::widget::android::GeckoAppShell::NotifyIME(AndroidBridge::NOTIFY_IME_REPLY_EVENT);
  1842         return;
  1843     } else if (ae->Action() == AndroidGeckoEvent::IME_UPDATE_CONTEXT) {
  1844         mozilla::widget::android::GeckoAppShell::NotifyIMEContext(mInputContext.mIMEState.mEnabled,
  1845                                         mInputContext.mHTMLInputType,
  1846                                         mInputContext.mHTMLInputInputmode,
  1847                                         mInputContext.mActionHint);
  1848         mIMEUpdatingContext = false;
  1849         return;
  1851     if (mIMEMaskEventsCount > 0) {
  1852         // Still reply to events, but don't do anything else
  1853         if (ae->Action() == AndroidGeckoEvent::IME_SYNCHRONIZE ||
  1854             ae->Action() == AndroidGeckoEvent::IME_REPLACE_TEXT) {
  1855             mozilla::widget::android::GeckoAppShell::NotifyIME(AndroidBridge::NOTIFY_IME_REPLY_EVENT);
  1857         return;
  1859     switch (ae->Action()) {
  1860     case AndroidGeckoEvent::IME_FLUSH_CHANGES:
  1862             FlushIMEChanges();
  1864         break;
  1865     case AndroidGeckoEvent::IME_SYNCHRONIZE:
  1867             FlushIMEChanges();
  1868             mozilla::widget::android::GeckoAppShell::NotifyIME(AndroidBridge::NOTIFY_IME_REPLY_EVENT);
  1870         break;
  1871     case AndroidGeckoEvent::IME_REPLACE_TEXT:
  1873             /*
  1874                 Replace text in Gecko thread from ae->Start() to ae->End()
  1875                   with the string ae->Characters()
  1877                 Selection updates are masked so the result of our temporary
  1878                   selection event is not passed on to Java
  1880                 Text updates are passed on, so the Java text can shadow the
  1881                   Gecko text
  1882             */
  1883             AutoIMEMask selMask(mIMEMaskSelectionUpdate);
  1884             RemoveIMEComposition();
  1886                 WidgetSelectionEvent event(true, NS_SELECTION_SET, this);
  1887                 InitEvent(event, nullptr);
  1888                 event.mOffset = uint32_t(ae->Start());
  1889                 event.mLength = uint32_t(ae->End() - ae->Start());
  1890                 event.mExpandToClusterBoundary = false;
  1891                 DispatchEvent(&event);
  1894             if (!mIMEKeyEvents.IsEmpty()) {
  1895                 for (uint32_t i = 0; i < mIMEKeyEvents.Length(); i++) {
  1896                     OnKeyEvent(&mIMEKeyEvents[i]);
  1898                 mIMEKeyEvents.Clear();
  1899                 FlushIMEChanges();
  1900                 mozilla::widget::android::GeckoAppShell::NotifyIME(AndroidBridge::NOTIFY_IME_REPLY_EVENT);
  1901                 break;
  1905                 WidgetCompositionEvent event(true, NS_COMPOSITION_START, this);
  1906                 InitEvent(event, nullptr);
  1907                 DispatchEvent(&event);
  1910                 WidgetCompositionEvent event(true, NS_COMPOSITION_UPDATE, this);
  1911                 InitEvent(event, nullptr);
  1912                 event.data = ae->Characters();
  1913                 DispatchEvent(&event);
  1916                 WidgetTextEvent event(true, NS_TEXT_TEXT, this);
  1917                 InitEvent(event, nullptr);
  1918                 event.theText = ae->Characters();
  1919                 DispatchEvent(&event);
  1922                 WidgetCompositionEvent event(true, NS_COMPOSITION_END, this);
  1923                 InitEvent(event, nullptr);
  1924                 event.data = ae->Characters();
  1925                 DispatchEvent(&event);
  1927             FlushIMEChanges();
  1928             mozilla::widget::android::GeckoAppShell::NotifyIME(AndroidBridge::NOTIFY_IME_REPLY_EVENT);
  1930         break;
  1931     case AndroidGeckoEvent::IME_SET_SELECTION:
  1933             /*
  1934                 Set Gecko selection to ae->Start() to ae->End()
  1936                 Selection updates are masked to prevent Java from being
  1937                   notified of the new selection
  1938             */
  1939             AutoIMEMask selMask(mIMEMaskSelectionUpdate);
  1940             RemoveIMEComposition();
  1941             WidgetSelectionEvent selEvent(true, NS_SELECTION_SET, this);
  1942             InitEvent(selEvent, nullptr);
  1944             int32_t start = ae->Start(), end = ae->End();
  1946             if (start < 0 || end < 0) {
  1947                 WidgetQueryContentEvent event(true, NS_QUERY_SELECTED_TEXT,
  1948                                               this);
  1949                 InitEvent(event, nullptr);
  1950                 DispatchEvent(&event);
  1951                 MOZ_ASSERT(event.mSucceeded && !event.mWasAsync);
  1953                 if (start < 0)
  1954                     start = int32_t(event.GetSelectionStart());
  1955                 if (end < 0)
  1956                     end = int32_t(event.GetSelectionEnd());
  1959             selEvent.mOffset = std::min(start, end);
  1960             selEvent.mLength = std::max(start, end) - selEvent.mOffset;
  1961             selEvent.mReversed = start > end;
  1962             selEvent.mExpandToClusterBoundary = false;
  1964             DispatchEvent(&selEvent);
  1966             // Notify SelectionHandler of final caret position
  1967             // Required after IME hide via 'Back' button
  1968             AndroidGeckoEvent* broadcastEvent = AndroidGeckoEvent::MakeBroadcastEvent(
  1969                 NS_LITERAL_CSTRING("TextSelection:UpdateCaretPos"),
  1970                 NS_LITERAL_CSTRING(""));
  1971             nsAppShell::gAppShell->PostEvent(broadcastEvent);
  1973         break;
  1974     case AndroidGeckoEvent::IME_ADD_COMPOSITION_RANGE:
  1976             TextRange range;
  1977             range.mStartOffset = ae->Start();
  1978             range.mEndOffset = ae->End();
  1979             range.mRangeType = ae->RangeType();
  1980             range.mRangeStyle.mDefinedStyles = ae->RangeStyles();
  1981             range.mRangeStyle.mLineStyle = ae->RangeLineStyle();
  1982             range.mRangeStyle.mIsBoldLine = ae->RangeBoldLine();
  1983             range.mRangeStyle.mForegroundColor =
  1984                     ConvertAndroidColor(uint32_t(ae->RangeForeColor()));
  1985             range.mRangeStyle.mBackgroundColor =
  1986                     ConvertAndroidColor(uint32_t(ae->RangeBackColor()));
  1987             range.mRangeStyle.mUnderlineColor =
  1988                     ConvertAndroidColor(uint32_t(ae->RangeLineColor()));
  1989             mIMERanges->AppendElement(range);
  1991         break;
  1992     case AndroidGeckoEvent::IME_UPDATE_COMPOSITION:
  1994             /*
  1995                 Update the composition from ae->Start() to ae->End() using
  1996                   information from added ranges. This is only used for
  1997                   visual indication and does not affect the text content.
  1998                   Only the offsets are specified and not the text content
  1999                   to eliminate the possibility of this event altering the
  2000                   text content unintentionally.
  2002                 Selection and text updates are masked so the result of
  2003                   temporary events are not passed on to Java
  2004             */
  2005             AutoIMEMask selMask(mIMEMaskSelectionUpdate);
  2006             AutoIMEMask textMask(mIMEMaskTextUpdate);
  2007             RemoveIMEComposition();
  2009             WidgetTextEvent event(true, NS_TEXT_TEXT, this);
  2010             InitEvent(event, nullptr);
  2012             event.mRanges = new TextRangeArray();
  2013             mIMERanges.swap(event.mRanges);
  2016                 WidgetSelectionEvent event(true, NS_SELECTION_SET, this);
  2017                 InitEvent(event, nullptr);
  2018                 event.mOffset = uint32_t(ae->Start());
  2019                 event.mLength = uint32_t(ae->End() - ae->Start());
  2020                 event.mExpandToClusterBoundary = false;
  2021                 DispatchEvent(&event);
  2024                 WidgetQueryContentEvent queryEvent(true,
  2025                                                    NS_QUERY_SELECTED_TEXT,
  2026                                                    this);
  2027                 InitEvent(queryEvent, nullptr);
  2028                 DispatchEvent(&queryEvent);
  2029                 MOZ_ASSERT(queryEvent.mSucceeded && !queryEvent.mWasAsync);
  2030                 event.theText = queryEvent.mReply.mString;
  2033                 WidgetCompositionEvent event(true, NS_COMPOSITION_START, this);
  2034                 InitEvent(event, nullptr);
  2035                 DispatchEvent(&event);
  2038                 WidgetCompositionEvent compositionUpdate(true,
  2039                                                          NS_COMPOSITION_UPDATE,
  2040                                                          this);
  2041                 InitEvent(compositionUpdate, nullptr);
  2042                 compositionUpdate.data = event.theText;
  2043                 DispatchEvent(&compositionUpdate);
  2046 #ifdef DEBUG_ANDROID_IME
  2047             const NS_ConvertUTF16toUTF8 theText8(event.theText);
  2048             const char* text = theText8.get();
  2049             ALOGIME("IME: IME_SET_TEXT: text=\"%s\", length=%u, range=%u",
  2050                     text, event.theText.Length(), event.mRanges->Length());
  2051 #endif // DEBUG_ANDROID_IME
  2053             DispatchEvent(&event);
  2055             // Notify SelectionHandler of final caret position
  2056             // Required in cases of keyboards providing autoCorrections
  2057             AndroidGeckoEvent* broadcastEvent = AndroidGeckoEvent::MakeBroadcastEvent(
  2058                 NS_LITERAL_CSTRING("TextSelection:UpdateCaretPos"),
  2059                 NS_LITERAL_CSTRING(""));
  2060             nsAppShell::gAppShell->PostEvent(broadcastEvent);
  2062         break;
  2063     case AndroidGeckoEvent::IME_REMOVE_COMPOSITION:
  2065             /*
  2066              *  Remove any previous composition.  This is only used for
  2067              *    visual indication and does not affect the text content.
  2069              *  Selection and text updates are masked so the result of
  2070              *    temporary events are not passed on to Java
  2071              */
  2072             AutoIMEMask selMask(mIMEMaskSelectionUpdate);
  2073             AutoIMEMask textMask(mIMEMaskTextUpdate);
  2074             RemoveIMEComposition();
  2075             mIMERanges->Clear();
  2077         break;
  2081 nsWindow *
  2082 nsWindow::FindWindowForPoint(const nsIntPoint& pt)
  2084     if (!mBounds.Contains(pt))
  2085         return nullptr;
  2087     // children mBounds are relative to their parent
  2088     nsIntPoint childPoint(pt.x - mBounds.x, pt.y - mBounds.y);
  2090     for (uint32_t i = 0; i < mChildren.Length(); ++i) {
  2091         if (mChildren[i]->mBounds.Contains(childPoint))
  2092             return mChildren[i]->FindWindowForPoint(childPoint);
  2095     return this;
  2098 void
  2099 nsWindow::UserActivity()
  2101   if (!mIdleService) {
  2102     mIdleService = do_GetService("@mozilla.org/widget/idleservice;1");
  2105   if (mIdleService) {
  2106     mIdleService->ResetIdleTimeOut(0);
  2110 NS_IMETHODIMP
  2111 nsWindow::NotifyIME(const IMENotification& aIMENotification)
  2113     switch (aIMENotification.mMessage) {
  2114         case REQUEST_TO_COMMIT_COMPOSITION:
  2115             //ALOGIME("IME: REQUEST_TO_COMMIT_COMPOSITION: s=%d", aState);
  2116             RemoveIMEComposition();
  2117             mozilla::widget::android::GeckoAppShell::NotifyIME(REQUEST_TO_COMMIT_COMPOSITION);
  2118             return NS_OK;
  2119         case REQUEST_TO_CANCEL_COMPOSITION:
  2120             ALOGIME("IME: REQUEST_TO_CANCEL_COMPOSITION");
  2122             // Cancel composition on Gecko side
  2123             if (mIMEComposing) {
  2124                 nsRefPtr<nsWindow> kungFuDeathGrip(this);
  2126                 WidgetCompositionEvent updateEvent(true, NS_COMPOSITION_UPDATE,
  2127                                                    this);
  2128                 InitEvent(updateEvent, nullptr);
  2129                 DispatchEvent(&updateEvent);
  2131                 WidgetTextEvent textEvent(true, NS_TEXT_TEXT, this);
  2132                 InitEvent(textEvent, nullptr);
  2133                 DispatchEvent(&textEvent);
  2135                 WidgetCompositionEvent compEvent(true, NS_COMPOSITION_END,
  2136                                                  this);
  2137                 InitEvent(compEvent, nullptr);
  2138                 DispatchEvent(&compEvent);
  2141             mozilla::widget::android::GeckoAppShell::NotifyIME(REQUEST_TO_CANCEL_COMPOSITION);
  2142             return NS_OK;
  2143         case NOTIFY_IME_OF_FOCUS:
  2144             ALOGIME("IME: NOTIFY_IME_OF_FOCUS");
  2145             mozilla::widget::android::GeckoAppShell::NotifyIME(NOTIFY_IME_OF_FOCUS);
  2146             return NS_OK;
  2147         case NOTIFY_IME_OF_BLUR:
  2148             ALOGIME("IME: NOTIFY_IME_OF_BLUR");
  2150             // Mask events because we lost focus. On the next focus event,
  2151             // Gecko will notify Java, and Java will send an acknowledge focus
  2152             // event back to Gecko. That is where we unmask event handling
  2153             mIMEMaskEventsCount++;
  2154             mIMEComposing = false;
  2155             mIMEComposingText.Truncate();
  2157             mozilla::widget::android::GeckoAppShell::NotifyIME(NOTIFY_IME_OF_BLUR);
  2158             return NS_OK;
  2159         case NOTIFY_IME_OF_SELECTION_CHANGE:
  2160             if (mIMEMaskSelectionUpdate) {
  2161                 return NS_OK;
  2164             ALOGIME("IME: NOTIFY_IME_OF_SELECTION_CHANGE");
  2166             PostFlushIMEChanges();
  2167             mIMESelectionChanged = true;
  2168             return NS_OK;
  2169         case NOTIFY_IME_OF_TEXT_CHANGE:
  2170             return NotifyIMEOfTextChange(aIMENotification);
  2171         default:
  2172             return NS_ERROR_NOT_IMPLEMENTED;
  2176 NS_IMETHODIMP_(void)
  2177 nsWindow::SetInputContext(const InputContext& aContext,
  2178                           const InputContextAction& aAction)
  2180     nsWindow *top = TopWindow();
  2181     if (top && top->mFocus && this != top->mFocus) {
  2182         // We are using an IME event later to notify Java, and the IME event
  2183         // will be processed by the focused window. Therefore, to ensure the
  2184         // IME event uses the correct mInputContext, we need to let the focused
  2185         // window process SetInputContext
  2186         top->mFocus->SetInputContext(aContext, aAction);
  2187         return;
  2190     ALOGIME("IME: SetInputContext: s=0x%X, 0x%X, action=0x%X, 0x%X",
  2191             aContext.mIMEState.mEnabled, aContext.mIMEState.mOpen,
  2192             aAction.mCause, aAction.mFocusChange);
  2194     mInputContext = aContext;
  2196     // Ensure that opening the virtual keyboard is allowed for this specific
  2197     // InputContext depending on the content.ime.strict.policy pref
  2198     if (aContext.mIMEState.mEnabled != IMEState::DISABLED && 
  2199         aContext.mIMEState.mEnabled != IMEState::PLUGIN &&
  2200         Preferences::GetBool("content.ime.strict_policy", false) &&
  2201         !aAction.ContentGotFocusByTrustedCause() &&
  2202         !aAction.UserMightRequestOpenVKB()) {
  2203         return;
  2206     IMEState::Enabled enabled = aContext.mIMEState.mEnabled;
  2208     // Only show the virtual keyboard for plugins if mOpen is set appropriately.
  2209     // This avoids showing it whenever a plugin is focused. Bug 747492
  2210     if (aContext.mIMEState.mEnabled == IMEState::PLUGIN &&
  2211         aContext.mIMEState.mOpen != IMEState::OPEN) {
  2212         enabled = IMEState::DISABLED;
  2215     mInputContext.mIMEState.mEnabled = enabled;
  2217     if (enabled == IMEState::ENABLED && aAction.UserMightRequestOpenVKB()) {
  2218         // Don't reset keyboard when we should simply open the vkb
  2219         mozilla::widget::android::GeckoAppShell::NotifyIME(AndroidBridge::NOTIFY_IME_OPEN_VKB);
  2220         return;
  2223     if (mIMEUpdatingContext) {
  2224         return;
  2226     AndroidGeckoEvent *event = AndroidGeckoEvent::MakeIMEEvent(
  2227             AndroidGeckoEvent::IME_UPDATE_CONTEXT);
  2228     nsAppShell::gAppShell->PostEvent(event);
  2229     mIMEUpdatingContext = true;
  2232 NS_IMETHODIMP_(InputContext)
  2233 nsWindow::GetInputContext()
  2235     nsWindow *top = TopWindow();
  2236     if (top && top->mFocus && this != top->mFocus) {
  2237         // We let the focused window process SetInputContext,
  2238         // so we should let it process GetInputContext as well.
  2239         return top->mFocus->GetInputContext();
  2241     InputContext context = mInputContext;
  2242     context.mIMEState.mOpen = IMEState::OPEN_STATE_NOT_SUPPORTED;
  2243     // We assume that there is only one context per process on Android
  2244     context.mNativeIMEContext = nullptr;
  2245     return context;
  2248 void
  2249 nsWindow::PostFlushIMEChanges()
  2251     if (!mIMETextChanges.IsEmpty() || mIMESelectionChanged) {
  2252         // Already posted
  2253         return;
  2255     AndroidGeckoEvent *event = AndroidGeckoEvent::MakeIMEEvent(
  2256             AndroidGeckoEvent::IME_FLUSH_CHANGES);
  2257     nsAppShell::gAppShell->PostEvent(event);
  2260 void
  2261 nsWindow::FlushIMEChanges()
  2263     nsRefPtr<nsWindow> kungFuDeathGrip(this);
  2264     for (uint32_t i = 0; i < mIMETextChanges.Length(); i++) {
  2265         IMEChange &change = mIMETextChanges[i];
  2267         WidgetQueryContentEvent event(true, NS_QUERY_TEXT_CONTENT, this);
  2268         InitEvent(event, nullptr);
  2269         event.InitForQueryTextContent(change.mStart,
  2270                                       change.mNewEnd - change.mStart);
  2271         DispatchEvent(&event);
  2272         if (!event.mSucceeded)
  2273             return;
  2275         mozilla::widget::android::GeckoAppShell::NotifyIMEChange(event.mReply.mString,
  2276                                        change.mStart,
  2277                                        change.mOldEnd,
  2278                                        change.mNewEnd);
  2280     mIMETextChanges.Clear();
  2282     if (mIMESelectionChanged) {
  2283         WidgetQueryContentEvent event(true, NS_QUERY_SELECTED_TEXT, this);
  2284         InitEvent(event, nullptr);
  2286         DispatchEvent(&event);
  2287         if (!event.mSucceeded)
  2288             return;
  2290         mozilla::widget::android::GeckoAppShell::NotifyIMEChange(EmptyString(),
  2291                              (int32_t) event.GetSelectionStart(),
  2292                              (int32_t) event.GetSelectionEnd(), -1);
  2293         mIMESelectionChanged = false;
  2297 nsresult
  2298 nsWindow::NotifyIMEOfTextChange(const IMENotification& aIMENotification)
  2300     MOZ_ASSERT(aIMENotification.mMessage == NOTIFY_IME_OF_TEXT_CHANGE,
  2301                "NotifyIMEOfTextChange() is called with invaild notification");
  2303     if (mIMEMaskTextUpdate)
  2304         return NS_OK;
  2306     ALOGIME("IME: NotifyIMEOfTextChange: s=%d, oe=%d, ne=%d",
  2307             aIMENotification.mTextChangeData.mStartOffset,
  2308             aIMENotification.mTextChangeData.mOldEndOffset,
  2309             aIMENotification.mTextChangeData.mNewEndOffset);
  2311     /* Make sure Java's selection is up-to-date */
  2312     mIMESelectionChanged = false;
  2313     NotifyIME(NOTIFY_IME_OF_SELECTION_CHANGE);
  2314     PostFlushIMEChanges();
  2316     mIMETextChanges.AppendElement(IMEChange(aIMENotification));
  2317     // Now that we added a new range we need to go back and
  2318     // update all the ranges before that.
  2319     // Ranges that have offsets which follow this new range
  2320     // need to be updated to reflect new offsets
  2321     int32_t delta = aIMENotification.mTextChangeData.AdditionalLength();
  2322     for (int32_t i = mIMETextChanges.Length() - 2; i >= 0; i--) {
  2323         IMEChange &previousChange = mIMETextChanges[i];
  2324         if (previousChange.mStart >
  2325                 static_cast<int32_t>(
  2326                     aIMENotification.mTextChangeData.mOldEndOffset)) {
  2327             previousChange.mStart += delta;
  2328             previousChange.mOldEnd += delta;
  2329             previousChange.mNewEnd += delta;
  2333     // Now go through all ranges to merge any ranges that are connected
  2334     // srcIndex is the index of the range to merge from
  2335     // dstIndex is the index of the range to potentially merge into
  2336     int32_t srcIndex = mIMETextChanges.Length() - 1;
  2337     int32_t dstIndex = srcIndex;
  2339     while (--dstIndex >= 0) {
  2340         IMEChange &src = mIMETextChanges[srcIndex];
  2341         IMEChange &dst = mIMETextChanges[dstIndex];
  2342         // When merging a more recent change into an older
  2343         // change, we need to compare recent change's (start, oldEnd)
  2344         // range to the older change's (start, newEnd)
  2345         if (src.mOldEnd < dst.mStart || dst.mNewEnd < src.mStart) {
  2346             // No overlap between ranges
  2347             continue;
  2349         // When merging two ranges, there are generally four posibilities:
  2350         // [----(----]----), (----[----]----),
  2351         // [----(----)----], (----[----)----]
  2352         // where [----] is the first range and (----) is the second range
  2353         // As seen above, the start of the merged range is always the lesser
  2354         // of the two start offsets. OldEnd and NewEnd then need to be
  2355         // adjusted separately depending on the case. In any case, the change
  2356         // in text length of the merged range should be the sum of text length
  2357         // changes of the two original ranges, i.e.,
  2358         // newNewEnd - newOldEnd == newEnd1 - oldEnd1 + newEnd2 - oldEnd2
  2359         dst.mStart = std::min(dst.mStart, src.mStart);
  2360         if (src.mOldEnd < dst.mNewEnd) {
  2361             // New range overlaps or is within previous range; merge
  2362             dst.mNewEnd += src.mNewEnd - src.mOldEnd;
  2363         } else { // src.mOldEnd >= dst.mNewEnd
  2364             // New range overlaps previous range; merge
  2365             dst.mOldEnd += src.mOldEnd - dst.mNewEnd;
  2366             dst.mNewEnd = src.mNewEnd;
  2368         // src merged to dst; delete src.
  2369         mIMETextChanges.RemoveElementAt(srcIndex);
  2370         // Any ranges that we skip over between src and dst are not mergeable
  2371         // so we can safely continue the merge starting at dst
  2372         srcIndex = dstIndex;
  2374     return NS_OK;
  2377 nsIMEUpdatePreference
  2378 nsWindow::GetIMEUpdatePreference()
  2380     return nsIMEUpdatePreference(
  2381         nsIMEUpdatePreference::NOTIFY_SELECTION_CHANGE |
  2382         nsIMEUpdatePreference::NOTIFY_TEXT_CHANGE);
  2385 void
  2386 nsWindow::DrawWindowUnderlay(LayerManagerComposite* aManager, nsIntRect aRect)
  2388     JNIEnv *env = GetJNIForThread();
  2390     AutoLocalJNIFrame jniFrame(env);
  2392     mozilla::widget::android::GeckoLayerClient* client = AndroidBridge::Bridge()->GetLayerClient();
  2393     if (!client || client->isNull()) {
  2394         ALOG_BRIDGE("Exceptional Exit: %s", __PRETTY_FUNCTION__);
  2395         return;
  2398     jobject frameObj = client->CreateFrame();
  2399     if (!frameObj) {
  2400         NS_WARNING("Warning: unable to obtain a LayerRenderer frame; aborting window underlay draw");
  2401         return;
  2404     mLayerRendererFrame.Init(env, frameObj);
  2405     if (!WidgetPaintsBackground()) {
  2406         return;
  2409     gl::GLContext* gl = static_cast<CompositorOGL*>(aManager->GetCompositor())->gl();
  2410     gl::ScopedGLState scopedScissorTestState(gl, LOCAL_GL_SCISSOR_TEST);
  2411     gl::ScopedScissorRect scopedScissorRectState(gl);
  2413     client->ActivateProgram();
  2414     if (!mLayerRendererFrame.BeginDrawing(&jniFrame)) return;
  2415     if (!mLayerRendererFrame.DrawBackground(&jniFrame)) return;
  2416     client->DeactivateProgram(); // redundant, but in case somebody adds code after this...
  2419 void
  2420 nsWindow::DrawWindowOverlay(LayerManagerComposite* aManager, nsIntRect aRect)
  2422     PROFILER_LABEL("nsWindow", "DrawWindowOverlay");
  2423     JNIEnv *env = GetJNIForThread();
  2425     AutoLocalJNIFrame jniFrame(env);
  2427     if (mLayerRendererFrame.isNull()) {
  2428         NS_WARNING("Warning: do not have a LayerRenderer frame; aborting window overlay draw");
  2429         return;
  2432     mozilla::widget::android::GeckoLayerClient* client = AndroidBridge::Bridge()->GetLayerClient();
  2434     gl::GLContext* gl = static_cast<CompositorOGL*>(aManager->GetCompositor())->gl();
  2435     gl::ScopedGLState scopedScissorTestState(gl, LOCAL_GL_SCISSOR_TEST);
  2436     gl::ScopedScissorRect scopedScissorRectState(gl);
  2438     client->ActivateProgram();
  2439     if (!mLayerRendererFrame.DrawForeground(&jniFrame)) return;
  2440     if (!mLayerRendererFrame.EndDrawing(&jniFrame)) return;
  2441     client->DeactivateProgram();
  2442     mLayerRendererFrame.Dispose(env);
  2445 // off-main-thread compositor fields and functions
  2447 StaticRefPtr<mozilla::layers::APZCTreeManager> nsWindow::sApzcTreeManager;
  2448 StaticRefPtr<mozilla::layers::LayerManager> nsWindow::sLayerManager;
  2449 StaticRefPtr<mozilla::layers::CompositorParent> nsWindow::sCompositorParent;
  2450 StaticRefPtr<mozilla::layers::CompositorChild> nsWindow::sCompositorChild;
  2451 bool nsWindow::sCompositorPaused = true;
  2453 void
  2454 nsWindow::SetCompositor(mozilla::layers::LayerManager* aLayerManager,
  2455                         mozilla::layers::CompositorParent* aCompositorParent,
  2456                         mozilla::layers::CompositorChild* aCompositorChild)
  2458     sLayerManager = aLayerManager;
  2459     sCompositorParent = aCompositorParent;
  2460     sCompositorChild = aCompositorChild;
  2463 void
  2464 nsWindow::ScheduleComposite()
  2466     if (sCompositorParent) {
  2467         sCompositorParent->ScheduleRenderOnCompositorThread();
  2471 void
  2472 nsWindow::ScheduleResumeComposition(int width, int height)
  2474     if (sCompositorParent && sCompositorParent->ScheduleResumeOnCompositorThread(width, height)) {
  2475         sCompositorPaused = false;
  2479 void
  2480 nsWindow::ForceIsFirstPaint()
  2482     if (sCompositorParent) {
  2483         sCompositorParent->ForceIsFirstPaint();
  2487 float
  2488 nsWindow::ComputeRenderIntegrity()
  2490     if (sCompositorParent) {
  2491         return sCompositorParent->ComputeRenderIntegrity();
  2494     return 1.f;
  2497 bool
  2498 nsWindow::WidgetPaintsBackground()
  2500     static bool sWidgetPaintsBackground = true;
  2501     static bool sWidgetPaintsBackgroundPrefCached = false;
  2503     if (!sWidgetPaintsBackgroundPrefCached) {
  2504         sWidgetPaintsBackgroundPrefCached = true;
  2505         mozilla::Preferences::AddBoolVarCache(&sWidgetPaintsBackground,
  2506                                               "android.widget_paints_background",
  2507                                               true);
  2510     return sWidgetPaintsBackground;
  2513 bool
  2514 nsWindow::NeedsPaint()
  2516   if (sCompositorPaused || FindTopLevel() != nsWindow::TopWindow() || !GetLayerManager(nullptr)) {
  2517     return false;
  2519   return nsIWidget::NeedsPaint();
  2522 CompositorParent*
  2523 nsWindow::NewCompositorParent(int aSurfaceWidth, int aSurfaceHeight)
  2525     return new CompositorParent(this, true, aSurfaceWidth, aSurfaceHeight);
  2528 mozilla::layers::APZCTreeManager*
  2529 nsWindow::GetAPZCTreeManager()
  2531     if (!sApzcTreeManager) {
  2532         CompositorParent* compositor = sCompositorParent;
  2533         if (!compositor) {
  2534             return nullptr;
  2536         uint64_t rootLayerTreeId = compositor->RootLayerTreeId();
  2537         CompositorParent::SetControllerForLayerTree(rootLayerTreeId, AndroidBridge::Bridge());
  2538         sApzcTreeManager = CompositorParent::GetAPZCTreeManager(rootLayerTreeId);
  2540     return sApzcTreeManager;
  2543 uint64_t
  2544 nsWindow::RootLayerTreeId()
  2546     MOZ_ASSERT(sCompositorParent);
  2547     return sCompositorParent->RootLayerTreeId();

mercurial