Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
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 | } |