|
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/. */ |
|
5 |
|
6 #include <android/log.h> |
|
7 #include <math.h> |
|
8 #include <unistd.h> |
|
9 |
|
10 #include "mozilla/MiscEvents.h" |
|
11 #include "mozilla/MouseEvents.h" |
|
12 #include "mozilla/TextEvents.h" |
|
13 #include "mozilla/TouchEvents.h" |
|
14 |
|
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> |
|
21 |
|
22 using mozilla::dom::ContentParent; |
|
23 using mozilla::dom::ContentChild; |
|
24 using mozilla::unused; |
|
25 |
|
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" |
|
33 |
|
34 #include "nsRenderingContext.h" |
|
35 #include "nsIDOMSimpleGestureEvent.h" |
|
36 |
|
37 #include "nsGkAtoms.h" |
|
38 #include "nsWidgetsCID.h" |
|
39 #include "nsGfxCIID.h" |
|
40 |
|
41 #include "gfxImageSurface.h" |
|
42 #include "gfxContext.h" |
|
43 |
|
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" |
|
52 |
|
53 #include "nsTArray.h" |
|
54 |
|
55 #include "AndroidBridge.h" |
|
56 #include "AndroidBridgeUtilities.h" |
|
57 #include "android_npapi.h" |
|
58 |
|
59 #include "imgIEncoder.h" |
|
60 |
|
61 #include "nsString.h" |
|
62 #include "GeckoProfiler.h" // For PROFILER_LABEL |
|
63 #include "nsIXULRuntime.h" |
|
64 |
|
65 using namespace mozilla; |
|
66 using namespace mozilla::dom; |
|
67 using namespace mozilla::widget; |
|
68 using namespace mozilla::layers; |
|
69 |
|
70 NS_IMPL_ISUPPORTS_INHERITED0(nsWindow, nsBaseWidget) |
|
71 |
|
72 // The dimensions of the current android view |
|
73 static gfxIntSize gAndroidBounds = gfxIntSize(0, 0); |
|
74 static gfxIntSize gAndroidScreenBounds; |
|
75 |
|
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" |
|
82 |
|
83 class ContentCreationNotifier; |
|
84 static StaticRefPtr<ContentCreationNotifier> gContentCreationNotifier; |
|
85 |
|
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 |
|
91 |
|
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 } |
|
111 |
|
112 return NS_OK; |
|
113 } |
|
114 }; |
|
115 |
|
116 NS_IMPL_ISUPPORTS(ContentCreationNotifier, |
|
117 nsIObserver) |
|
118 |
|
119 static bool gMenu; |
|
120 static bool gMenuConsumed; |
|
121 |
|
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; |
|
126 |
|
127 static bool sFailedToCreateGLContext = false; |
|
128 |
|
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; |
|
132 |
|
133 nsWindow* |
|
134 nsWindow::TopWindow() |
|
135 { |
|
136 if (!gTopLevelWindows.IsEmpty()) |
|
137 return gTopLevelWindows[0]; |
|
138 return nullptr; |
|
139 } |
|
140 |
|
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 } |
|
154 |
|
155 void |
|
156 nsWindow::DumpWindows() |
|
157 { |
|
158 DumpWindows(gTopLevelWindows); |
|
159 } |
|
160 |
|
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 } |
|
170 |
|
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 } |
|
184 |
|
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 } |
|
198 |
|
199 bool |
|
200 nsWindow::IsTopLevel() |
|
201 { |
|
202 return mWindowType == eWindowType_toplevel || |
|
203 mWindowType == eWindowType_dialog || |
|
204 mWindowType == eWindowType_invisible; |
|
205 } |
|
206 |
|
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 } |
|
223 |
|
224 mBounds = aRect; |
|
225 |
|
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 } |
|
233 |
|
234 BaseCreate(nullptr, mBounds, aContext, aInitData); |
|
235 |
|
236 NS_ASSERTION(IsTopLevel() || parent, "non top level windowdoesn't have a parent!"); |
|
237 |
|
238 if (IsTopLevel()) { |
|
239 gTopLevelWindows.AppendElement(this); |
|
240 } |
|
241 |
|
242 if (parent) { |
|
243 parent->mChildren.AppendElement(this); |
|
244 mParent = parent; |
|
245 } |
|
246 |
|
247 #ifdef DEBUG_ANDROID_WIDGET |
|
248 DumpWindows(); |
|
249 #endif |
|
250 |
|
251 return NS_OK; |
|
252 } |
|
253 |
|
254 NS_IMETHODIMP |
|
255 nsWindow::Destroy(void) |
|
256 { |
|
257 nsBaseWidget::mOnDestroyCalled = true; |
|
258 |
|
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 } |
|
264 |
|
265 if (IsTopLevel()) |
|
266 gTopLevelWindows.RemoveElement(this); |
|
267 |
|
268 SetParent(nullptr); |
|
269 |
|
270 nsBaseWidget::OnDestroy(); |
|
271 |
|
272 #ifdef DEBUG_ANDROID_WIDGET |
|
273 DumpWindows(); |
|
274 #endif |
|
275 |
|
276 return NS_OK; |
|
277 } |
|
278 |
|
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 } |
|
290 |
|
291 return NS_OK; |
|
292 } |
|
293 |
|
294 void |
|
295 nsWindow::RedrawAll() |
|
296 { |
|
297 if (mFocus && mFocus->mWidgetListener) { |
|
298 mFocus->mWidgetListener->RequestRepaint(); |
|
299 } |
|
300 } |
|
301 |
|
302 NS_IMETHODIMP |
|
303 nsWindow::SetParent(nsIWidget *aNewParent) |
|
304 { |
|
305 if ((nsIWidget*)mParent == aNewParent) |
|
306 return NS_OK; |
|
307 |
|
308 // If we had a parent before, remove ourselves from its list of |
|
309 // children. |
|
310 if (mParent) |
|
311 mParent->mChildren.RemoveElement(this); |
|
312 |
|
313 mParent = (nsWindow*)aNewParent; |
|
314 |
|
315 if (mParent) |
|
316 mParent->mChildren.AppendElement(this); |
|
317 |
|
318 // if we are now in the toplevel window's hierarchy, schedule a redraw |
|
319 if (FindTopLevel() == nsWindow::TopWindow()) |
|
320 RedrawAll(); |
|
321 |
|
322 return NS_OK; |
|
323 } |
|
324 |
|
325 NS_IMETHODIMP |
|
326 nsWindow::ReparentNativeWidget(nsIWidget *aNewParent) |
|
327 { |
|
328 NS_PRECONDITION(aNewParent, ""); |
|
329 return NS_OK; |
|
330 } |
|
331 |
|
332 nsIWidget* |
|
333 nsWindow::GetParent() |
|
334 { |
|
335 return mParent; |
|
336 } |
|
337 |
|
338 float |
|
339 nsWindow::GetDPI() |
|
340 { |
|
341 if (AndroidBridge::Bridge()) |
|
342 return AndroidBridge::Bridge()->GetDPI(); |
|
343 return 160.0f; |
|
344 } |
|
345 |
|
346 double |
|
347 nsWindow::GetDefaultScaleInternal() |
|
348 { |
|
349 static double density = 0.0; |
|
350 |
|
351 if (density != 0.0) { |
|
352 return density; |
|
353 } |
|
354 |
|
355 density = mozilla::widget::android::GeckoAppShell::GetDensity(); |
|
356 |
|
357 if (!density) { |
|
358 density = 1.0; |
|
359 } |
|
360 |
|
361 return density; |
|
362 } |
|
363 |
|
364 NS_IMETHODIMP |
|
365 nsWindow::Show(bool aState) |
|
366 { |
|
367 ALOG("nsWindow[%p]::Show %d", (void*)this, aState); |
|
368 |
|
369 if (mWindowType == eWindowType_invisible) { |
|
370 ALOG("trying to show invisible window! ignoring.."); |
|
371 return NS_ERROR_FAILURE; |
|
372 } |
|
373 |
|
374 if (aState == mIsVisible) |
|
375 return NS_OK; |
|
376 |
|
377 mIsVisible = aState; |
|
378 |
|
379 if (IsTopLevel()) { |
|
380 // XXX should we bring this to the front when it's shown, |
|
381 // if it's a toplevel widget? |
|
382 |
|
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. |
|
387 |
|
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; |
|
400 |
|
401 win->BringToFront(); |
|
402 break; |
|
403 } |
|
404 } |
|
405 } else if (FindTopLevel() == nsWindow::TopWindow()) { |
|
406 RedrawAll(); |
|
407 } |
|
408 |
|
409 #ifdef DEBUG_ANDROID_WIDGET |
|
410 DumpWindows(); |
|
411 #endif |
|
412 |
|
413 return NS_OK; |
|
414 } |
|
415 |
|
416 NS_IMETHODIMP |
|
417 nsWindow::SetModal(bool aState) |
|
418 { |
|
419 ALOG("nsWindow[%p]::SetModal %d ignored", (void*)this, aState); |
|
420 |
|
421 return NS_OK; |
|
422 } |
|
423 |
|
424 bool |
|
425 nsWindow::IsVisible() const |
|
426 { |
|
427 return mIsVisible; |
|
428 } |
|
429 |
|
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); |
|
436 |
|
437 // constrain toplevel windows; children we don't care about |
|
438 if (IsTopLevel()) { |
|
439 *aX = 0; |
|
440 *aY = 0; |
|
441 } |
|
442 |
|
443 return NS_OK; |
|
444 } |
|
445 |
|
446 NS_IMETHODIMP |
|
447 nsWindow::Move(double aX, |
|
448 double aY) |
|
449 { |
|
450 if (IsTopLevel()) |
|
451 return NS_OK; |
|
452 |
|
453 return Resize(aX, |
|
454 aY, |
|
455 mBounds.width, |
|
456 mBounds.height, |
|
457 true); |
|
458 } |
|
459 |
|
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 } |
|
471 |
|
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); |
|
480 |
|
481 bool needSizeDispatch = aWidth != mBounds.width || aHeight != mBounds.height; |
|
482 |
|
483 mBounds.x = NSToIntRound(aX); |
|
484 mBounds.y = NSToIntRound(aY); |
|
485 mBounds.width = NSToIntRound(aWidth); |
|
486 mBounds.height = NSToIntRound(aHeight); |
|
487 |
|
488 if (needSizeDispatch) |
|
489 OnSizeChanged(gfxIntSize(aWidth, aHeight)); |
|
490 |
|
491 // Should we skip honoring aRepaint here? |
|
492 if (aRepaint && FindTopLevel() == nsWindow::TopWindow()) |
|
493 RedrawAll(); |
|
494 |
|
495 return NS_OK; |
|
496 } |
|
497 |
|
498 void |
|
499 nsWindow::SetZIndex(int32_t aZIndex) |
|
500 { |
|
501 ALOG("nsWindow[%p]::SetZIndex %d ignored", (void*)this, aZIndex); |
|
502 } |
|
503 |
|
504 NS_IMETHODIMP |
|
505 nsWindow::PlaceBehind(nsTopLevelWidgetZPlacement aPlacement, |
|
506 nsIWidget *aWidget, |
|
507 bool aActivate) |
|
508 { |
|
509 return NS_OK; |
|
510 } |
|
511 |
|
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 } |
|
525 |
|
526 NS_IMETHODIMP |
|
527 nsWindow::Enable(bool aState) |
|
528 { |
|
529 ALOG("nsWindow[%p]::Enable %d ignored", (void*)this, aState); |
|
530 return NS_OK; |
|
531 } |
|
532 |
|
533 bool |
|
534 nsWindow::IsEnabled() const |
|
535 { |
|
536 return true; |
|
537 } |
|
538 |
|
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 } |
|
546 |
|
547 nsWindow* |
|
548 nsWindow::FindTopLevel() |
|
549 { |
|
550 nsWindow *toplevel = this; |
|
551 while (toplevel) { |
|
552 if (toplevel->IsTopLevel()) |
|
553 return toplevel; |
|
554 |
|
555 toplevel = toplevel->mParent; |
|
556 } |
|
557 |
|
558 ALOG("nsWindow::FindTopLevel(): couldn't find a toplevel or dialog window in this [%p] widget's hierarchy!", (void*)this); |
|
559 return this; |
|
560 } |
|
561 |
|
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 } |
|
568 |
|
569 nsWindow *top = FindTopLevel(); |
|
570 top->mFocus = this; |
|
571 top->BringToFront(); |
|
572 |
|
573 return NS_OK; |
|
574 } |
|
575 |
|
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; |
|
588 |
|
589 if (!IsTopLevel()) { |
|
590 FindTopLevel()->BringToFront(); |
|
591 return; |
|
592 } |
|
593 |
|
594 nsRefPtr<nsWindow> kungFuDeathGrip(this); |
|
595 |
|
596 nsWindow *oldTop = nullptr; |
|
597 nsWindow *newTop = this; |
|
598 if (!gTopLevelWindows.IsEmpty()) |
|
599 oldTop = gTopLevelWindows[0]; |
|
600 |
|
601 gTopLevelWindows.RemoveElement(this); |
|
602 gTopLevelWindows.InsertElementAt(0, this); |
|
603 |
|
604 if (oldTop) { |
|
605 nsIWidgetListener* listener = oldTop->GetWidgetListener(); |
|
606 if (listener) { |
|
607 listener->WindowDeactivated(); |
|
608 } |
|
609 } |
|
610 |
|
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 } |
|
619 |
|
620 if (mWidgetListener) { |
|
621 mWidgetListener->WindowActivated(); |
|
622 } |
|
623 |
|
624 // force a window resize |
|
625 nsAppShell::gAppShell->ResendLastResizeEvent(newTop); |
|
626 RedrawAll(); |
|
627 } |
|
628 |
|
629 NS_IMETHODIMP |
|
630 nsWindow::GetScreenBounds(nsIntRect &aRect) |
|
631 { |
|
632 nsIntPoint p = WidgetToScreenOffset(); |
|
633 |
|
634 aRect.x = p.x; |
|
635 aRect.y = p.y; |
|
636 aRect.width = mBounds.width; |
|
637 aRect.height = mBounds.height; |
|
638 |
|
639 return NS_OK; |
|
640 } |
|
641 |
|
642 nsIntPoint |
|
643 nsWindow::WidgetToScreenOffset() |
|
644 { |
|
645 nsIntPoint p(0, 0); |
|
646 nsWindow *w = this; |
|
647 |
|
648 while (w && !w->IsTopLevel()) { |
|
649 p.x += w->mBounds.x; |
|
650 p.y += w->mBounds.y; |
|
651 |
|
652 w = w->mParent; |
|
653 } |
|
654 |
|
655 return p; |
|
656 } |
|
657 |
|
658 NS_IMETHODIMP |
|
659 nsWindow::DispatchEvent(WidgetGUIEvent* aEvent, |
|
660 nsEventStatus &aStatus) |
|
661 { |
|
662 aStatus = DispatchEvent(aEvent); |
|
663 return NS_OK; |
|
664 } |
|
665 |
|
666 nsEventStatus |
|
667 nsWindow::DispatchEvent(WidgetGUIEvent* aEvent) |
|
668 { |
|
669 if (mWidgetListener) { |
|
670 nsEventStatus status = mWidgetListener->HandleEvent(aEvent, mUseAttachedEvents); |
|
671 |
|
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 } |
|
691 |
|
692 NS_IMETHODIMP |
|
693 nsWindow::MakeFullScreen(bool aFullScreen) |
|
694 { |
|
695 mozilla::widget::android::GeckoAppShell::SetFullScreen(aFullScreen); |
|
696 return NS_OK; |
|
697 } |
|
698 |
|
699 NS_IMETHODIMP |
|
700 nsWindow::SetWindowClass(const nsAString& xulWinType) |
|
701 { |
|
702 return NS_OK; |
|
703 } |
|
704 |
|
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 } |
|
722 |
|
723 void |
|
724 nsWindow::CreateLayerManager(int aCompositorWidth, int aCompositorHeight) |
|
725 { |
|
726 if (mLayerManager) { |
|
727 return; |
|
728 } |
|
729 |
|
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 } |
|
735 |
|
736 mUseLayersAcceleration = ComputeShouldAccelerate(mUseLayersAcceleration); |
|
737 |
|
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 } |
|
750 |
|
751 // If we get here, then off main thread compositing failed to initialize. |
|
752 sFailedToCreateGLContext = true; |
|
753 } |
|
754 |
|
755 if (!mUseLayersAcceleration || sFailedToCreateGLContext) { |
|
756 printf_stderr(" -- creating basic, not accelerated\n"); |
|
757 mLayerManager = CreateBasicLayerManager(); |
|
758 } |
|
759 } |
|
760 |
|
761 void |
|
762 nsWindow::OnGlobalAndroidEvent(AndroidGeckoEvent *ae) |
|
763 { |
|
764 nsWindow *win = TopWindow(); |
|
765 if (!win) |
|
766 return; |
|
767 |
|
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"); |
|
780 |
|
781 int nw = points[0].x; |
|
782 int nh = points[0].y; |
|
783 |
|
784 if (ae->Type() == AndroidGeckoEvent::FORCED_RESIZE || nw != gAndroidBounds.width || |
|
785 nh != gAndroidBounds.height) { |
|
786 gAndroidBounds.width = nw; |
|
787 gAndroidBounds.height = nh; |
|
788 |
|
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 } |
|
797 |
|
798 int newScreenWidth = points[1].x; |
|
799 int newScreenHeight = points[1].y; |
|
800 |
|
801 if (newScreenWidth == gAndroidScreenBounds.width && |
|
802 newScreenHeight == gAndroidScreenBounds.height) |
|
803 break; |
|
804 |
|
805 gAndroidScreenBounds.width = newScreenWidth; |
|
806 gAndroidScreenBounds.height = newScreenHeight; |
|
807 |
|
808 if (XRE_GetProcessType() != GeckoProcessType_Default || |
|
809 !BrowserTabsRemote()) { |
|
810 break; |
|
811 } |
|
812 |
|
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); |
|
818 |
|
819 if (gContentCreationNotifier) |
|
820 break; |
|
821 |
|
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; |
|
827 |
|
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 } |
|
837 |
|
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); |
|
853 |
|
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 } |
|
864 |
|
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); |
|
874 |
|
875 target->OnNativeGestureEvent(ae); |
|
876 break; |
|
877 } |
|
878 |
|
879 case AndroidGeckoEvent::KEY_EVENT: |
|
880 win->UserActivity(); |
|
881 if (win->mFocus) |
|
882 win->mFocus->OnKeyEvent(ae); |
|
883 break; |
|
884 |
|
885 case AndroidGeckoEvent::DRAW: |
|
886 layers::renderTraceEventStart("Global draw start", "414141"); |
|
887 win->OnDraw(ae); |
|
888 layers::renderTraceEventEnd("414141"); |
|
889 break; |
|
890 |
|
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; |
|
900 |
|
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; |
|
909 |
|
910 case AndroidGeckoEvent::COMPOSITOR_CREATE: |
|
911 win->CreateLayerManager(ae->Width(), ae->Height()); |
|
912 break; |
|
913 |
|
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; |
|
924 |
|
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 } |
|
939 |
|
940 void |
|
941 nsWindow::OnAndroidEvent(AndroidGeckoEvent *ae) |
|
942 { |
|
943 switch (ae->Type()) { |
|
944 case AndroidGeckoEvent::DRAW: |
|
945 OnDraw(ae); |
|
946 break; |
|
947 |
|
948 default: |
|
949 ALOG("Window got targetted android event type %d, but didn't handle!", ae->Type()); |
|
950 break; |
|
951 } |
|
952 } |
|
953 |
|
954 bool |
|
955 nsWindow::DrawTo(gfxASurface *targetSurface) |
|
956 { |
|
957 nsIntRect boundsRect(0, 0, mBounds.width, mBounds.height); |
|
958 return DrawTo(targetSurface, boundsRect); |
|
959 } |
|
960 |
|
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; |
|
967 |
|
968 nsRefPtr<nsWindow> kungFuDeathGrip(this); |
|
969 nsIntRect boundsRect(0, 0, mBounds.width, mBounds.height); |
|
970 |
|
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; |
|
976 |
|
977 if (mChildren[i]->mBounds.Contains(boundsRect)) { |
|
978 coveringChildIndex = int32_t(i); |
|
979 } |
|
980 } |
|
981 |
|
982 // If we have no covering child, then we need to render this. |
|
983 if (coveringChildIndex == -1) { |
|
984 nsIntRegion region = invalidRect; |
|
985 |
|
986 mWidgetListener->WillPaintWindow(this); |
|
987 |
|
988 switch (GetLayerManager(nullptr)->GetBackendType()) { |
|
989 case mozilla::layers::LayersBackend::LAYERS_BASIC: { |
|
990 |
|
991 nsRefPtr<gfxContext> ctx = new gfxContext(targetSurface); |
|
992 |
|
993 { |
|
994 mozilla::layers::RenderTraceScope trace2("Basic DrawTo", "727272"); |
|
995 AutoLayerManagerSetup |
|
996 setupLayerManager(this, ctx, mozilla::layers::BufferMode::BUFFER_NONE); |
|
997 |
|
998 mWidgetListener->PaintWindow(this, region); |
|
999 } |
|
1000 break; |
|
1001 } |
|
1002 |
|
1003 case mozilla::layers::LayersBackend::LAYERS_CLIENT: { |
|
1004 mWidgetListener->PaintWindow(this, region); |
|
1005 break; |
|
1006 } |
|
1007 |
|
1008 default: |
|
1009 NS_ERROR("Invalid layer manager"); |
|
1010 } |
|
1011 |
|
1012 mWidgetListener->DidPaintWindow(); |
|
1013 |
|
1014 // We had no covering child, so make sure we draw all the children, |
|
1015 // starting from index 0. |
|
1016 coveringChildIndex = 0; |
|
1017 } |
|
1018 |
|
1019 gfxPoint offset; |
|
1020 |
|
1021 if (targetSurface) |
|
1022 offset = targetSurface->GetDeviceOffset(); |
|
1023 |
|
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 } |
|
1029 |
|
1030 if (targetSurface) |
|
1031 targetSurface->SetDeviceOffset(offset + gfxPoint(mChildren[i]->mBounds.x, |
|
1032 mChildren[i]->mBounds.y)); |
|
1033 |
|
1034 bool ok = mChildren[i]->DrawTo(targetSurface, invalidRect); |
|
1035 |
|
1036 if (!ok) { |
|
1037 ALOG("nsWindow[%p]::DrawTo child %d[%p] returned FALSE!", (void*) this, i, (void*)mChildren[i]); |
|
1038 } |
|
1039 } |
|
1040 |
|
1041 if (targetSurface) |
|
1042 targetSurface->SetDeviceOffset(offset); |
|
1043 |
|
1044 return true; |
|
1045 } |
|
1046 |
|
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 } |
|
1055 |
|
1056 if (!mIsVisible) { |
|
1057 ALOG("##### redraw for window %p, which is not visible -- ignoring!", (void*) this); |
|
1058 DumpWindows(); |
|
1059 return; |
|
1060 } |
|
1061 |
|
1062 nsRefPtr<nsWindow> kungFuDeathGrip(this); |
|
1063 |
|
1064 AutoLocalJNIFrame jniFrame; |
|
1065 |
|
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 } |
|
1070 |
|
1071 int bytesPerPixel = 2; |
|
1072 gfxImageFormat format = gfxImageFormat::RGB16_565; |
|
1073 if (AndroidBridge::Bridge()->GetScreenDepth() == 24) { |
|
1074 bytesPerPixel = 4; |
|
1075 format = gfxImageFormat::RGB24; |
|
1076 } |
|
1077 |
|
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"); |
|
1083 |
|
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 } |
|
1092 |
|
1093 void |
|
1094 nsWindow::OnSizeChanged(const gfxIntSize& aSize) |
|
1095 { |
|
1096 ALOG("nsWindow: %p OnSizeChanged [%d %d]", (void*)this, aSize.width, aSize.height); |
|
1097 |
|
1098 mBounds.width = aSize.width; |
|
1099 mBounds.height = aSize.height; |
|
1100 |
|
1101 if (mWidgetListener) { |
|
1102 mWidgetListener->WindowResized(this, aSize.width, aSize.height); |
|
1103 } |
|
1104 } |
|
1105 |
|
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 } |
|
1116 |
|
1117 event.time = PR_Now() / 1000; |
|
1118 } |
|
1119 |
|
1120 gfxIntSize |
|
1121 nsWindow::GetAndroidScreenBounds() |
|
1122 { |
|
1123 if (XRE_GetProcessType() == GeckoProcessType_Content) { |
|
1124 return ContentChild::GetSingleton()->GetScreenSize(); |
|
1125 } |
|
1126 return gAndroidScreenBounds; |
|
1127 } |
|
1128 |
|
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; |
|
1136 |
|
1137 case NS_NATIVE_WIDGET: |
|
1138 return (void *) this; |
|
1139 } |
|
1140 |
|
1141 return nullptr; |
|
1142 } |
|
1143 |
|
1144 void |
|
1145 nsWindow::OnMouseEvent(AndroidGeckoEvent *ae) |
|
1146 { |
|
1147 nsRefPtr<nsWindow> kungFuDeathGrip(this); |
|
1148 |
|
1149 WidgetMouseEvent event = ae->MakeMouseEvent(this); |
|
1150 if (event.message == NS_EVENT_NULL) { |
|
1151 // invalid event type, abort |
|
1152 return; |
|
1153 } |
|
1154 |
|
1155 // XXX add the double-click handling logic here |
|
1156 DispatchEvent(&event); |
|
1157 } |
|
1158 |
|
1159 bool nsWindow::OnMultitouchEvent(AndroidGeckoEvent *ae) |
|
1160 { |
|
1161 nsRefPtr<nsWindow> kungFuDeathGrip(this); |
|
1162 |
|
1163 // End any composition in progress in case the touch event listener |
|
1164 // modifies the input field value (see bug 856155) |
|
1165 RemoveIMEComposition(); |
|
1166 |
|
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; |
|
1172 |
|
1173 bool preventDefaultActions = false; |
|
1174 bool isDownEvent = false; |
|
1175 |
|
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 } |
|
1188 |
|
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 } |
|
1199 |
|
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; |
|
1212 |
|
1213 return preventDefaultActions; |
|
1214 } |
|
1215 |
|
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; |
|
1223 |
|
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 } |
|
1242 |
|
1243 nsRefPtr<nsWindow> kungFuDeathGrip(this); |
|
1244 DispatchGestureEvent(msg, 0, delta, pt, ae->Time()); |
|
1245 } |
|
1246 |
|
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); |
|
1252 |
|
1253 event.direction = direction; |
|
1254 event.delta = delta; |
|
1255 event.modifiers = 0; |
|
1256 event.time = time; |
|
1257 event.refPoint = LayoutDeviceIntPoint::FromUntyped(refPoint); |
|
1258 |
|
1259 DispatchEvent(&event); |
|
1260 } |
|
1261 |
|
1262 |
|
1263 void |
|
1264 nsWindow::DispatchMotionEvent(WidgetInputEvent &event, AndroidGeckoEvent *ae, |
|
1265 const nsIntPoint &refPoint) |
|
1266 { |
|
1267 nsIntPoint offset = WidgetToScreenOffset(); |
|
1268 |
|
1269 event.modifiers = ae->DOMModifiers(); |
|
1270 event.time = ae->Time(); |
|
1271 |
|
1272 // XXX possibly bound the range of event.refPoint here. |
|
1273 // some code may get confused. |
|
1274 event.refPoint = LayoutDeviceIntPoint::FromUntyped(refPoint - offset); |
|
1275 |
|
1276 DispatchEvent(&event); |
|
1277 } |
|
1278 |
|
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 } |
|
1286 |
|
1287 if (androidKeyCode >= AKEYCODE_0 && |
|
1288 androidKeyCode <= AKEYCODE_9) { |
|
1289 return androidKeyCode - AKEYCODE_0 + NS_VK_0; |
|
1290 } |
|
1291 |
|
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) |
|
1374 |
|
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; |
|
1388 |
|
1389 // the A key is the action key for gamepad devices. |
|
1390 case AKEYCODE_BUTTON_A: return NS_VK_RETURN; |
|
1391 |
|
1392 default: |
|
1393 ALOG("ConvertAndroidKeyCodeToDOMKeyCode: " |
|
1394 "No DOM keycode for Android keycode %d", androidKeyCode); |
|
1395 return 0; |
|
1396 } |
|
1397 } |
|
1398 |
|
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 } |
|
1407 |
|
1408 if (keyCode >= AKEYCODE_0 && keyCode <= AKEYCODE_9) { |
|
1409 return KEY_NAME_INDEX_USE_STRING; |
|
1410 } |
|
1411 |
|
1412 switch (keyCode) { |
|
1413 |
|
1414 #define NS_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX(aNativeKey, aKeyNameIndex) \ |
|
1415 case aNativeKey: return aKeyNameIndex; |
|
1416 |
|
1417 #include "NativeKeyToDOMKeyName.h" |
|
1418 |
|
1419 #undef NS_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX |
|
1420 |
|
1421 // KEYCODE_0 (7) ... KEYCODE_9 (16) |
|
1422 case AKEYCODE_STAR: // '*' key |
|
1423 case AKEYCODE_POUND: // '#' key |
|
1424 |
|
1425 // KEYCODE_A (29) ... KEYCODE_Z (54) |
|
1426 |
|
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 |
|
1441 |
|
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: |
|
1461 |
|
1462 case AKEYCODE_YEN: // yen sign key |
|
1463 case AKEYCODE_RO: // Japanese Ro key |
|
1464 return KEY_NAME_INDEX_USE_STRING; |
|
1465 |
|
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: |
|
1476 |
|
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: |
|
1492 |
|
1493 case AKEYCODE_MUTE: // mutes the microphone |
|
1494 case AKEYCODE_MEDIA_CLOSE: |
|
1495 |
|
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: |
|
1505 |
|
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: |
|
1522 |
|
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: |
|
1530 |
|
1531 case AKEYCODE_ZENKAKU_HANKAKU: |
|
1532 case AKEYCODE_KATAKANA_HIRAGANA: |
|
1533 return KEY_NAME_INDEX_Unidentified; |
|
1534 |
|
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; |
|
1543 |
|
1544 default: |
|
1545 ALOG("ConvertAndroidKeyCodeToKeyNameIndex: " |
|
1546 "No DOM key name index for Android keycode %d", keyCode); |
|
1547 return KEY_NAME_INDEX_Unidentified; |
|
1548 } |
|
1549 } |
|
1550 |
|
1551 static void InitPluginEvent(ANPEvent* pluginEvent, ANPKeyActions keyAction, |
|
1552 AndroidGeckoEvent& key) |
|
1553 { |
|
1554 int androidKeyCode = key.KeyCode(); |
|
1555 uint32_t domKeyCode = ConvertAndroidKeyCodeToDOMKeyCode(androidKeyCode); |
|
1556 |
|
1557 int modifiers = 0; |
|
1558 if (key.IsAltPressed()) |
|
1559 modifiers |= kAlt_ANPKeyModifier; |
|
1560 if (key.IsShiftPressed()) |
|
1561 modifiers |= kShift_ANPKeyModifier; |
|
1562 |
|
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 } |
|
1572 |
|
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()); |
|
1585 |
|
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 |
|
1602 |
|
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); |
|
1608 |
|
1609 event.isChar = false; |
|
1610 event.charCode = 0; |
|
1611 event.keyCode = domKeyCode; |
|
1612 event.pluginEvent = pluginEvent; |
|
1613 } |
|
1614 |
|
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 } |
|
1629 |
|
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(); |
|
1635 |
|
1636 if (gMenu) |
|
1637 gMenuConsumed = true; |
|
1638 } |
|
1639 |
|
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(); |
|
1649 |
|
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 } |
|
1695 |
|
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 } |
|
1717 |
|
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 } |
|
1734 |
|
1735 nsEventStatus status; |
|
1736 WidgetKeyboardEvent event(true, msg, this); |
|
1737 ANPEvent pluginEvent; |
|
1738 InitKeyEvent(event, *ae, &pluginEvent); |
|
1739 DispatchEvent(&event, status); |
|
1740 |
|
1741 if (Destroyed()) |
|
1742 return; |
|
1743 if (!firePress || status == nsEventStatus_eConsumeNoDefault) { |
|
1744 return; |
|
1745 } |
|
1746 |
|
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 } |
|
1754 |
|
1755 #ifdef DEBUG_ANDROID_IME |
|
1756 #define ALOGIME(args...) ALOG(args) |
|
1757 #else |
|
1758 #define ALOGIME(args...) ((void)0) |
|
1759 #endif |
|
1760 |
|
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 } |
|
1769 |
|
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 }; |
|
1781 |
|
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; |
|
1791 |
|
1792 nsRefPtr<nsWindow> kungFuDeathGrip(this); |
|
1793 AutoIMEMask selMask(mIMEMaskSelectionUpdate); |
|
1794 AutoIMEMask textMask(mIMEMaskTextUpdate); |
|
1795 |
|
1796 WidgetTextEvent textEvent(true, NS_TEXT_TEXT, this); |
|
1797 InitEvent(textEvent, nullptr); |
|
1798 textEvent.theText = mIMEComposingText; |
|
1799 DispatchEvent(&textEvent); |
|
1800 |
|
1801 WidgetCompositionEvent event(true, NS_COMPOSITION_END, this); |
|
1802 InitEvent(event, nullptr); |
|
1803 DispatchEvent(&event); |
|
1804 } |
|
1805 |
|
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: |
|
1813 |
|
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); |
|
1822 |
|
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() |
|
1876 |
|
1877 Selection updates are masked so the result of our temporary |
|
1878 selection event is not passed on to Java |
|
1879 |
|
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 } |
|
1893 |
|
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 } |
|
1903 |
|
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() |
|
1935 |
|
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); |
|
1943 |
|
1944 int32_t start = ae->Start(), end = ae->End(); |
|
1945 |
|
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); |
|
1952 |
|
1953 if (start < 0) |
|
1954 start = int32_t(event.GetSelectionStart()); |
|
1955 if (end < 0) |
|
1956 end = int32_t(event.GetSelectionEnd()); |
|
1957 } |
|
1958 |
|
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; |
|
1963 |
|
1964 DispatchEvent(&selEvent); |
|
1965 |
|
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. |
|
2001 |
|
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(); |
|
2008 |
|
2009 WidgetTextEvent event(true, NS_TEXT_TEXT, this); |
|
2010 InitEvent(event, nullptr); |
|
2011 |
|
2012 event.mRanges = new TextRangeArray(); |
|
2013 mIMERanges.swap(event.mRanges); |
|
2014 |
|
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 } |
|
2045 |
|
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 |
|
2052 |
|
2053 DispatchEvent(&event); |
|
2054 |
|
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 } |
|
2080 |
|
2081 nsWindow * |
|
2082 nsWindow::FindWindowForPoint(const nsIntPoint& pt) |
|
2083 { |
|
2084 if (!mBounds.Contains(pt)) |
|
2085 return nullptr; |
|
2086 |
|
2087 // children mBounds are relative to their parent |
|
2088 nsIntPoint childPoint(pt.x - mBounds.x, pt.y - mBounds.y); |
|
2089 |
|
2090 for (uint32_t i = 0; i < mChildren.Length(); ++i) { |
|
2091 if (mChildren[i]->mBounds.Contains(childPoint)) |
|
2092 return mChildren[i]->FindWindowForPoint(childPoint); |
|
2093 } |
|
2094 |
|
2095 return this; |
|
2096 } |
|
2097 |
|
2098 void |
|
2099 nsWindow::UserActivity() |
|
2100 { |
|
2101 if (!mIdleService) { |
|
2102 mIdleService = do_GetService("@mozilla.org/widget/idleservice;1"); |
|
2103 } |
|
2104 |
|
2105 if (mIdleService) { |
|
2106 mIdleService->ResetIdleTimeOut(0); |
|
2107 } |
|
2108 } |
|
2109 |
|
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"); |
|
2121 |
|
2122 // Cancel composition on Gecko side |
|
2123 if (mIMEComposing) { |
|
2124 nsRefPtr<nsWindow> kungFuDeathGrip(this); |
|
2125 |
|
2126 WidgetCompositionEvent updateEvent(true, NS_COMPOSITION_UPDATE, |
|
2127 this); |
|
2128 InitEvent(updateEvent, nullptr); |
|
2129 DispatchEvent(&updateEvent); |
|
2130 |
|
2131 WidgetTextEvent textEvent(true, NS_TEXT_TEXT, this); |
|
2132 InitEvent(textEvent, nullptr); |
|
2133 DispatchEvent(&textEvent); |
|
2134 |
|
2135 WidgetCompositionEvent compEvent(true, NS_COMPOSITION_END, |
|
2136 this); |
|
2137 InitEvent(compEvent, nullptr); |
|
2138 DispatchEvent(&compEvent); |
|
2139 } |
|
2140 |
|
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"); |
|
2149 |
|
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(); |
|
2156 |
|
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 } |
|
2163 |
|
2164 ALOGIME("IME: NOTIFY_IME_OF_SELECTION_CHANGE"); |
|
2165 |
|
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 } |
|
2175 |
|
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 } |
|
2189 |
|
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); |
|
2193 |
|
2194 mInputContext = aContext; |
|
2195 |
|
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 } |
|
2205 |
|
2206 IMEState::Enabled enabled = aContext.mIMEState.mEnabled; |
|
2207 |
|
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 } |
|
2214 |
|
2215 mInputContext.mIMEState.mEnabled = enabled; |
|
2216 |
|
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 } |
|
2222 |
|
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 } |
|
2231 |
|
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 } |
|
2247 |
|
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 } |
|
2259 |
|
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]; |
|
2266 |
|
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; |
|
2274 |
|
2275 mozilla::widget::android::GeckoAppShell::NotifyIMEChange(event.mReply.mString, |
|
2276 change.mStart, |
|
2277 change.mOldEnd, |
|
2278 change.mNewEnd); |
|
2279 } |
|
2280 mIMETextChanges.Clear(); |
|
2281 |
|
2282 if (mIMESelectionChanged) { |
|
2283 WidgetQueryContentEvent event(true, NS_QUERY_SELECTED_TEXT, this); |
|
2284 InitEvent(event, nullptr); |
|
2285 |
|
2286 DispatchEvent(&event); |
|
2287 if (!event.mSucceeded) |
|
2288 return; |
|
2289 |
|
2290 mozilla::widget::android::GeckoAppShell::NotifyIMEChange(EmptyString(), |
|
2291 (int32_t) event.GetSelectionStart(), |
|
2292 (int32_t) event.GetSelectionEnd(), -1); |
|
2293 mIMESelectionChanged = false; |
|
2294 } |
|
2295 } |
|
2296 |
|
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"); |
|
2302 |
|
2303 if (mIMEMaskTextUpdate) |
|
2304 return NS_OK; |
|
2305 |
|
2306 ALOGIME("IME: NotifyIMEOfTextChange: s=%d, oe=%d, ne=%d", |
|
2307 aIMENotification.mTextChangeData.mStartOffset, |
|
2308 aIMENotification.mTextChangeData.mOldEndOffset, |
|
2309 aIMENotification.mTextChangeData.mNewEndOffset); |
|
2310 |
|
2311 /* Make sure Java's selection is up-to-date */ |
|
2312 mIMESelectionChanged = false; |
|
2313 NotifyIME(NOTIFY_IME_OF_SELECTION_CHANGE); |
|
2314 PostFlushIMEChanges(); |
|
2315 |
|
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 } |
|
2332 |
|
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; |
|
2338 |
|
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 } |
|
2376 |
|
2377 nsIMEUpdatePreference |
|
2378 nsWindow::GetIMEUpdatePreference() |
|
2379 { |
|
2380 return nsIMEUpdatePreference( |
|
2381 nsIMEUpdatePreference::NOTIFY_SELECTION_CHANGE | |
|
2382 nsIMEUpdatePreference::NOTIFY_TEXT_CHANGE); |
|
2383 } |
|
2384 |
|
2385 void |
|
2386 nsWindow::DrawWindowUnderlay(LayerManagerComposite* aManager, nsIntRect aRect) |
|
2387 { |
|
2388 JNIEnv *env = GetJNIForThread(); |
|
2389 |
|
2390 AutoLocalJNIFrame jniFrame(env); |
|
2391 |
|
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 } |
|
2397 |
|
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 } |
|
2403 |
|
2404 mLayerRendererFrame.Init(env, frameObj); |
|
2405 if (!WidgetPaintsBackground()) { |
|
2406 return; |
|
2407 } |
|
2408 |
|
2409 gl::GLContext* gl = static_cast<CompositorOGL*>(aManager->GetCompositor())->gl(); |
|
2410 gl::ScopedGLState scopedScissorTestState(gl, LOCAL_GL_SCISSOR_TEST); |
|
2411 gl::ScopedScissorRect scopedScissorRectState(gl); |
|
2412 |
|
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 } |
|
2418 |
|
2419 void |
|
2420 nsWindow::DrawWindowOverlay(LayerManagerComposite* aManager, nsIntRect aRect) |
|
2421 { |
|
2422 PROFILER_LABEL("nsWindow", "DrawWindowOverlay"); |
|
2423 JNIEnv *env = GetJNIForThread(); |
|
2424 |
|
2425 AutoLocalJNIFrame jniFrame(env); |
|
2426 |
|
2427 if (mLayerRendererFrame.isNull()) { |
|
2428 NS_WARNING("Warning: do not have a LayerRenderer frame; aborting window overlay draw"); |
|
2429 return; |
|
2430 } |
|
2431 |
|
2432 mozilla::widget::android::GeckoLayerClient* client = AndroidBridge::Bridge()->GetLayerClient(); |
|
2433 |
|
2434 gl::GLContext* gl = static_cast<CompositorOGL*>(aManager->GetCompositor())->gl(); |
|
2435 gl::ScopedGLState scopedScissorTestState(gl, LOCAL_GL_SCISSOR_TEST); |
|
2436 gl::ScopedScissorRect scopedScissorRectState(gl); |
|
2437 |
|
2438 client->ActivateProgram(); |
|
2439 if (!mLayerRendererFrame.DrawForeground(&jniFrame)) return; |
|
2440 if (!mLayerRendererFrame.EndDrawing(&jniFrame)) return; |
|
2441 client->DeactivateProgram(); |
|
2442 mLayerRendererFrame.Dispose(env); |
|
2443 } |
|
2444 |
|
2445 // off-main-thread compositor fields and functions |
|
2446 |
|
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; |
|
2452 |
|
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 } |
|
2462 |
|
2463 void |
|
2464 nsWindow::ScheduleComposite() |
|
2465 { |
|
2466 if (sCompositorParent) { |
|
2467 sCompositorParent->ScheduleRenderOnCompositorThread(); |
|
2468 } |
|
2469 } |
|
2470 |
|
2471 void |
|
2472 nsWindow::ScheduleResumeComposition(int width, int height) |
|
2473 { |
|
2474 if (sCompositorParent && sCompositorParent->ScheduleResumeOnCompositorThread(width, height)) { |
|
2475 sCompositorPaused = false; |
|
2476 } |
|
2477 } |
|
2478 |
|
2479 void |
|
2480 nsWindow::ForceIsFirstPaint() |
|
2481 { |
|
2482 if (sCompositorParent) { |
|
2483 sCompositorParent->ForceIsFirstPaint(); |
|
2484 } |
|
2485 } |
|
2486 |
|
2487 float |
|
2488 nsWindow::ComputeRenderIntegrity() |
|
2489 { |
|
2490 if (sCompositorParent) { |
|
2491 return sCompositorParent->ComputeRenderIntegrity(); |
|
2492 } |
|
2493 |
|
2494 return 1.f; |
|
2495 } |
|
2496 |
|
2497 bool |
|
2498 nsWindow::WidgetPaintsBackground() |
|
2499 { |
|
2500 static bool sWidgetPaintsBackground = true; |
|
2501 static bool sWidgetPaintsBackgroundPrefCached = false; |
|
2502 |
|
2503 if (!sWidgetPaintsBackgroundPrefCached) { |
|
2504 sWidgetPaintsBackgroundPrefCached = true; |
|
2505 mozilla::Preferences::AddBoolVarCache(&sWidgetPaintsBackground, |
|
2506 "android.widget_paints_background", |
|
2507 true); |
|
2508 } |
|
2509 |
|
2510 return sWidgetPaintsBackground; |
|
2511 } |
|
2512 |
|
2513 bool |
|
2514 nsWindow::NeedsPaint() |
|
2515 { |
|
2516 if (sCompositorPaused || FindTopLevel() != nsWindow::TopWindow() || !GetLayerManager(nullptr)) { |
|
2517 return false; |
|
2518 } |
|
2519 return nsIWidget::NeedsPaint(); |
|
2520 } |
|
2521 |
|
2522 CompositorParent* |
|
2523 nsWindow::NewCompositorParent(int aSurfaceWidth, int aSurfaceHeight) |
|
2524 { |
|
2525 return new CompositorParent(this, true, aSurfaceWidth, aSurfaceHeight); |
|
2526 } |
|
2527 |
|
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 } |
|
2542 |
|
2543 uint64_t |
|
2544 nsWindow::RootLayerTreeId() |
|
2545 { |
|
2546 MOZ_ASSERT(sCompositorParent); |
|
2547 return sCompositorParent->RootLayerTreeId(); |
|
2548 } |