Thu, 22 Jan 2015 13:21:57 +0100
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;
1001 }
1003 case mozilla::layers::LayersBackend::LAYERS_CLIENT: {
1004 mWidgetListener->PaintWindow(this, region);
1005 break;
1006 }
1008 default:
1009 NS_ERROR("Invalid layer manager");
1010 }
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;
1017 }
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;
1028 }
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]);
1038 }
1039 }
1041 if (targetSurface)
1042 targetSurface->SetDeviceOffset(offset);
1044 return true;
1045 }
1047 void
1048 nsWindow::OnDraw(AndroidGeckoEvent *ae)
1049 {
1050 if (!IsTopLevel()) {
1051 ALOG("##### redraw for window %p, which is not a toplevel window -- sending to toplevel!", (void*) this);
1052 DumpWindows();
1053 return;
1054 }
1056 if (!mIsVisible) {
1057 ALOG("##### redraw for window %p, which is not visible -- ignoring!", (void*) this);
1058 DumpWindows();
1059 return;
1060 }
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;
1069 }
1071 int bytesPerPixel = 2;
1072 gfxImageFormat format = gfxImageFormat::RGB16_565;
1073 if (AndroidBridge::Bridge()->GetScreenDepth() == 24) {
1074 bytesPerPixel = 4;
1075 format = gfxImageFormat::RGB24;
1076 }
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());
1089 }
1090 layers::renderTraceEventEnd("Widget draw to", "434646");
1091 }
1093 void
1094 nsWindow::OnSizeChanged(const gfxIntSize& aSize)
1095 {
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);
1103 }
1104 }
1106 void
1107 nsWindow::InitEvent(WidgetGUIEvent& event, nsIntPoint* aPoint)
1108 {
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;
1115 }
1117 event.time = PR_Now() / 1000;
1118 }
1120 gfxIntSize
1121 nsWindow::GetAndroidScreenBounds()
1122 {
1123 if (XRE_GetProcessType() == GeckoProcessType_Content) {
1124 return ContentChild::GetSingleton()->GetScreenSize();
1125 }
1126 return gAndroidScreenBounds;
1127 }
1129 void *
1130 nsWindow::GetNativeData(uint32_t aDataType)
1131 {
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;
1139 }
1141 return nullptr;
1142 }
1144 void
1145 nsWindow::OnMouseEvent(AndroidGeckoEvent *ae)
1146 {
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;
1153 }
1155 // XXX add the double-click handling logic here
1156 DispatchEvent(&event);
1157 }
1159 bool nsWindow::OnMultitouchEvent(AndroidGeckoEvent *ae)
1160 {
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);
1187 }
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;
1198 }
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;
1209 }
1210 }
1211 sLastWasDownEvent = isDownEvent;
1213 return preventDefaultActions;
1214 }
1216 void
1217 nsWindow::OnNativeGestureEvent(AndroidGeckoEvent *ae)
1218 {
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;
1241 }
1243 nsRefPtr<nsWindow> kungFuDeathGrip(this);
1244 DispatchGestureEvent(msg, 0, delta, pt, ae->Time());
1245 }
1247 void
1248 nsWindow::DispatchGestureEvent(uint32_t msg, uint32_t direction, double delta,
1249 const nsIntPoint &refPoint, uint64_t time)
1250 {
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);
1260 }
1263 void
1264 nsWindow::DispatchMotionEvent(WidgetInputEvent &event, AndroidGeckoEvent *ae,
1265 const nsIntPoint &refPoint)
1266 {
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);
1277 }
1279 static unsigned int ConvertAndroidKeyCodeToDOMKeyCode(int androidKeyCode)
1280 {
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;
1285 }
1287 if (androidKeyCode >= AKEYCODE_0 &&
1288 androidKeyCode <= AKEYCODE_9) {
1289 return androidKeyCode - AKEYCODE_0 + NS_VK_0;
1290 }
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;
1396 }
1397 }
1399 static KeyNameIndex
1400 ConvertAndroidKeyCodeToKeyNameIndex(AndroidGeckoEvent& aAndroidGeckoEvent)
1401 {
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;
1406 }
1408 if (keyCode >= AKEYCODE_0 && keyCode <= AKEYCODE_9) {
1409 return KEY_NAME_INDEX_USE_STRING;
1410 }
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;
1548 }
1549 }
1551 static void InitPluginEvent(ANPEvent* pluginEvent, ANPKeyActions keyAction,
1552 AndroidGeckoEvent& key)
1553 {
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();
1571 }
1573 void
1574 nsWindow::InitKeyEvent(WidgetKeyboardEvent& event, AndroidGeckoEvent& key,
1575 ANPEvent* pluginEvent)
1576 {
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);
1582 }
1583 }
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();
1591 }
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);
1600 }
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;
1613 }
1615 event.modifiers = key.DOMModifiers();
1616 if (gMenu) {
1617 event.modifiers |= MODIFIER_CONTROL;
1618 }
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);
1628 }
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;
1638 }
1640 void
1641 nsWindow::HandleSpecialKey(AndroidGeckoEvent *ae)
1642 {
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;
1656 }
1657 break;
1658 case AKEYCODE_MENU:
1659 gMenu = true;
1660 gMenuConsumed = isLongPress;
1661 break;
1662 }
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;
1672 }
1673 case AKEYCODE_MENU:
1674 gMenu = false;
1675 if (!gMenuConsumed) {
1676 command = nsGkAtoms::Menu;
1677 doCommand = true;
1678 }
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;
1687 }
1688 }
1689 if (doCommand) {
1690 WidgetCommandEvent event(true, nsGkAtoms::onAppCommand, command, this);
1691 InitEvent(event);
1692 DispatchEvent(&event);
1693 }
1694 }
1696 void
1697 nsWindow::OnKeyEvent(AndroidGeckoEvent *ae)
1698 {
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;
1716 }
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;
1733 }
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;
1745 }
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);
1753 }
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)
1763 {
1764 return NS_RGBA((argb & 0x00ff0000) >> 16,
1765 (argb & 0x0000ff00) >> 8,
1766 (argb & 0x000000ff),
1767 (argb & 0xff000000) >> 24);
1768 }
1770 class AutoIMEMask {
1771 private:
1772 bool mOldMask, *mMask;
1773 public:
1774 AutoIMEMask(bool &mask) : mOldMask(mask), mMask(&mask) {
1775 mask = true;
1776 }
1777 ~AutoIMEMask() {
1778 *mMask = mOldMask;
1779 }
1780 };
1782 /*
1783 Remove the composition but leave the text content as-is
1784 */
1785 void
1786 nsWindow::RemoveIMEComposition()
1787 {
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);
1804 }
1806 void
1807 nsWindow::OnIMEEvent(AndroidGeckoEvent *ae)
1808 {
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();
1840 }
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;
1850 }
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);
1856 }
1857 return;
1858 }
1859 switch (ae->Action()) {
1860 case AndroidGeckoEvent::IME_FLUSH_CHANGES:
1861 {
1862 FlushIMEChanges();
1863 }
1864 break;
1865 case AndroidGeckoEvent::IME_SYNCHRONIZE:
1866 {
1867 FlushIMEChanges();
1868 mozilla::widget::android::GeckoAppShell::NotifyIME(AndroidBridge::NOTIFY_IME_REPLY_EVENT);
1869 }
1870 break;
1871 case AndroidGeckoEvent::IME_REPLACE_TEXT:
1872 {
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();
1885 {
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);
1892 }
1894 if (!mIMEKeyEvents.IsEmpty()) {
1895 for (uint32_t i = 0; i < mIMEKeyEvents.Length(); i++) {
1896 OnKeyEvent(&mIMEKeyEvents[i]);
1897 }
1898 mIMEKeyEvents.Clear();
1899 FlushIMEChanges();
1900 mozilla::widget::android::GeckoAppShell::NotifyIME(AndroidBridge::NOTIFY_IME_REPLY_EVENT);
1901 break;
1902 }
1904 {
1905 WidgetCompositionEvent event(true, NS_COMPOSITION_START, this);
1906 InitEvent(event, nullptr);
1907 DispatchEvent(&event);
1908 }
1909 {
1910 WidgetCompositionEvent event(true, NS_COMPOSITION_UPDATE, this);
1911 InitEvent(event, nullptr);
1912 event.data = ae->Characters();
1913 DispatchEvent(&event);
1914 }
1915 {
1916 WidgetTextEvent event(true, NS_TEXT_TEXT, this);
1917 InitEvent(event, nullptr);
1918 event.theText = ae->Characters();
1919 DispatchEvent(&event);
1920 }
1921 {
1922 WidgetCompositionEvent event(true, NS_COMPOSITION_END, this);
1923 InitEvent(event, nullptr);
1924 event.data = ae->Characters();
1925 DispatchEvent(&event);
1926 }
1927 FlushIMEChanges();
1928 mozilla::widget::android::GeckoAppShell::NotifyIME(AndroidBridge::NOTIFY_IME_REPLY_EVENT);
1929 }
1930 break;
1931 case AndroidGeckoEvent::IME_SET_SELECTION:
1932 {
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());
1957 }
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);
1972 }
1973 break;
1974 case AndroidGeckoEvent::IME_ADD_COMPOSITION_RANGE:
1975 {
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);
1990 }
1991 break;
1992 case AndroidGeckoEvent::IME_UPDATE_COMPOSITION:
1993 {
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);
2015 {
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);
2022 }
2023 {
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;
2031 }
2032 {
2033 WidgetCompositionEvent event(true, NS_COMPOSITION_START, this);
2034 InitEvent(event, nullptr);
2035 DispatchEvent(&event);
2036 }
2037 {
2038 WidgetCompositionEvent compositionUpdate(true,
2039 NS_COMPOSITION_UPDATE,
2040 this);
2041 InitEvent(compositionUpdate, nullptr);
2042 compositionUpdate.data = event.theText;
2043 DispatchEvent(&compositionUpdate);
2044 }
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);
2061 }
2062 break;
2063 case AndroidGeckoEvent::IME_REMOVE_COMPOSITION:
2064 {
2065 /*
2066 * Remove any previous composition. This is only used for
2067 * visual indication and does not affect the text content.
2068 *
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();
2076 }
2077 break;
2078 }
2079 }
2081 nsWindow *
2082 nsWindow::FindWindowForPoint(const nsIntPoint& pt)
2083 {
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);
2093 }
2095 return this;
2096 }
2098 void
2099 nsWindow::UserActivity()
2100 {
2101 if (!mIdleService) {
2102 mIdleService = do_GetService("@mozilla.org/widget/idleservice;1");
2103 }
2105 if (mIdleService) {
2106 mIdleService->ResetIdleTimeOut(0);
2107 }
2108 }
2110 NS_IMETHODIMP
2111 nsWindow::NotifyIME(const IMENotification& aIMENotification)
2112 {
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);
2139 }
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;
2162 }
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;
2173 }
2174 }
2176 NS_IMETHODIMP_(void)
2177 nsWindow::SetInputContext(const InputContext& aContext,
2178 const InputContextAction& aAction)
2179 {
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;
2188 }
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;
2204 }
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;
2213 }
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;
2221 }
2223 if (mIMEUpdatingContext) {
2224 return;
2225 }
2226 AndroidGeckoEvent *event = AndroidGeckoEvent::MakeIMEEvent(
2227 AndroidGeckoEvent::IME_UPDATE_CONTEXT);
2228 nsAppShell::gAppShell->PostEvent(event);
2229 mIMEUpdatingContext = true;
2230 }
2232 NS_IMETHODIMP_(InputContext)
2233 nsWindow::GetInputContext()
2234 {
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();
2240 }
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;
2246 }
2248 void
2249 nsWindow::PostFlushIMEChanges()
2250 {
2251 if (!mIMETextChanges.IsEmpty() || mIMESelectionChanged) {
2252 // Already posted
2253 return;
2254 }
2255 AndroidGeckoEvent *event = AndroidGeckoEvent::MakeIMEEvent(
2256 AndroidGeckoEvent::IME_FLUSH_CHANGES);
2257 nsAppShell::gAppShell->PostEvent(event);
2258 }
2260 void
2261 nsWindow::FlushIMEChanges()
2262 {
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);
2279 }
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;
2294 }
2295 }
2297 nsresult
2298 nsWindow::NotifyIMEOfTextChange(const IMENotification& aIMENotification)
2299 {
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;
2330 }
2331 }
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;
2348 }
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;
2367 }
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;
2373 }
2374 return NS_OK;
2375 }
2377 nsIMEUpdatePreference
2378 nsWindow::GetIMEUpdatePreference()
2379 {
2380 return nsIMEUpdatePreference(
2381 nsIMEUpdatePreference::NOTIFY_SELECTION_CHANGE |
2382 nsIMEUpdatePreference::NOTIFY_TEXT_CHANGE);
2383 }
2385 void
2386 nsWindow::DrawWindowUnderlay(LayerManagerComposite* aManager, nsIntRect aRect)
2387 {
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;
2396 }
2398 jobject frameObj = client->CreateFrame();
2399 if (!frameObj) {
2400 NS_WARNING("Warning: unable to obtain a LayerRenderer frame; aborting window underlay draw");
2401 return;
2402 }
2404 mLayerRendererFrame.Init(env, frameObj);
2405 if (!WidgetPaintsBackground()) {
2406 return;
2407 }
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...
2417 }
2419 void
2420 nsWindow::DrawWindowOverlay(LayerManagerComposite* aManager, nsIntRect aRect)
2421 {
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;
2430 }
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);
2443 }
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)
2457 {
2458 sLayerManager = aLayerManager;
2459 sCompositorParent = aCompositorParent;
2460 sCompositorChild = aCompositorChild;
2461 }
2463 void
2464 nsWindow::ScheduleComposite()
2465 {
2466 if (sCompositorParent) {
2467 sCompositorParent->ScheduleRenderOnCompositorThread();
2468 }
2469 }
2471 void
2472 nsWindow::ScheduleResumeComposition(int width, int height)
2473 {
2474 if (sCompositorParent && sCompositorParent->ScheduleResumeOnCompositorThread(width, height)) {
2475 sCompositorPaused = false;
2476 }
2477 }
2479 void
2480 nsWindow::ForceIsFirstPaint()
2481 {
2482 if (sCompositorParent) {
2483 sCompositorParent->ForceIsFirstPaint();
2484 }
2485 }
2487 float
2488 nsWindow::ComputeRenderIntegrity()
2489 {
2490 if (sCompositorParent) {
2491 return sCompositorParent->ComputeRenderIntegrity();
2492 }
2494 return 1.f;
2495 }
2497 bool
2498 nsWindow::WidgetPaintsBackground()
2499 {
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);
2508 }
2510 return sWidgetPaintsBackground;
2511 }
2513 bool
2514 nsWindow::NeedsPaint()
2515 {
2516 if (sCompositorPaused || FindTopLevel() != nsWindow::TopWindow() || !GetLayerManager(nullptr)) {
2517 return false;
2518 }
2519 return nsIWidget::NeedsPaint();
2520 }
2522 CompositorParent*
2523 nsWindow::NewCompositorParent(int aSurfaceWidth, int aSurfaceHeight)
2524 {
2525 return new CompositorParent(this, true, aSurfaceWidth, aSurfaceHeight);
2526 }
2528 mozilla::layers::APZCTreeManager*
2529 nsWindow::GetAPZCTreeManager()
2530 {
2531 if (!sApzcTreeManager) {
2532 CompositorParent* compositor = sCompositorParent;
2533 if (!compositor) {
2534 return nullptr;
2535 }
2536 uint64_t rootLayerTreeId = compositor->RootLayerTreeId();
2537 CompositorParent::SetControllerForLayerTree(rootLayerTreeId, AndroidBridge::Bridge());
2538 sApzcTreeManager = CompositorParent::GetAPZCTreeManager(rootLayerTreeId);
2539 }
2540 return sApzcTreeManager;
2541 }
2543 uint64_t
2544 nsWindow::RootLayerTreeId()
2545 {
2546 MOZ_ASSERT(sCompositorParent);
2547 return sCompositorParent->RootLayerTreeId();
2548 }