widget/android/nsWindow.cpp

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

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

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

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

mercurial