widget/android/nsWindow.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/widget/android/nsWindow.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,2548 @@
     1.4 +/* -*- Mode: c++; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
     1.5 + * This Source Code Form is subject to the terms of the Mozilla Public
     1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.8 +
     1.9 +#include <android/log.h>
    1.10 +#include <math.h>
    1.11 +#include <unistd.h>
    1.12 +
    1.13 +#include "mozilla/MiscEvents.h"
    1.14 +#include "mozilla/MouseEvents.h"
    1.15 +#include "mozilla/TextEvents.h"
    1.16 +#include "mozilla/TouchEvents.h"
    1.17 +
    1.18 +#include "mozilla/dom/ContentParent.h"
    1.19 +#include "mozilla/dom/ContentChild.h"
    1.20 +#include "mozilla/unused.h"
    1.21 +#include "mozilla/Preferences.h"
    1.22 +#include "mozilla/layers/RenderTrace.h"
    1.23 +#include <algorithm>
    1.24 +
    1.25 +using mozilla::dom::ContentParent;
    1.26 +using mozilla::dom::ContentChild;
    1.27 +using mozilla::unused;
    1.28 +
    1.29 +#include "nsAppShell.h"
    1.30 +#include "nsIdleService.h"
    1.31 +#include "nsWindow.h"
    1.32 +#include "nsIObserverService.h"
    1.33 +#include "nsFocusManager.h"
    1.34 +#include "nsIWidgetListener.h"
    1.35 +#include "nsViewManager.h"
    1.36 +
    1.37 +#include "nsRenderingContext.h"
    1.38 +#include "nsIDOMSimpleGestureEvent.h"
    1.39 +
    1.40 +#include "nsGkAtoms.h"
    1.41 +#include "nsWidgetsCID.h"
    1.42 +#include "nsGfxCIID.h"
    1.43 +
    1.44 +#include "gfxImageSurface.h"
    1.45 +#include "gfxContext.h"
    1.46 +
    1.47 +#include "Layers.h"
    1.48 +#include "mozilla/layers/LayerManagerComposite.h"
    1.49 +#include "mozilla/layers/AsyncCompositionManager.h"
    1.50 +#include "mozilla/layers/APZCTreeManager.h"
    1.51 +#include "GLContext.h"
    1.52 +#include "GLContextProvider.h"
    1.53 +#include "ScopedGLHelpers.h"
    1.54 +#include "mozilla/layers/CompositorOGL.h"
    1.55 +
    1.56 +#include "nsTArray.h"
    1.57 +
    1.58 +#include "AndroidBridge.h"
    1.59 +#include "AndroidBridgeUtilities.h"
    1.60 +#include "android_npapi.h"
    1.61 +
    1.62 +#include "imgIEncoder.h"
    1.63 +
    1.64 +#include "nsString.h"
    1.65 +#include "GeckoProfiler.h" // For PROFILER_LABEL
    1.66 +#include "nsIXULRuntime.h"
    1.67 +
    1.68 +using namespace mozilla;
    1.69 +using namespace mozilla::dom;
    1.70 +using namespace mozilla::widget;
    1.71 +using namespace mozilla::layers;
    1.72 +
    1.73 +NS_IMPL_ISUPPORTS_INHERITED0(nsWindow, nsBaseWidget)
    1.74 +
    1.75 +// The dimensions of the current android view
    1.76 +static gfxIntSize gAndroidBounds = gfxIntSize(0, 0);
    1.77 +static gfxIntSize gAndroidScreenBounds;
    1.78 +
    1.79 +#include "mozilla/layers/AsyncPanZoomController.h"
    1.80 +#include "mozilla/layers/CompositorChild.h"
    1.81 +#include "mozilla/layers/CompositorParent.h"
    1.82 +#include "mozilla/layers/LayerTransactionParent.h"
    1.83 +#include "mozilla/Mutex.h"
    1.84 +#include "nsThreadUtils.h"
    1.85 +
    1.86 +class ContentCreationNotifier;
    1.87 +static StaticRefPtr<ContentCreationNotifier> gContentCreationNotifier;
    1.88 +
    1.89 +// A helper class to send updates when content processes
    1.90 +// are created. Currently an update for the screen size is sent.
    1.91 +class ContentCreationNotifier MOZ_FINAL : public nsIObserver
    1.92 +{
    1.93 +    NS_DECL_ISUPPORTS
    1.94 +
    1.95 +    NS_IMETHOD Observe(nsISupports* aSubject,
    1.96 +                       const char* aTopic,
    1.97 +                       const char16_t* aData)
    1.98 +    {
    1.99 +        if (!strcmp(aTopic, "ipc:content-created")) {
   1.100 +            nsCOMPtr<nsIObserver> cpo = do_QueryInterface(aSubject);
   1.101 +            ContentParent* cp = static_cast<ContentParent*>(cpo.get());
   1.102 +            unused << cp->SendScreenSizeChanged(gAndroidScreenBounds);
   1.103 +        } else if (!strcmp(aTopic, "xpcom-shutdown")) {
   1.104 +            nsCOMPtr<nsIObserverService>
   1.105 +                obs(do_GetService("@mozilla.org/observer-service;1"));
   1.106 +            if (obs) {
   1.107 +                obs->RemoveObserver(static_cast<nsIObserver*>(this),
   1.108 +                                    "xpcom-shutdown");
   1.109 +                obs->RemoveObserver(static_cast<nsIObserver*>(this),
   1.110 +                                    "ipc:content-created");
   1.111 +            }
   1.112 +            gContentCreationNotifier = nullptr;
   1.113 +        }
   1.114 +
   1.115 +        return NS_OK;
   1.116 +    }
   1.117 +};
   1.118 +
   1.119 +NS_IMPL_ISUPPORTS(ContentCreationNotifier,
   1.120 +                  nsIObserver)
   1.121 +
   1.122 +static bool gMenu;
   1.123 +static bool gMenuConsumed;
   1.124 +
   1.125 +// All the toplevel windows that have been created; these are in
   1.126 +// stacking order, so the window at gAndroidBounds[0] is the topmost
   1.127 +// one.
   1.128 +static nsTArray<nsWindow*> gTopLevelWindows;
   1.129 +
   1.130 +static bool sFailedToCreateGLContext = false;
   1.131 +
   1.132 +// Multitouch swipe thresholds in inches
   1.133 +static const double SWIPE_MAX_PINCH_DELTA_INCHES = 0.4;
   1.134 +static const double SWIPE_MIN_DISTANCE_INCHES = 0.6;
   1.135 +
   1.136 +nsWindow*
   1.137 +nsWindow::TopWindow()
   1.138 +{
   1.139 +    if (!gTopLevelWindows.IsEmpty())
   1.140 +        return gTopLevelWindows[0];
   1.141 +    return nullptr;
   1.142 +}
   1.143 +
   1.144 +void
   1.145 +nsWindow::LogWindow(nsWindow *win, int index, int indent)
   1.146 +{
   1.147 +#if defined(DEBUG) || defined(FORCE_ALOG)
   1.148 +    char spaces[] = "                    ";
   1.149 +    spaces[indent < 20 ? indent : 20] = 0;
   1.150 +    ALOG("%s [% 2d] 0x%08x [parent 0x%08x] [% 3d,% 3dx% 3d,% 3d] vis %d type %d",
   1.151 +         spaces, index, (intptr_t)win, (intptr_t)win->mParent,
   1.152 +         win->mBounds.x, win->mBounds.y,
   1.153 +         win->mBounds.width, win->mBounds.height,
   1.154 +         win->mIsVisible, win->mWindowType);
   1.155 +#endif
   1.156 +}
   1.157 +
   1.158 +void
   1.159 +nsWindow::DumpWindows()
   1.160 +{
   1.161 +    DumpWindows(gTopLevelWindows);
   1.162 +}
   1.163 +
   1.164 +void
   1.165 +nsWindow::DumpWindows(const nsTArray<nsWindow*>& wins, int indent)
   1.166 +{
   1.167 +    for (uint32_t i = 0; i < wins.Length(); ++i) {
   1.168 +        nsWindow *w = wins[i];
   1.169 +        LogWindow(w, i, indent);
   1.170 +        DumpWindows(w->mChildren, indent+1);
   1.171 +    }
   1.172 +}
   1.173 +
   1.174 +nsWindow::nsWindow() :
   1.175 +    mIsVisible(false),
   1.176 +    mParent(nullptr),
   1.177 +    mFocus(nullptr),
   1.178 +    mIMEComposing(false),
   1.179 +    mIMEMaskSelectionUpdate(false),
   1.180 +    mIMEMaskTextUpdate(false),
   1.181 +    mIMEMaskEventsCount(1), // Mask IME events since there's no focus yet
   1.182 +    mIMERanges(new TextRangeArray()),
   1.183 +    mIMEUpdatingContext(false),
   1.184 +    mIMESelectionChanged(false)
   1.185 +{
   1.186 +}
   1.187 +
   1.188 +nsWindow::~nsWindow()
   1.189 +{
   1.190 +    gTopLevelWindows.RemoveElement(this);
   1.191 +    nsWindow *top = FindTopLevel();
   1.192 +    if (top->mFocus == this)
   1.193 +        top->mFocus = nullptr;
   1.194 +    ALOG("nsWindow %p destructor", (void*)this);
   1.195 +    if (mLayerManager == sLayerManager) {
   1.196 +        // If this window was the one that created the global OMTC layer manager
   1.197 +        // and compositor, then we should null those out.
   1.198 +        SetCompositor(nullptr, nullptr, nullptr);
   1.199 +    }
   1.200 +}
   1.201 +
   1.202 +bool
   1.203 +nsWindow::IsTopLevel()
   1.204 +{
   1.205 +    return mWindowType == eWindowType_toplevel ||
   1.206 +        mWindowType == eWindowType_dialog ||
   1.207 +        mWindowType == eWindowType_invisible;
   1.208 +}
   1.209 +
   1.210 +NS_IMETHODIMP
   1.211 +nsWindow::Create(nsIWidget *aParent,
   1.212 +                 nsNativeWidget aNativeParent,
   1.213 +                 const nsIntRect &aRect,
   1.214 +                 nsDeviceContext *aContext,
   1.215 +                 nsWidgetInitData *aInitData)
   1.216 +{
   1.217 +    ALOG("nsWindow[%p]::Create %p [%d %d %d %d]", (void*)this, (void*)aParent, aRect.x, aRect.y, aRect.width, aRect.height);
   1.218 +    nsWindow *parent = (nsWindow*) aParent;
   1.219 +    if (aNativeParent) {
   1.220 +        if (parent) {
   1.221 +            ALOG("Ignoring native parent on Android window [%p], since parent was specified (%p %p)", (void*)this, (void*)aNativeParent, (void*)aParent);
   1.222 +        } else {
   1.223 +            parent = (nsWindow*) aNativeParent;
   1.224 +        }
   1.225 +    }
   1.226 +
   1.227 +    mBounds = aRect;
   1.228 +
   1.229 +    // for toplevel windows, bounds are fixed to full screen size
   1.230 +    if (!parent) {
   1.231 +        mBounds.x = 0;
   1.232 +        mBounds.y = 0;
   1.233 +        mBounds.width = gAndroidBounds.width;
   1.234 +        mBounds.height = gAndroidBounds.height;
   1.235 +    }
   1.236 +
   1.237 +    BaseCreate(nullptr, mBounds, aContext, aInitData);
   1.238 +
   1.239 +    NS_ASSERTION(IsTopLevel() || parent, "non top level windowdoesn't have a parent!");
   1.240 +
   1.241 +    if (IsTopLevel()) {
   1.242 +        gTopLevelWindows.AppendElement(this);
   1.243 +    }
   1.244 +
   1.245 +    if (parent) {
   1.246 +        parent->mChildren.AppendElement(this);
   1.247 +        mParent = parent;
   1.248 +    }
   1.249 +
   1.250 +#ifdef DEBUG_ANDROID_WIDGET
   1.251 +    DumpWindows();
   1.252 +#endif
   1.253 +
   1.254 +    return NS_OK;
   1.255 +}
   1.256 +
   1.257 +NS_IMETHODIMP
   1.258 +nsWindow::Destroy(void)
   1.259 +{
   1.260 +    nsBaseWidget::mOnDestroyCalled = true;
   1.261 +
   1.262 +    while (mChildren.Length()) {
   1.263 +        // why do we still have children?
   1.264 +        ALOG("### Warning: Destroying window %p and reparenting child %p to null!", (void*)this, (void*)mChildren[0]);
   1.265 +        mChildren[0]->SetParent(nullptr);
   1.266 +    }
   1.267 +
   1.268 +    if (IsTopLevel())
   1.269 +        gTopLevelWindows.RemoveElement(this);
   1.270 +
   1.271 +    SetParent(nullptr);
   1.272 +
   1.273 +    nsBaseWidget::OnDestroy();
   1.274 +
   1.275 +#ifdef DEBUG_ANDROID_WIDGET
   1.276 +    DumpWindows();
   1.277 +#endif
   1.278 +
   1.279 +    return NS_OK;
   1.280 +}
   1.281 +
   1.282 +NS_IMETHODIMP
   1.283 +nsWindow::ConfigureChildren(const nsTArray<nsIWidget::Configuration>& config)
   1.284 +{
   1.285 +    for (uint32_t i = 0; i < config.Length(); ++i) {
   1.286 +        nsWindow *childWin = (nsWindow*) config[i].mChild;
   1.287 +        childWin->Resize(config[i].mBounds.x,
   1.288 +                         config[i].mBounds.y,
   1.289 +                         config[i].mBounds.width,
   1.290 +                         config[i].mBounds.height,
   1.291 +                         false);
   1.292 +    }
   1.293 +
   1.294 +    return NS_OK;
   1.295 +}
   1.296 +
   1.297 +void
   1.298 +nsWindow::RedrawAll()
   1.299 +{
   1.300 +    if (mFocus && mFocus->mWidgetListener) {
   1.301 +        mFocus->mWidgetListener->RequestRepaint();
   1.302 +    }
   1.303 +}
   1.304 +
   1.305 +NS_IMETHODIMP
   1.306 +nsWindow::SetParent(nsIWidget *aNewParent)
   1.307 +{
   1.308 +    if ((nsIWidget*)mParent == aNewParent)
   1.309 +        return NS_OK;
   1.310 +
   1.311 +    // If we had a parent before, remove ourselves from its list of
   1.312 +    // children.
   1.313 +    if (mParent)
   1.314 +        mParent->mChildren.RemoveElement(this);
   1.315 +
   1.316 +    mParent = (nsWindow*)aNewParent;
   1.317 +
   1.318 +    if (mParent)
   1.319 +        mParent->mChildren.AppendElement(this);
   1.320 +
   1.321 +    // if we are now in the toplevel window's hierarchy, schedule a redraw
   1.322 +    if (FindTopLevel() == nsWindow::TopWindow())
   1.323 +        RedrawAll();
   1.324 +
   1.325 +    return NS_OK;
   1.326 +}
   1.327 +
   1.328 +NS_IMETHODIMP
   1.329 +nsWindow::ReparentNativeWidget(nsIWidget *aNewParent)
   1.330 +{
   1.331 +    NS_PRECONDITION(aNewParent, "");
   1.332 +    return NS_OK;
   1.333 +}
   1.334 +
   1.335 +nsIWidget*
   1.336 +nsWindow::GetParent()
   1.337 +{
   1.338 +    return mParent;
   1.339 +}
   1.340 +
   1.341 +float
   1.342 +nsWindow::GetDPI()
   1.343 +{
   1.344 +    if (AndroidBridge::Bridge())
   1.345 +        return AndroidBridge::Bridge()->GetDPI();
   1.346 +    return 160.0f;
   1.347 +}
   1.348 +
   1.349 +double
   1.350 +nsWindow::GetDefaultScaleInternal()
   1.351 +{
   1.352 +    static double density = 0.0;
   1.353 +
   1.354 +    if (density != 0.0) {
   1.355 +        return density;
   1.356 +    }
   1.357 +
   1.358 +    density = mozilla::widget::android::GeckoAppShell::GetDensity();
   1.359 +
   1.360 +    if (!density) {
   1.361 +        density = 1.0;
   1.362 +    }
   1.363 +
   1.364 +    return density;
   1.365 +}
   1.366 +
   1.367 +NS_IMETHODIMP
   1.368 +nsWindow::Show(bool aState)
   1.369 +{
   1.370 +    ALOG("nsWindow[%p]::Show %d", (void*)this, aState);
   1.371 +
   1.372 +    if (mWindowType == eWindowType_invisible) {
   1.373 +        ALOG("trying to show invisible window! ignoring..");
   1.374 +        return NS_ERROR_FAILURE;
   1.375 +    }
   1.376 +
   1.377 +    if (aState == mIsVisible)
   1.378 +        return NS_OK;
   1.379 +
   1.380 +    mIsVisible = aState;
   1.381 +
   1.382 +    if (IsTopLevel()) {
   1.383 +        // XXX should we bring this to the front when it's shown,
   1.384 +        // if it's a toplevel widget?
   1.385 +
   1.386 +        // XXX we should synthesize a NS_MOUSE_EXIT (for old top
   1.387 +        // window)/NS_MOUSE_ENTER (for new top window) since we need
   1.388 +        // to pretend that the top window always has focus.  Not sure
   1.389 +        // if Show() is the right place to do this, though.
   1.390 +
   1.391 +        if (aState) {
   1.392 +            // It just became visible, so send a resize update if necessary
   1.393 +            // and bring it to the front.
   1.394 +            Resize(0, 0, gAndroidBounds.width, gAndroidBounds.height, false);
   1.395 +            BringToFront();
   1.396 +        } else if (nsWindow::TopWindow() == this) {
   1.397 +            // find the next visible window to show
   1.398 +            unsigned int i;
   1.399 +            for (i = 1; i < gTopLevelWindows.Length(); i++) {
   1.400 +                nsWindow *win = gTopLevelWindows[i];
   1.401 +                if (!win->mIsVisible)
   1.402 +                    continue;
   1.403 +
   1.404 +                win->BringToFront();
   1.405 +                break;
   1.406 +            }
   1.407 +        }
   1.408 +    } else if (FindTopLevel() == nsWindow::TopWindow()) {
   1.409 +        RedrawAll();
   1.410 +    }
   1.411 +
   1.412 +#ifdef DEBUG_ANDROID_WIDGET
   1.413 +    DumpWindows();
   1.414 +#endif
   1.415 +
   1.416 +    return NS_OK;
   1.417 +}
   1.418 +
   1.419 +NS_IMETHODIMP
   1.420 +nsWindow::SetModal(bool aState)
   1.421 +{
   1.422 +    ALOG("nsWindow[%p]::SetModal %d ignored", (void*)this, aState);
   1.423 +
   1.424 +    return NS_OK;
   1.425 +}
   1.426 +
   1.427 +bool
   1.428 +nsWindow::IsVisible() const
   1.429 +{
   1.430 +    return mIsVisible;
   1.431 +}
   1.432 +
   1.433 +NS_IMETHODIMP
   1.434 +nsWindow::ConstrainPosition(bool aAllowSlop,
   1.435 +                            int32_t *aX,
   1.436 +                            int32_t *aY)
   1.437 +{
   1.438 +    ALOG("nsWindow[%p]::ConstrainPosition %d [%d %d]", (void*)this, aAllowSlop, *aX, *aY);
   1.439 +
   1.440 +    // constrain toplevel windows; children we don't care about
   1.441 +    if (IsTopLevel()) {
   1.442 +        *aX = 0;
   1.443 +        *aY = 0;
   1.444 +    }
   1.445 +
   1.446 +    return NS_OK;
   1.447 +}
   1.448 +
   1.449 +NS_IMETHODIMP
   1.450 +nsWindow::Move(double aX,
   1.451 +               double aY)
   1.452 +{
   1.453 +    if (IsTopLevel())
   1.454 +        return NS_OK;
   1.455 +
   1.456 +    return Resize(aX,
   1.457 +                  aY,
   1.458 +                  mBounds.width,
   1.459 +                  mBounds.height,
   1.460 +                  true);
   1.461 +}
   1.462 +
   1.463 +NS_IMETHODIMP
   1.464 +nsWindow::Resize(double aWidth,
   1.465 +                 double aHeight,
   1.466 +                 bool aRepaint)
   1.467 +{
   1.468 +    return Resize(mBounds.x,
   1.469 +                  mBounds.y,
   1.470 +                  aWidth,
   1.471 +                  aHeight,
   1.472 +                  aRepaint);
   1.473 +}
   1.474 +
   1.475 +NS_IMETHODIMP
   1.476 +nsWindow::Resize(double aX,
   1.477 +                 double aY,
   1.478 +                 double aWidth,
   1.479 +                 double aHeight,
   1.480 +                 bool aRepaint)
   1.481 +{
   1.482 +    ALOG("nsWindow[%p]::Resize [%f %f %f %f] (repaint %d)", (void*)this, aX, aY, aWidth, aHeight, aRepaint);
   1.483 +
   1.484 +    bool needSizeDispatch = aWidth != mBounds.width || aHeight != mBounds.height;
   1.485 +
   1.486 +    mBounds.x = NSToIntRound(aX);
   1.487 +    mBounds.y = NSToIntRound(aY);
   1.488 +    mBounds.width = NSToIntRound(aWidth);
   1.489 +    mBounds.height = NSToIntRound(aHeight);
   1.490 +
   1.491 +    if (needSizeDispatch)
   1.492 +        OnSizeChanged(gfxIntSize(aWidth, aHeight));
   1.493 +
   1.494 +    // Should we skip honoring aRepaint here?
   1.495 +    if (aRepaint && FindTopLevel() == nsWindow::TopWindow())
   1.496 +        RedrawAll();
   1.497 +
   1.498 +    return NS_OK;
   1.499 +}
   1.500 +
   1.501 +void
   1.502 +nsWindow::SetZIndex(int32_t aZIndex)
   1.503 +{
   1.504 +    ALOG("nsWindow[%p]::SetZIndex %d ignored", (void*)this, aZIndex);
   1.505 +}
   1.506 +
   1.507 +NS_IMETHODIMP
   1.508 +nsWindow::PlaceBehind(nsTopLevelWidgetZPlacement aPlacement,
   1.509 +                      nsIWidget *aWidget,
   1.510 +                      bool aActivate)
   1.511 +{
   1.512 +    return NS_OK;
   1.513 +}
   1.514 +
   1.515 +NS_IMETHODIMP
   1.516 +nsWindow::SetSizeMode(int32_t aMode)
   1.517 +{
   1.518 +    switch (aMode) {
   1.519 +        case nsSizeMode_Minimized:
   1.520 +            mozilla::widget::android::GeckoAppShell::MoveTaskToBack();
   1.521 +            break;
   1.522 +        case nsSizeMode_Fullscreen:
   1.523 +            MakeFullScreen(true);
   1.524 +            break;
   1.525 +    }
   1.526 +    return NS_OK;
   1.527 +}
   1.528 +
   1.529 +NS_IMETHODIMP
   1.530 +nsWindow::Enable(bool aState)
   1.531 +{
   1.532 +    ALOG("nsWindow[%p]::Enable %d ignored", (void*)this, aState);
   1.533 +    return NS_OK;
   1.534 +}
   1.535 +
   1.536 +bool
   1.537 +nsWindow::IsEnabled() const
   1.538 +{
   1.539 +    return true;
   1.540 +}
   1.541 +
   1.542 +NS_IMETHODIMP
   1.543 +nsWindow::Invalidate(const nsIntRect &aRect)
   1.544 +{
   1.545 +    AndroidGeckoEvent *event = AndroidGeckoEvent::MakeDrawEvent(aRect);
   1.546 +    nsAppShell::gAppShell->PostEvent(event);
   1.547 +    return NS_OK;
   1.548 +}
   1.549 +
   1.550 +nsWindow*
   1.551 +nsWindow::FindTopLevel()
   1.552 +{
   1.553 +    nsWindow *toplevel = this;
   1.554 +    while (toplevel) {
   1.555 +        if (toplevel->IsTopLevel())
   1.556 +            return toplevel;
   1.557 +
   1.558 +        toplevel = toplevel->mParent;
   1.559 +    }
   1.560 +
   1.561 +    ALOG("nsWindow::FindTopLevel(): couldn't find a toplevel or dialog window in this [%p] widget's hierarchy!", (void*)this);
   1.562 +    return this;
   1.563 +}
   1.564 +
   1.565 +NS_IMETHODIMP
   1.566 +nsWindow::SetFocus(bool aRaise)
   1.567 +{
   1.568 +    if (!aRaise) {
   1.569 +        ALOG("nsWindow::SetFocus: can't set focus without raising, ignoring aRaise = false!");
   1.570 +    }
   1.571 +
   1.572 +    nsWindow *top = FindTopLevel();
   1.573 +    top->mFocus = this;
   1.574 +    top->BringToFront();
   1.575 +
   1.576 +    return NS_OK;
   1.577 +}
   1.578 +
   1.579 +void
   1.580 +nsWindow::BringToFront()
   1.581 +{
   1.582 +    // If the window to be raised is the same as the currently raised one,
   1.583 +    // do nothing. We need to check the focus manager as well, as the first
   1.584 +    // window that is created will be first in the window list but won't yet
   1.585 +    // be focused.
   1.586 +    nsCOMPtr<nsIFocusManager> fm = do_GetService(FOCUSMANAGER_CONTRACTID);
   1.587 +    nsCOMPtr<nsIDOMWindow> existingTopWindow;
   1.588 +    fm->GetActiveWindow(getter_AddRefs(existingTopWindow));
   1.589 +    if (existingTopWindow && FindTopLevel() == nsWindow::TopWindow())
   1.590 +        return;
   1.591 +
   1.592 +    if (!IsTopLevel()) {
   1.593 +        FindTopLevel()->BringToFront();
   1.594 +        return;
   1.595 +    }
   1.596 +
   1.597 +    nsRefPtr<nsWindow> kungFuDeathGrip(this);
   1.598 +
   1.599 +    nsWindow *oldTop = nullptr;
   1.600 +    nsWindow *newTop = this;
   1.601 +    if (!gTopLevelWindows.IsEmpty())
   1.602 +        oldTop = gTopLevelWindows[0];
   1.603 +
   1.604 +    gTopLevelWindows.RemoveElement(this);
   1.605 +    gTopLevelWindows.InsertElementAt(0, this);
   1.606 +
   1.607 +    if (oldTop) {
   1.608 +      nsIWidgetListener* listener = oldTop->GetWidgetListener();
   1.609 +      if (listener) {
   1.610 +          listener->WindowDeactivated();
   1.611 +      }
   1.612 +    }
   1.613 +
   1.614 +    if (Destroyed()) {
   1.615 +        // somehow the deactivate event handler destroyed this window.
   1.616 +        // try to recover by grabbing the next window in line and activating
   1.617 +        // that instead
   1.618 +        if (gTopLevelWindows.IsEmpty())
   1.619 +            return;
   1.620 +        newTop = gTopLevelWindows[0];
   1.621 +    }
   1.622 +
   1.623 +    if (mWidgetListener) {
   1.624 +        mWidgetListener->WindowActivated();
   1.625 +    }
   1.626 +
   1.627 +    // force a window resize
   1.628 +    nsAppShell::gAppShell->ResendLastResizeEvent(newTop);
   1.629 +    RedrawAll();
   1.630 +}
   1.631 +
   1.632 +NS_IMETHODIMP
   1.633 +nsWindow::GetScreenBounds(nsIntRect &aRect)
   1.634 +{
   1.635 +    nsIntPoint p = WidgetToScreenOffset();
   1.636 +
   1.637 +    aRect.x = p.x;
   1.638 +    aRect.y = p.y;
   1.639 +    aRect.width = mBounds.width;
   1.640 +    aRect.height = mBounds.height;
   1.641 +    
   1.642 +    return NS_OK;
   1.643 +}
   1.644 +
   1.645 +nsIntPoint
   1.646 +nsWindow::WidgetToScreenOffset()
   1.647 +{
   1.648 +    nsIntPoint p(0, 0);
   1.649 +    nsWindow *w = this;
   1.650 +
   1.651 +    while (w && !w->IsTopLevel()) {
   1.652 +        p.x += w->mBounds.x;
   1.653 +        p.y += w->mBounds.y;
   1.654 +
   1.655 +        w = w->mParent;
   1.656 +    }
   1.657 +
   1.658 +    return p;
   1.659 +}
   1.660 +
   1.661 +NS_IMETHODIMP
   1.662 +nsWindow::DispatchEvent(WidgetGUIEvent* aEvent,
   1.663 +                        nsEventStatus &aStatus)
   1.664 +{
   1.665 +    aStatus = DispatchEvent(aEvent);
   1.666 +    return NS_OK;
   1.667 +}
   1.668 +
   1.669 +nsEventStatus
   1.670 +nsWindow::DispatchEvent(WidgetGUIEvent* aEvent)
   1.671 +{
   1.672 +    if (mWidgetListener) {
   1.673 +        nsEventStatus status = mWidgetListener->HandleEvent(aEvent, mUseAttachedEvents);
   1.674 +
   1.675 +        switch (aEvent->message) {
   1.676 +        case NS_COMPOSITION_START:
   1.677 +            MOZ_ASSERT(!mIMEComposing);
   1.678 +            mIMEComposing = true;
   1.679 +            break;
   1.680 +        case NS_COMPOSITION_END:
   1.681 +            MOZ_ASSERT(mIMEComposing);
   1.682 +            mIMEComposing = false;
   1.683 +            mIMEComposingText.Truncate();
   1.684 +            break;
   1.685 +        case NS_TEXT_TEXT:
   1.686 +            MOZ_ASSERT(mIMEComposing);
   1.687 +            mIMEComposingText = aEvent->AsTextEvent()->theText;
   1.688 +            break;
   1.689 +        }
   1.690 +        return status;
   1.691 +    }
   1.692 +    return nsEventStatus_eIgnore;
   1.693 +}
   1.694 +
   1.695 +NS_IMETHODIMP
   1.696 +nsWindow::MakeFullScreen(bool aFullScreen)
   1.697 +{
   1.698 +    mozilla::widget::android::GeckoAppShell::SetFullScreen(aFullScreen);
   1.699 +    return NS_OK;
   1.700 +}
   1.701 +
   1.702 +NS_IMETHODIMP
   1.703 +nsWindow::SetWindowClass(const nsAString& xulWinType)
   1.704 +{
   1.705 +    return NS_OK;
   1.706 +}
   1.707 +
   1.708 +mozilla::layers::LayerManager*
   1.709 +nsWindow::GetLayerManager(PLayerTransactionChild*, LayersBackend, LayerManagerPersistence,
   1.710 +                          bool* aAllowRetaining)
   1.711 +{
   1.712 +    if (aAllowRetaining) {
   1.713 +        *aAllowRetaining = true;
   1.714 +    }
   1.715 +    if (mLayerManager) {
   1.716 +        return mLayerManager;
   1.717 +    }
   1.718 +    // for OMTC allow use of the single layer manager/compositor
   1.719 +    // shared across all windows
   1.720 +    if (ShouldUseOffMainThreadCompositing()) {
   1.721 +        return sLayerManager;
   1.722 +    }
   1.723 +    return nullptr;
   1.724 +}
   1.725 +
   1.726 +void
   1.727 +nsWindow::CreateLayerManager(int aCompositorWidth, int aCompositorHeight)
   1.728 +{
   1.729 +    if (mLayerManager) {
   1.730 +        return;
   1.731 +    }
   1.732 +
   1.733 +    nsWindow *topLevelWindow = FindTopLevel();
   1.734 +    if (!topLevelWindow || topLevelWindow->mWindowType == eWindowType_invisible) {
   1.735 +        // don't create a layer manager for an invisible top-level window
   1.736 +        return;
   1.737 +    }
   1.738 +
   1.739 +    mUseLayersAcceleration = ComputeShouldAccelerate(mUseLayersAcceleration);
   1.740 +
   1.741 +    if (ShouldUseOffMainThreadCompositing()) {
   1.742 +        if (sLayerManager) {
   1.743 +            return;
   1.744 +        }
   1.745 +        CreateCompositor(aCompositorWidth, aCompositorHeight);
   1.746 +        if (mLayerManager) {
   1.747 +            // for OMTC create a single layer manager and compositor that will be
   1.748 +            // used for all windows.
   1.749 +            SetCompositor(mLayerManager, mCompositorParent, mCompositorChild);
   1.750 +            sCompositorPaused = false;
   1.751 +            return;
   1.752 +        }
   1.753 +
   1.754 +        // If we get here, then off main thread compositing failed to initialize.
   1.755 +        sFailedToCreateGLContext = true;
   1.756 +    }
   1.757 +
   1.758 +    if (!mUseLayersAcceleration || sFailedToCreateGLContext) {
   1.759 +        printf_stderr(" -- creating basic, not accelerated\n");
   1.760 +        mLayerManager = CreateBasicLayerManager();
   1.761 +    }
   1.762 +}
   1.763 +
   1.764 +void
   1.765 +nsWindow::OnGlobalAndroidEvent(AndroidGeckoEvent *ae)
   1.766 +{
   1.767 +    nsWindow *win = TopWindow();
   1.768 +    if (!win)
   1.769 +        return;
   1.770 +
   1.771 +    switch (ae->Type()) {
   1.772 +        case AndroidGeckoEvent::FORCED_RESIZE:
   1.773 +            win->mBounds.width = 0;
   1.774 +            win->mBounds.height = 0;
   1.775 +            // also resize the children
   1.776 +            for (uint32_t i = 0; i < win->mChildren.Length(); i++) {
   1.777 +                win->mChildren[i]->mBounds.width = 0;
   1.778 +                win->mChildren[i]->mBounds.height = 0;
   1.779 +            }
   1.780 +        case AndroidGeckoEvent::SIZE_CHANGED: {
   1.781 +            const nsTArray<nsIntPoint>& points = ae->Points();
   1.782 +            NS_ASSERTION(points.Length() == 2, "Size changed does not have enough coordinates");
   1.783 +
   1.784 +            int nw = points[0].x;
   1.785 +            int nh = points[0].y;
   1.786 +
   1.787 +            if (ae->Type() == AndroidGeckoEvent::FORCED_RESIZE || nw != gAndroidBounds.width ||
   1.788 +                nh != gAndroidBounds.height) {
   1.789 +                gAndroidBounds.width = nw;
   1.790 +                gAndroidBounds.height = nh;
   1.791 +
   1.792 +                // tell all the windows about the new size
   1.793 +                for (size_t i = 0; i < gTopLevelWindows.Length(); ++i) {
   1.794 +                    if (gTopLevelWindows[i]->mIsVisible)
   1.795 +                        gTopLevelWindows[i]->Resize(gAndroidBounds.width,
   1.796 +                                                    gAndroidBounds.height,
   1.797 +                                                    false);
   1.798 +                }
   1.799 +            }
   1.800 +
   1.801 +            int newScreenWidth = points[1].x;
   1.802 +            int newScreenHeight = points[1].y;
   1.803 +
   1.804 +            if (newScreenWidth == gAndroidScreenBounds.width &&
   1.805 +                newScreenHeight == gAndroidScreenBounds.height)
   1.806 +                break;
   1.807 +
   1.808 +            gAndroidScreenBounds.width = newScreenWidth;
   1.809 +            gAndroidScreenBounds.height = newScreenHeight;
   1.810 +
   1.811 +            if (XRE_GetProcessType() != GeckoProcessType_Default ||
   1.812 +                !BrowserTabsRemote()) {
   1.813 +                break;
   1.814 +            }
   1.815 +
   1.816 +            // Tell the content process the new screen size.
   1.817 +            nsTArray<ContentParent*> cplist;
   1.818 +            ContentParent::GetAll(cplist);
   1.819 +            for (uint32_t i = 0; i < cplist.Length(); ++i)
   1.820 +                unused << cplist[i]->SendScreenSizeChanged(gAndroidScreenBounds);
   1.821 +
   1.822 +            if (gContentCreationNotifier)
   1.823 +                break;
   1.824 +
   1.825 +            // If the content process is not created yet, wait until it's
   1.826 +            // created and then tell it the screen size.
   1.827 +            nsCOMPtr<nsIObserverService> obs = do_GetService("@mozilla.org/observer-service;1");
   1.828 +            if (!obs)
   1.829 +                break;
   1.830 +
   1.831 +            nsRefPtr<ContentCreationNotifier> notifier = new ContentCreationNotifier;
   1.832 +            if (NS_SUCCEEDED(obs->AddObserver(notifier, "ipc:content-created", false))) {
   1.833 +                if (NS_SUCCEEDED(obs->AddObserver(notifier, "xpcom-shutdown", false)))
   1.834 +                    gContentCreationNotifier = notifier;
   1.835 +                else
   1.836 +                    obs->RemoveObserver(notifier, "ipc:content-created");
   1.837 +            }
   1.838 +            break;
   1.839 +        }
   1.840 +
   1.841 +        case AndroidGeckoEvent::MOTION_EVENT: {
   1.842 +            win->UserActivity();
   1.843 +            if (!gTopLevelWindows.IsEmpty()) {
   1.844 +                nsIntPoint pt(0,0);
   1.845 +                const nsTArray<nsIntPoint>& points = ae->Points();
   1.846 +                if (points.Length() > 0) {
   1.847 +                    pt = points[0];
   1.848 +                }
   1.849 +                pt.x = clamped(pt.x, 0, std::max(gAndroidBounds.width - 1, 0));
   1.850 +                pt.y = clamped(pt.y, 0, std::max(gAndroidBounds.height - 1, 0));
   1.851 +                nsWindow *target = win->FindWindowForPoint(pt);
   1.852 +#if 0
   1.853 +                ALOG("MOTION_EVENT %f,%f -> %p (visible: %d children: %d)", pt.x, pt.y, (void*)target,
   1.854 +                     target ? target->mIsVisible : 0,
   1.855 +                     target ? target->mChildren.Length() : 0);
   1.856 +
   1.857 +                DumpWindows();
   1.858 +#endif
   1.859 +                if (target) {
   1.860 +                    bool preventDefaultActions = target->OnMultitouchEvent(ae);
   1.861 +                    if (!preventDefaultActions && ae->Count() < 2)
   1.862 +                        target->OnMouseEvent(ae);
   1.863 +                }
   1.864 +            }
   1.865 +            break;
   1.866 +        }
   1.867 +
   1.868 +        case AndroidGeckoEvent::NATIVE_GESTURE_EVENT: {
   1.869 +            nsIntPoint pt(0,0);
   1.870 +            const nsTArray<nsIntPoint>& points = ae->Points();
   1.871 +            if (points.Length() > 0) {
   1.872 +                pt = points[0];
   1.873 +            }
   1.874 +            pt.x = clamped(pt.x, 0, std::max(gAndroidBounds.width - 1, 0));
   1.875 +            pt.y = clamped(pt.y, 0, std::max(gAndroidBounds.height - 1, 0));
   1.876 +            nsWindow *target = win->FindWindowForPoint(pt);
   1.877 +
   1.878 +            target->OnNativeGestureEvent(ae);
   1.879 +            break;
   1.880 +        }
   1.881 +
   1.882 +        case AndroidGeckoEvent::KEY_EVENT:
   1.883 +            win->UserActivity();
   1.884 +            if (win->mFocus)
   1.885 +                win->mFocus->OnKeyEvent(ae);
   1.886 +            break;
   1.887 +
   1.888 +        case AndroidGeckoEvent::DRAW:
   1.889 +            layers::renderTraceEventStart("Global draw start", "414141");
   1.890 +            win->OnDraw(ae);
   1.891 +            layers::renderTraceEventEnd("414141");
   1.892 +            break;
   1.893 +
   1.894 +        case AndroidGeckoEvent::IME_EVENT:
   1.895 +            win->UserActivity();
   1.896 +            if (win->mFocus) {
   1.897 +                win->mFocus->OnIMEEvent(ae);
   1.898 +            } else {
   1.899 +                NS_WARNING("Sending unexpected IME event to top window");
   1.900 +                win->OnIMEEvent(ae);
   1.901 +            }
   1.902 +            break;
   1.903 +
   1.904 +        case AndroidGeckoEvent::IME_KEY_EVENT:
   1.905 +            // Keys synthesized by Java IME code are saved in the mIMEKeyEvents
   1.906 +            // array until the next IME_REPLACE_TEXT event, at which point
   1.907 +            // these keys are dispatched in sequence.
   1.908 +            if (win->mFocus) {
   1.909 +                win->mFocus->mIMEKeyEvents.AppendElement(*ae);
   1.910 +            }
   1.911 +            break;
   1.912 +
   1.913 +        case AndroidGeckoEvent::COMPOSITOR_CREATE:
   1.914 +            win->CreateLayerManager(ae->Width(), ae->Height());
   1.915 +            break;
   1.916 +
   1.917 +        case AndroidGeckoEvent::COMPOSITOR_PAUSE:
   1.918 +            // The compositor gets paused when the app is about to go into the
   1.919 +            // background. While the compositor is paused, we need to ensure that
   1.920 +            // no layer tree updates (from draw events) occur, since the compositor
   1.921 +            // cannot make a GL context current in order to process updates.
   1.922 +            if (sCompositorChild) {
   1.923 +                sCompositorChild->SendPause();
   1.924 +            }
   1.925 +            sCompositorPaused = true;
   1.926 +            break;
   1.927 +
   1.928 +        case AndroidGeckoEvent::COMPOSITOR_RESUME:
   1.929 +            // When we receive this, the compositor has already been told to
   1.930 +            // resume. (It turns out that waiting till we reach here to tell
   1.931 +            // the compositor to resume takes too long, resulting in a black
   1.932 +            // flash.) This means it's now safe for layer updates to occur.
   1.933 +            // Since we might have prevented one or more draw events from
   1.934 +            // occurring while the compositor was paused, we need to schedule
   1.935 +            // a draw event now.
   1.936 +            if (!sCompositorPaused) {
   1.937 +                win->RedrawAll();
   1.938 +            }
   1.939 +            break;
   1.940 +    }
   1.941 +}
   1.942 +
   1.943 +void
   1.944 +nsWindow::OnAndroidEvent(AndroidGeckoEvent *ae)
   1.945 +{
   1.946 +    switch (ae->Type()) {
   1.947 +        case AndroidGeckoEvent::DRAW:
   1.948 +            OnDraw(ae);
   1.949 +            break;
   1.950 +
   1.951 +        default:
   1.952 +            ALOG("Window got targetted android event type %d, but didn't handle!", ae->Type());
   1.953 +            break;
   1.954 +    }
   1.955 +}
   1.956 +
   1.957 +bool
   1.958 +nsWindow::DrawTo(gfxASurface *targetSurface)
   1.959 +{
   1.960 +    nsIntRect boundsRect(0, 0, mBounds.width, mBounds.height);
   1.961 +    return DrawTo(targetSurface, boundsRect);
   1.962 +}
   1.963 +
   1.964 +bool
   1.965 +nsWindow::DrawTo(gfxASurface *targetSurface, const nsIntRect &invalidRect)
   1.966 +{
   1.967 +    mozilla::layers::RenderTraceScope trace("DrawTo", "717171");
   1.968 +    if (!mIsVisible || !mWidgetListener || !GetLayerManager(nullptr))
   1.969 +        return false;
   1.970 +
   1.971 +    nsRefPtr<nsWindow> kungFuDeathGrip(this);
   1.972 +    nsIntRect boundsRect(0, 0, mBounds.width, mBounds.height);
   1.973 +
   1.974 +    // Figure out if any of our children cover this widget completely
   1.975 +    int32_t coveringChildIndex = -1;
   1.976 +    for (uint32_t i = 0; i < mChildren.Length(); ++i) {
   1.977 +        if (mChildren[i]->mBounds.IsEmpty())
   1.978 +            continue;
   1.979 +
   1.980 +        if (mChildren[i]->mBounds.Contains(boundsRect)) {
   1.981 +            coveringChildIndex = int32_t(i);
   1.982 +        }
   1.983 +    }
   1.984 +
   1.985 +    // If we have no covering child, then we need to render this.
   1.986 +    if (coveringChildIndex == -1) {
   1.987 +        nsIntRegion region = invalidRect;
   1.988 +
   1.989 +        mWidgetListener->WillPaintWindow(this);
   1.990 +
   1.991 +        switch (GetLayerManager(nullptr)->GetBackendType()) {
   1.992 +            case mozilla::layers::LayersBackend::LAYERS_BASIC: {
   1.993 +
   1.994 +                nsRefPtr<gfxContext> ctx = new gfxContext(targetSurface);
   1.995 +
   1.996 +                {
   1.997 +                    mozilla::layers::RenderTraceScope trace2("Basic DrawTo", "727272");
   1.998 +                    AutoLayerManagerSetup
   1.999 +                      setupLayerManager(this, ctx, mozilla::layers::BufferMode::BUFFER_NONE);
  1.1000 +
  1.1001 +                    mWidgetListener->PaintWindow(this, region);
  1.1002 +                }
  1.1003 +                break;
  1.1004 +            }
  1.1005 +
  1.1006 +            case mozilla::layers::LayersBackend::LAYERS_CLIENT: {
  1.1007 +                mWidgetListener->PaintWindow(this, region);
  1.1008 +                break;
  1.1009 +            }
  1.1010 +
  1.1011 +            default:
  1.1012 +                NS_ERROR("Invalid layer manager");
  1.1013 +        }
  1.1014 +
  1.1015 +        mWidgetListener->DidPaintWindow();
  1.1016 +
  1.1017 +        // We had no covering child, so make sure we draw all the children,
  1.1018 +        // starting from index 0.
  1.1019 +        coveringChildIndex = 0;
  1.1020 +    }
  1.1021 +
  1.1022 +    gfxPoint offset;
  1.1023 +
  1.1024 +    if (targetSurface)
  1.1025 +        offset = targetSurface->GetDeviceOffset();
  1.1026 +
  1.1027 +    for (uint32_t i = coveringChildIndex; i < mChildren.Length(); ++i) {
  1.1028 +        if (mChildren[i]->mBounds.IsEmpty() ||
  1.1029 +            !mChildren[i]->mBounds.Intersects(boundsRect)) {
  1.1030 +            continue;
  1.1031 +        }
  1.1032 +
  1.1033 +        if (targetSurface)
  1.1034 +            targetSurface->SetDeviceOffset(offset + gfxPoint(mChildren[i]->mBounds.x,
  1.1035 +                                                             mChildren[i]->mBounds.y));
  1.1036 +
  1.1037 +        bool ok = mChildren[i]->DrawTo(targetSurface, invalidRect);
  1.1038 +
  1.1039 +        if (!ok) {
  1.1040 +            ALOG("nsWindow[%p]::DrawTo child %d[%p] returned FALSE!", (void*) this, i, (void*)mChildren[i]);
  1.1041 +        }
  1.1042 +    }
  1.1043 +
  1.1044 +    if (targetSurface)
  1.1045 +        targetSurface->SetDeviceOffset(offset);
  1.1046 +
  1.1047 +    return true;
  1.1048 +}
  1.1049 +
  1.1050 +void
  1.1051 +nsWindow::OnDraw(AndroidGeckoEvent *ae)
  1.1052 +{
  1.1053 +    if (!IsTopLevel()) {
  1.1054 +        ALOG("##### redraw for window %p, which is not a toplevel window -- sending to toplevel!", (void*) this);
  1.1055 +        DumpWindows();
  1.1056 +        return;
  1.1057 +    }
  1.1058 +
  1.1059 +    if (!mIsVisible) {
  1.1060 +        ALOG("##### redraw for window %p, which is not visible -- ignoring!", (void*) this);
  1.1061 +        DumpWindows();
  1.1062 +        return;
  1.1063 +    }
  1.1064 +
  1.1065 +    nsRefPtr<nsWindow> kungFuDeathGrip(this);
  1.1066 +
  1.1067 +    AutoLocalJNIFrame jniFrame;
  1.1068 +
  1.1069 +    // We're paused, or we haven't been given a window-size yet, so do nothing
  1.1070 +    if (sCompositorPaused || gAndroidBounds.width <= 0 || gAndroidBounds.height <= 0) {
  1.1071 +        return;
  1.1072 +    }
  1.1073 +
  1.1074 +    int bytesPerPixel = 2;
  1.1075 +    gfxImageFormat format = gfxImageFormat::RGB16_565;
  1.1076 +    if (AndroidBridge::Bridge()->GetScreenDepth() == 24) {
  1.1077 +        bytesPerPixel = 4;
  1.1078 +        format = gfxImageFormat::RGB24;
  1.1079 +    }
  1.1080 +
  1.1081 +    layers::renderTraceEventStart("Get surface", "424545");
  1.1082 +    static unsigned char bits2[32 * 32 * 4];
  1.1083 +    nsRefPtr<gfxImageSurface> targetSurface =
  1.1084 +        new gfxImageSurface(bits2, gfxIntSize(32, 32), 32 * bytesPerPixel, format);
  1.1085 +    layers::renderTraceEventEnd("Get surface", "424545");
  1.1086 +
  1.1087 +    layers::renderTraceEventStart("Widget draw to", "434646");
  1.1088 +    if (targetSurface->CairoStatus()) {
  1.1089 +        ALOG("### Failed to create a valid surface from the bitmap");
  1.1090 +    } else {
  1.1091 +        DrawTo(targetSurface, ae->Rect());
  1.1092 +    }
  1.1093 +    layers::renderTraceEventEnd("Widget draw to", "434646");
  1.1094 +}
  1.1095 +
  1.1096 +void
  1.1097 +nsWindow::OnSizeChanged(const gfxIntSize& aSize)
  1.1098 +{
  1.1099 +    ALOG("nsWindow: %p OnSizeChanged [%d %d]", (void*)this, aSize.width, aSize.height);
  1.1100 +
  1.1101 +    mBounds.width = aSize.width;
  1.1102 +    mBounds.height = aSize.height;
  1.1103 +
  1.1104 +    if (mWidgetListener) {
  1.1105 +        mWidgetListener->WindowResized(this, aSize.width, aSize.height);
  1.1106 +    }
  1.1107 +}
  1.1108 +
  1.1109 +void
  1.1110 +nsWindow::InitEvent(WidgetGUIEvent& event, nsIntPoint* aPoint)
  1.1111 +{
  1.1112 +    if (aPoint) {
  1.1113 +        event.refPoint.x = aPoint->x;
  1.1114 +        event.refPoint.y = aPoint->y;
  1.1115 +    } else {
  1.1116 +        event.refPoint.x = 0;
  1.1117 +        event.refPoint.y = 0;
  1.1118 +    }
  1.1119 +
  1.1120 +    event.time = PR_Now() / 1000;
  1.1121 +}
  1.1122 +
  1.1123 +gfxIntSize
  1.1124 +nsWindow::GetAndroidScreenBounds()
  1.1125 +{
  1.1126 +    if (XRE_GetProcessType() == GeckoProcessType_Content) {
  1.1127 +        return ContentChild::GetSingleton()->GetScreenSize();
  1.1128 +    }
  1.1129 +    return gAndroidScreenBounds;
  1.1130 +}
  1.1131 +
  1.1132 +void *
  1.1133 +nsWindow::GetNativeData(uint32_t aDataType)
  1.1134 +{
  1.1135 +    switch (aDataType) {
  1.1136 +        // used by GLContextProviderEGL, nullptr is EGL_DEFAULT_DISPLAY
  1.1137 +        case NS_NATIVE_DISPLAY:
  1.1138 +            return nullptr;
  1.1139 +
  1.1140 +        case NS_NATIVE_WIDGET:
  1.1141 +            return (void *) this;
  1.1142 +    }
  1.1143 +
  1.1144 +    return nullptr;
  1.1145 +}
  1.1146 +
  1.1147 +void
  1.1148 +nsWindow::OnMouseEvent(AndroidGeckoEvent *ae)
  1.1149 +{
  1.1150 +    nsRefPtr<nsWindow> kungFuDeathGrip(this);
  1.1151 +
  1.1152 +    WidgetMouseEvent event = ae->MakeMouseEvent(this);
  1.1153 +    if (event.message == NS_EVENT_NULL) {
  1.1154 +        // invalid event type, abort
  1.1155 +        return;
  1.1156 +    }
  1.1157 +
  1.1158 +    // XXX add the double-click handling logic here
  1.1159 +    DispatchEvent(&event);
  1.1160 +}
  1.1161 +
  1.1162 +bool nsWindow::OnMultitouchEvent(AndroidGeckoEvent *ae)
  1.1163 +{
  1.1164 +    nsRefPtr<nsWindow> kungFuDeathGrip(this);
  1.1165 +
  1.1166 +    // End any composition in progress in case the touch event listener
  1.1167 +    // modifies the input field value (see bug 856155)
  1.1168 +    RemoveIMEComposition();
  1.1169 +
  1.1170 +    // This is set to true once we have called SetPreventPanning() exactly
  1.1171 +    // once for a given sequence of touch events. It is reset on the start
  1.1172 +    // of the next sequence.
  1.1173 +    static bool sDefaultPreventedNotified = false;
  1.1174 +    static bool sLastWasDownEvent = false;
  1.1175 +
  1.1176 +    bool preventDefaultActions = false;
  1.1177 +    bool isDownEvent = false;
  1.1178 +
  1.1179 +    WidgetTouchEvent event = ae->MakeTouchEvent(this);
  1.1180 +    if (event.message != NS_EVENT_NULL) {
  1.1181 +        nsEventStatus status;
  1.1182 +        DispatchEvent(&event, status);
  1.1183 +        // We check mMultipleActionsPrevented because that's what <input type=range>
  1.1184 +        // sets when someone starts dragging the thumb. It doesn't set the status
  1.1185 +        // because it doesn't want to prevent the code that gives the input focus
  1.1186 +        // from running.
  1.1187 +        preventDefaultActions = (status == nsEventStatus_eConsumeNoDefault ||
  1.1188 +                                event.mFlags.mMultipleActionsPrevented);
  1.1189 +        isDownEvent = (event.message == NS_TOUCH_START);
  1.1190 +    }
  1.1191 +
  1.1192 +    // if the last event we got was a down event, then by now we know for sure whether
  1.1193 +    // this block has been default-prevented or not. if we haven't already sent the
  1.1194 +    // notification for this block, do so now.
  1.1195 +    if (sLastWasDownEvent && !sDefaultPreventedNotified) {
  1.1196 +        // if this event is a down event, that means it's the start of a new block, and the
  1.1197 +        // previous block should not be default-prevented
  1.1198 +        bool defaultPrevented = isDownEvent ? false : preventDefaultActions;
  1.1199 +        mozilla::widget::android::GeckoAppShell::NotifyDefaultPrevented(defaultPrevented);
  1.1200 +        sDefaultPreventedNotified = true;
  1.1201 +    }
  1.1202 +
  1.1203 +    // now, if this event is a down event, then we might already know that it has been
  1.1204 +    // default-prevented. if so, we send the notification right away; otherwise we wait
  1.1205 +    // for the next event.
  1.1206 +    if (isDownEvent) {
  1.1207 +        if (preventDefaultActions) {
  1.1208 +            mozilla::widget::android::GeckoAppShell::NotifyDefaultPrevented(true);
  1.1209 +            sDefaultPreventedNotified = true;
  1.1210 +        } else {
  1.1211 +            sDefaultPreventedNotified = false;
  1.1212 +        }
  1.1213 +    }
  1.1214 +    sLastWasDownEvent = isDownEvent;
  1.1215 +
  1.1216 +    return preventDefaultActions;
  1.1217 +}
  1.1218 +
  1.1219 +void
  1.1220 +nsWindow::OnNativeGestureEvent(AndroidGeckoEvent *ae)
  1.1221 +{
  1.1222 +  nsIntPoint pt(ae->Points()[0].x,
  1.1223 +                ae->Points()[0].y);
  1.1224 +  double delta = ae->X();
  1.1225 +  int msg = 0;
  1.1226 +
  1.1227 +  switch (ae->Action()) {
  1.1228 +      case AndroidMotionEvent::ACTION_MAGNIFY_START:
  1.1229 +          msg = NS_SIMPLE_GESTURE_MAGNIFY_START;
  1.1230 +          mStartDist = delta;
  1.1231 +          mLastDist = delta;
  1.1232 +          break;
  1.1233 +      case AndroidMotionEvent::ACTION_MAGNIFY:
  1.1234 +          msg = NS_SIMPLE_GESTURE_MAGNIFY_UPDATE;
  1.1235 +          delta -= mLastDist;
  1.1236 +          mLastDist += delta;
  1.1237 +          break;
  1.1238 +      case AndroidMotionEvent::ACTION_MAGNIFY_END:
  1.1239 +          msg = NS_SIMPLE_GESTURE_MAGNIFY;
  1.1240 +          delta -= mStartDist;
  1.1241 +          break;
  1.1242 +      default:
  1.1243 +          return;
  1.1244 +  }
  1.1245 +
  1.1246 +  nsRefPtr<nsWindow> kungFuDeathGrip(this);
  1.1247 +  DispatchGestureEvent(msg, 0, delta, pt, ae->Time());
  1.1248 +}
  1.1249 +
  1.1250 +void
  1.1251 +nsWindow::DispatchGestureEvent(uint32_t msg, uint32_t direction, double delta,
  1.1252 +                               const nsIntPoint &refPoint, uint64_t time)
  1.1253 +{
  1.1254 +    WidgetSimpleGestureEvent event(true, msg, this);
  1.1255 +
  1.1256 +    event.direction = direction;
  1.1257 +    event.delta = delta;
  1.1258 +    event.modifiers = 0;
  1.1259 +    event.time = time;
  1.1260 +    event.refPoint = LayoutDeviceIntPoint::FromUntyped(refPoint);
  1.1261 +
  1.1262 +    DispatchEvent(&event);
  1.1263 +}
  1.1264 +
  1.1265 +
  1.1266 +void
  1.1267 +nsWindow::DispatchMotionEvent(WidgetInputEvent &event, AndroidGeckoEvent *ae,
  1.1268 +                              const nsIntPoint &refPoint)
  1.1269 +{
  1.1270 +    nsIntPoint offset = WidgetToScreenOffset();
  1.1271 +
  1.1272 +    event.modifiers = ae->DOMModifiers();
  1.1273 +    event.time = ae->Time();
  1.1274 +
  1.1275 +    // XXX possibly bound the range of event.refPoint here.
  1.1276 +    //     some code may get confused.
  1.1277 +    event.refPoint = LayoutDeviceIntPoint::FromUntyped(refPoint - offset);
  1.1278 +
  1.1279 +    DispatchEvent(&event);
  1.1280 +}
  1.1281 +
  1.1282 +static unsigned int ConvertAndroidKeyCodeToDOMKeyCode(int androidKeyCode)
  1.1283 +{
  1.1284 +    // Special-case alphanumeric keycodes because they are most common.
  1.1285 +    if (androidKeyCode >= AKEYCODE_A &&
  1.1286 +        androidKeyCode <= AKEYCODE_Z) {
  1.1287 +        return androidKeyCode - AKEYCODE_A + NS_VK_A;
  1.1288 +    }
  1.1289 +
  1.1290 +    if (androidKeyCode >= AKEYCODE_0 &&
  1.1291 +        androidKeyCode <= AKEYCODE_9) {
  1.1292 +        return androidKeyCode - AKEYCODE_0 + NS_VK_0;
  1.1293 +    }
  1.1294 +
  1.1295 +    switch (androidKeyCode) {
  1.1296 +        // KEYCODE_UNKNOWN (0) ... KEYCODE_HOME (3)
  1.1297 +        case AKEYCODE_BACK:               return NS_VK_ESCAPE;
  1.1298 +        // KEYCODE_CALL (5) ... KEYCODE_POUND (18)
  1.1299 +        case AKEYCODE_DPAD_UP:            return NS_VK_UP;
  1.1300 +        case AKEYCODE_DPAD_DOWN:          return NS_VK_DOWN;
  1.1301 +        case AKEYCODE_DPAD_LEFT:          return NS_VK_LEFT;
  1.1302 +        case AKEYCODE_DPAD_RIGHT:         return NS_VK_RIGHT;
  1.1303 +        case AKEYCODE_DPAD_CENTER:        return NS_VK_RETURN;
  1.1304 +        case AKEYCODE_VOLUME_UP:          return NS_VK_VOLUME_UP;
  1.1305 +        case AKEYCODE_VOLUME_DOWN:        return NS_VK_VOLUME_DOWN;
  1.1306 +        // KEYCODE_VOLUME_POWER (26) ... KEYCODE_Z (54)
  1.1307 +        case AKEYCODE_COMMA:              return NS_VK_COMMA;
  1.1308 +        case AKEYCODE_PERIOD:             return NS_VK_PERIOD;
  1.1309 +        case AKEYCODE_ALT_LEFT:           return NS_VK_ALT;
  1.1310 +        case AKEYCODE_ALT_RIGHT:          return NS_VK_ALT;
  1.1311 +        case AKEYCODE_SHIFT_LEFT:         return NS_VK_SHIFT;
  1.1312 +        case AKEYCODE_SHIFT_RIGHT:        return NS_VK_SHIFT;
  1.1313 +        case AKEYCODE_TAB:                return NS_VK_TAB;
  1.1314 +        case AKEYCODE_SPACE:              return NS_VK_SPACE;
  1.1315 +        // KEYCODE_SYM (63) ... KEYCODE_ENVELOPE (65)
  1.1316 +        case AKEYCODE_ENTER:              return NS_VK_RETURN;
  1.1317 +        case AKEYCODE_DEL:                return NS_VK_BACK; // Backspace
  1.1318 +        case AKEYCODE_GRAVE:              return NS_VK_BACK_QUOTE;
  1.1319 +        // KEYCODE_MINUS (69)
  1.1320 +        case AKEYCODE_EQUALS:             return NS_VK_EQUALS;
  1.1321 +        case AKEYCODE_LEFT_BRACKET:       return NS_VK_OPEN_BRACKET;
  1.1322 +        case AKEYCODE_RIGHT_BRACKET:      return NS_VK_CLOSE_BRACKET;
  1.1323 +        case AKEYCODE_BACKSLASH:          return NS_VK_BACK_SLASH;
  1.1324 +        case AKEYCODE_SEMICOLON:          return NS_VK_SEMICOLON;
  1.1325 +        // KEYCODE_APOSTROPHE (75)
  1.1326 +        case AKEYCODE_SLASH:              return NS_VK_SLASH;
  1.1327 +        // KEYCODE_AT (77) ... KEYCODE_MEDIA_FAST_FORWARD (90)
  1.1328 +        case AKEYCODE_MUTE:               return NS_VK_VOLUME_MUTE;
  1.1329 +        case AKEYCODE_PAGE_UP:            return NS_VK_PAGE_UP;
  1.1330 +        case AKEYCODE_PAGE_DOWN:          return NS_VK_PAGE_DOWN;
  1.1331 +        // KEYCODE_PICTSYMBOLS (94) ... KEYCODE_BUTTON_MODE (110)
  1.1332 +        case AKEYCODE_ESCAPE:             return NS_VK_ESCAPE;
  1.1333 +        case AKEYCODE_FORWARD_DEL:        return NS_VK_DELETE;
  1.1334 +        case AKEYCODE_CTRL_LEFT:          return NS_VK_CONTROL;
  1.1335 +        case AKEYCODE_CTRL_RIGHT:         return NS_VK_CONTROL;
  1.1336 +        case AKEYCODE_CAPS_LOCK:          return NS_VK_CAPS_LOCK;
  1.1337 +        case AKEYCODE_SCROLL_LOCK:        return NS_VK_SCROLL_LOCK;
  1.1338 +        // KEYCODE_META_LEFT (117) ... KEYCODE_FUNCTION (119)
  1.1339 +        case AKEYCODE_SYSRQ:              return NS_VK_PRINTSCREEN;
  1.1340 +        case AKEYCODE_BREAK:              return NS_VK_PAUSE;
  1.1341 +        case AKEYCODE_MOVE_HOME:          return NS_VK_HOME;
  1.1342 +        case AKEYCODE_MOVE_END:           return NS_VK_END;
  1.1343 +        case AKEYCODE_INSERT:             return NS_VK_INSERT;
  1.1344 +        // KEYCODE_FORWARD (125) ... KEYCODE_MEDIA_RECORD (130)
  1.1345 +        case AKEYCODE_F1:                 return NS_VK_F1;
  1.1346 +        case AKEYCODE_F2:                 return NS_VK_F2;
  1.1347 +        case AKEYCODE_F3:                 return NS_VK_F3;
  1.1348 +        case AKEYCODE_F4:                 return NS_VK_F4;
  1.1349 +        case AKEYCODE_F5:                 return NS_VK_F5;
  1.1350 +        case AKEYCODE_F6:                 return NS_VK_F6;
  1.1351 +        case AKEYCODE_F7:                 return NS_VK_F7;
  1.1352 +        case AKEYCODE_F8:                 return NS_VK_F8;
  1.1353 +        case AKEYCODE_F9:                 return NS_VK_F9;
  1.1354 +        case AKEYCODE_F10:                return NS_VK_F10;
  1.1355 +        case AKEYCODE_F11:                return NS_VK_F11;
  1.1356 +        case AKEYCODE_F12:                return NS_VK_F12;
  1.1357 +        case AKEYCODE_NUM_LOCK:           return NS_VK_NUM_LOCK;
  1.1358 +        case AKEYCODE_NUMPAD_0:           return NS_VK_NUMPAD0;
  1.1359 +        case AKEYCODE_NUMPAD_1:           return NS_VK_NUMPAD1;
  1.1360 +        case AKEYCODE_NUMPAD_2:           return NS_VK_NUMPAD2;
  1.1361 +        case AKEYCODE_NUMPAD_3:           return NS_VK_NUMPAD3;
  1.1362 +        case AKEYCODE_NUMPAD_4:           return NS_VK_NUMPAD4;
  1.1363 +        case AKEYCODE_NUMPAD_5:           return NS_VK_NUMPAD5;
  1.1364 +        case AKEYCODE_NUMPAD_6:           return NS_VK_NUMPAD6;
  1.1365 +        case AKEYCODE_NUMPAD_7:           return NS_VK_NUMPAD7;
  1.1366 +        case AKEYCODE_NUMPAD_8:           return NS_VK_NUMPAD8;
  1.1367 +        case AKEYCODE_NUMPAD_9:           return NS_VK_NUMPAD9;
  1.1368 +        case AKEYCODE_NUMPAD_DIVIDE:      return NS_VK_DIVIDE;
  1.1369 +        case AKEYCODE_NUMPAD_MULTIPLY:    return NS_VK_MULTIPLY;
  1.1370 +        case AKEYCODE_NUMPAD_SUBTRACT:    return NS_VK_SUBTRACT;
  1.1371 +        case AKEYCODE_NUMPAD_ADD:         return NS_VK_ADD;
  1.1372 +        case AKEYCODE_NUMPAD_DOT:         return NS_VK_DECIMAL;
  1.1373 +        case AKEYCODE_NUMPAD_COMMA:       return NS_VK_SEPARATOR;
  1.1374 +        case AKEYCODE_NUMPAD_ENTER:       return NS_VK_RETURN;
  1.1375 +        case AKEYCODE_NUMPAD_EQUALS:      return NS_VK_EQUALS;
  1.1376 +        // KEYCODE_NUMPAD_LEFT_PAREN (162) ... KEYCODE_CALCULATOR (210)
  1.1377 +
  1.1378 +        // Needs to confirm the behavior.  If the key switches the open state
  1.1379 +        // of Japanese IME (or switches input character between Hiragana and
  1.1380 +        // Roman numeric characters), then, it might be better to use
  1.1381 +        // NS_VK_KANJI which is used for Alt+Zenkaku/Hankaku key on Windows.
  1.1382 +        case AKEYCODE_ZENKAKU_HANKAKU:    return 0;
  1.1383 +        case AKEYCODE_EISU:               return NS_VK_EISU;
  1.1384 +        case AKEYCODE_MUHENKAN:           return NS_VK_NONCONVERT;
  1.1385 +        case AKEYCODE_HENKAN:             return NS_VK_CONVERT;
  1.1386 +        case AKEYCODE_KATAKANA_HIRAGANA:  return 0;
  1.1387 +        case AKEYCODE_YEN:                return NS_VK_BACK_SLASH; // Same as other platforms.
  1.1388 +        case AKEYCODE_RO:                 return NS_VK_BACK_SLASH; // Same as other platforms.
  1.1389 +        case AKEYCODE_KANA:               return NS_VK_KANA;
  1.1390 +        case AKEYCODE_ASSIST:             return NS_VK_HELP;
  1.1391 +
  1.1392 +        // the A key is the action key for gamepad devices.
  1.1393 +        case AKEYCODE_BUTTON_A:          return NS_VK_RETURN;
  1.1394 +
  1.1395 +        default:
  1.1396 +            ALOG("ConvertAndroidKeyCodeToDOMKeyCode: "
  1.1397 +                 "No DOM keycode for Android keycode %d", androidKeyCode);
  1.1398 +        return 0;
  1.1399 +    }
  1.1400 +}
  1.1401 +
  1.1402 +static KeyNameIndex
  1.1403 +ConvertAndroidKeyCodeToKeyNameIndex(AndroidGeckoEvent& aAndroidGeckoEvent)
  1.1404 +{
  1.1405 +    int keyCode = aAndroidGeckoEvent.KeyCode();
  1.1406 +    // Special-case alphanumeric keycodes because they are most common.
  1.1407 +    if (keyCode >= AKEYCODE_A && keyCode <= AKEYCODE_Z) {
  1.1408 +        return KEY_NAME_INDEX_USE_STRING;
  1.1409 +    }
  1.1410 +
  1.1411 +    if (keyCode >= AKEYCODE_0 && keyCode <= AKEYCODE_9) {
  1.1412 +        return KEY_NAME_INDEX_USE_STRING;
  1.1413 +    }
  1.1414 +
  1.1415 +    switch (keyCode) {
  1.1416 +
  1.1417 +#define NS_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX(aNativeKey, aKeyNameIndex) \
  1.1418 +        case aNativeKey: return aKeyNameIndex;
  1.1419 +
  1.1420 +#include "NativeKeyToDOMKeyName.h"
  1.1421 +
  1.1422 +#undef NS_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX
  1.1423 +
  1.1424 +        // KEYCODE_0 (7) ... KEYCODE_9 (16)
  1.1425 +        case AKEYCODE_STAR:               // '*' key
  1.1426 +        case AKEYCODE_POUND:              // '#' key
  1.1427 +
  1.1428 +        // KEYCODE_A (29) ... KEYCODE_Z (54)
  1.1429 +
  1.1430 +        case AKEYCODE_COMMA:              // ',' key
  1.1431 +        case AKEYCODE_PERIOD:             // '.' key
  1.1432 +        case AKEYCODE_SPACE:
  1.1433 +        case AKEYCODE_GRAVE:              // '`' key
  1.1434 +        case AKEYCODE_MINUS:              // '-' key
  1.1435 +        case AKEYCODE_EQUALS:             // '=' key
  1.1436 +        case AKEYCODE_LEFT_BRACKET:       // '[' key
  1.1437 +        case AKEYCODE_RIGHT_BRACKET:      // ']' key
  1.1438 +        case AKEYCODE_BACKSLASH:          // '\' key
  1.1439 +        case AKEYCODE_SEMICOLON:          // ';' key
  1.1440 +        case AKEYCODE_APOSTROPHE:         // ''' key
  1.1441 +        case AKEYCODE_SLASH:              // '/' key
  1.1442 +        case AKEYCODE_AT:                 // '@' key
  1.1443 +        case AKEYCODE_PLUS:               // '+' key
  1.1444 +
  1.1445 +        case AKEYCODE_NUMPAD_0:
  1.1446 +        case AKEYCODE_NUMPAD_1:
  1.1447 +        case AKEYCODE_NUMPAD_2:
  1.1448 +        case AKEYCODE_NUMPAD_3:
  1.1449 +        case AKEYCODE_NUMPAD_4:
  1.1450 +        case AKEYCODE_NUMPAD_5:
  1.1451 +        case AKEYCODE_NUMPAD_6:
  1.1452 +        case AKEYCODE_NUMPAD_7:
  1.1453 +        case AKEYCODE_NUMPAD_8:
  1.1454 +        case AKEYCODE_NUMPAD_9:
  1.1455 +        case AKEYCODE_NUMPAD_DIVIDE:
  1.1456 +        case AKEYCODE_NUMPAD_MULTIPLY:
  1.1457 +        case AKEYCODE_NUMPAD_SUBTRACT:
  1.1458 +        case AKEYCODE_NUMPAD_ADD:
  1.1459 +        case AKEYCODE_NUMPAD_DOT:
  1.1460 +        case AKEYCODE_NUMPAD_COMMA:
  1.1461 +        case AKEYCODE_NUMPAD_EQUALS:
  1.1462 +        case AKEYCODE_NUMPAD_LEFT_PAREN:
  1.1463 +        case AKEYCODE_NUMPAD_RIGHT_PAREN:
  1.1464 +
  1.1465 +        case AKEYCODE_YEN:                // yen sign key
  1.1466 +        case AKEYCODE_RO:                 // Japanese Ro key
  1.1467 +            return KEY_NAME_INDEX_USE_STRING;
  1.1468 +
  1.1469 +        case AKEYCODE_SOFT_LEFT:
  1.1470 +        case AKEYCODE_SOFT_RIGHT:
  1.1471 +        case AKEYCODE_CALL:
  1.1472 +        case AKEYCODE_ENDCALL:
  1.1473 +        case AKEYCODE_SYM:                // Symbol modifier
  1.1474 +        case AKEYCODE_NUM:                // XXX Not sure
  1.1475 +        case AKEYCODE_HEADSETHOOK:
  1.1476 +        case AKEYCODE_FOCUS:
  1.1477 +        case AKEYCODE_NOTIFICATION:       // XXX Not sure
  1.1478 +        case AKEYCODE_PICTSYMBOLS:
  1.1479 +
  1.1480 +        case AKEYCODE_BUTTON_A:
  1.1481 +        case AKEYCODE_BUTTON_B:
  1.1482 +        case AKEYCODE_BUTTON_C:
  1.1483 +        case AKEYCODE_BUTTON_X:
  1.1484 +        case AKEYCODE_BUTTON_Y:
  1.1485 +        case AKEYCODE_BUTTON_Z:
  1.1486 +        case AKEYCODE_BUTTON_L1:
  1.1487 +        case AKEYCODE_BUTTON_R1:
  1.1488 +        case AKEYCODE_BUTTON_L2:
  1.1489 +        case AKEYCODE_BUTTON_R2:
  1.1490 +        case AKEYCODE_BUTTON_THUMBL:
  1.1491 +        case AKEYCODE_BUTTON_THUMBR:
  1.1492 +        case AKEYCODE_BUTTON_START:
  1.1493 +        case AKEYCODE_BUTTON_SELECT:
  1.1494 +        case AKEYCODE_BUTTON_MODE:
  1.1495 +
  1.1496 +        case AKEYCODE_MUTE: // mutes the microphone
  1.1497 +        case AKEYCODE_MEDIA_CLOSE:
  1.1498 +
  1.1499 +        case AKEYCODE_ZOOM_IN:
  1.1500 +        case AKEYCODE_ZOOM_OUT:
  1.1501 +        case AKEYCODE_DVR:
  1.1502 +        case AKEYCODE_TV_POWER:
  1.1503 +        case AKEYCODE_TV_INPUT:
  1.1504 +        case AKEYCODE_STB_POWER:
  1.1505 +        case AKEYCODE_STB_INPUT:
  1.1506 +        case AKEYCODE_AVR_POWER:
  1.1507 +        case AKEYCODE_AVR_INPUT:
  1.1508 +
  1.1509 +        case AKEYCODE_BUTTON_1:
  1.1510 +        case AKEYCODE_BUTTON_2:
  1.1511 +        case AKEYCODE_BUTTON_3:
  1.1512 +        case AKEYCODE_BUTTON_4:
  1.1513 +        case AKEYCODE_BUTTON_5:
  1.1514 +        case AKEYCODE_BUTTON_6:
  1.1515 +        case AKEYCODE_BUTTON_7:
  1.1516 +        case AKEYCODE_BUTTON_8:
  1.1517 +        case AKEYCODE_BUTTON_9:
  1.1518 +        case AKEYCODE_BUTTON_10:
  1.1519 +        case AKEYCODE_BUTTON_11:
  1.1520 +        case AKEYCODE_BUTTON_12:
  1.1521 +        case AKEYCODE_BUTTON_13:
  1.1522 +        case AKEYCODE_BUTTON_14:
  1.1523 +        case AKEYCODE_BUTTON_15:
  1.1524 +        case AKEYCODE_BUTTON_16:
  1.1525 +
  1.1526 +        case AKEYCODE_LANGUAGE_SWITCH:
  1.1527 +        case AKEYCODE_MANNER_MODE:
  1.1528 +        case AKEYCODE_3D_MODE:
  1.1529 +        case AKEYCODE_CONTACTS:
  1.1530 +        case AKEYCODE_CALENDAR:
  1.1531 +        case AKEYCODE_MUSIC:
  1.1532 +        case AKEYCODE_CALCULATOR:
  1.1533 +
  1.1534 +        case AKEYCODE_ZENKAKU_HANKAKU:
  1.1535 +        case AKEYCODE_KATAKANA_HIRAGANA:
  1.1536 +            return KEY_NAME_INDEX_Unidentified;
  1.1537 +
  1.1538 +        case AKEYCODE_UNKNOWN:
  1.1539 +            MOZ_ASSERT(
  1.1540 +                aAndroidGeckoEvent.Action() != AKEY_EVENT_ACTION_MULTIPLE,
  1.1541 +                "Don't call this when action is AKEY_EVENT_ACTION_MULTIPLE!");
  1.1542 +            // It's actually an unknown key if the action isn't ACTION_MULTIPLE.
  1.1543 +            // However, it might cause text input.  So, let's check the value.
  1.1544 +            return aAndroidGeckoEvent.DOMPrintableKeyValue() ?
  1.1545 +                KEY_NAME_INDEX_USE_STRING : KEY_NAME_INDEX_Unidentified;
  1.1546 +
  1.1547 +        default:
  1.1548 +            ALOG("ConvertAndroidKeyCodeToKeyNameIndex: "
  1.1549 +                 "No DOM key name index for Android keycode %d", keyCode);
  1.1550 +            return KEY_NAME_INDEX_Unidentified;
  1.1551 +    }
  1.1552 +}
  1.1553 +
  1.1554 +static void InitPluginEvent(ANPEvent* pluginEvent, ANPKeyActions keyAction,
  1.1555 +                            AndroidGeckoEvent& key)
  1.1556 +{
  1.1557 +    int androidKeyCode = key.KeyCode();
  1.1558 +    uint32_t domKeyCode = ConvertAndroidKeyCodeToDOMKeyCode(androidKeyCode);
  1.1559 +
  1.1560 +    int modifiers = 0;
  1.1561 +    if (key.IsAltPressed())
  1.1562 +      modifiers |= kAlt_ANPKeyModifier;
  1.1563 +    if (key.IsShiftPressed())
  1.1564 +      modifiers |= kShift_ANPKeyModifier;
  1.1565 +
  1.1566 +    pluginEvent->inSize = sizeof(ANPEvent);
  1.1567 +    pluginEvent->eventType = kKey_ANPEventType;
  1.1568 +    pluginEvent->data.key.action = keyAction;
  1.1569 +    pluginEvent->data.key.nativeCode = androidKeyCode;
  1.1570 +    pluginEvent->data.key.virtualCode = domKeyCode;
  1.1571 +    pluginEvent->data.key.unichar = key.UnicodeChar();
  1.1572 +    pluginEvent->data.key.modifiers = modifiers;
  1.1573 +    pluginEvent->data.key.repeatCount = key.RepeatCount();
  1.1574 +}
  1.1575 +
  1.1576 +void
  1.1577 +nsWindow::InitKeyEvent(WidgetKeyboardEvent& event, AndroidGeckoEvent& key,
  1.1578 +                       ANPEvent* pluginEvent)
  1.1579 +{
  1.1580 +    event.mKeyNameIndex = ConvertAndroidKeyCodeToKeyNameIndex(key);
  1.1581 +    if (event.mKeyNameIndex == KEY_NAME_INDEX_USE_STRING) {
  1.1582 +        int keyValue = key.DOMPrintableKeyValue();
  1.1583 +        if (keyValue) {
  1.1584 +            event.mKeyValue = static_cast<char16_t>(keyValue);
  1.1585 +        }
  1.1586 +    }
  1.1587 +    uint32_t domKeyCode = ConvertAndroidKeyCodeToDOMKeyCode(key.KeyCode());
  1.1588 +
  1.1589 +    if (event.message == NS_KEY_PRESS) {
  1.1590 +        // Android gives us \n, so filter out some control characters.
  1.1591 +        int charCode = key.UnicodeChar();
  1.1592 +        if (!charCode) {
  1.1593 +            charCode = key.BaseUnicodeChar();
  1.1594 +        }
  1.1595 +        event.isChar = (charCode >= ' ');
  1.1596 +        event.charCode = event.isChar ? charCode : 0;
  1.1597 +        event.keyCode = (event.charCode > 0) ? 0 : domKeyCode;
  1.1598 +        event.pluginEvent = nullptr;
  1.1599 +    } else {
  1.1600 +#ifdef DEBUG
  1.1601 +        if (event.message != NS_KEY_DOWN && event.message != NS_KEY_UP) {
  1.1602 +            ALOG("InitKeyEvent: unexpected event.message %d", event.message);
  1.1603 +        }
  1.1604 +#endif // DEBUG
  1.1605 +
  1.1606 +        // Flash will want a pluginEvent for keydown and keyup events.
  1.1607 +        ANPKeyActions action = event.message == NS_KEY_DOWN
  1.1608 +                             ? kDown_ANPKeyAction
  1.1609 +                             : kUp_ANPKeyAction;
  1.1610 +        InitPluginEvent(pluginEvent, action, key);
  1.1611 +
  1.1612 +        event.isChar = false;
  1.1613 +        event.charCode = 0;
  1.1614 +        event.keyCode = domKeyCode;
  1.1615 +        event.pluginEvent = pluginEvent;
  1.1616 +    }
  1.1617 +
  1.1618 +    event.modifiers = key.DOMModifiers();
  1.1619 +    if (gMenu) {
  1.1620 +        event.modifiers |= MODIFIER_CONTROL;
  1.1621 +    }
  1.1622 +    // For keypress, if the unicode char already has modifiers applied, we
  1.1623 +    // don't specify extra modifiers. If UnicodeChar() != BaseUnicodeChar()
  1.1624 +    // it means UnicodeChar() already has modifiers applied.
  1.1625 +    // Note that on Android 4.x, Alt modifier isn't set when the key input
  1.1626 +    // causes text input even while right Alt key is pressed.  However, this
  1.1627 +    // is necessary for Android 2.3 compatibility.
  1.1628 +    if (event.message == NS_KEY_PRESS &&
  1.1629 +        key.UnicodeChar() && key.UnicodeChar() != key.BaseUnicodeChar()) {
  1.1630 +        event.modifiers &= ~(MODIFIER_ALT | MODIFIER_CONTROL | MODIFIER_META);
  1.1631 +    }
  1.1632 +
  1.1633 +    event.mIsRepeat =
  1.1634 +        (event.message == NS_KEY_DOWN || event.message == NS_KEY_PRESS) &&
  1.1635 +        (!!(key.Flags() & AKEY_EVENT_FLAG_LONG_PRESS) || !!key.RepeatCount());
  1.1636 +    event.location = key.DomKeyLocation();
  1.1637 +    event.time = key.Time();
  1.1638 +
  1.1639 +    if (gMenu)
  1.1640 +        gMenuConsumed = true;
  1.1641 +}
  1.1642 +
  1.1643 +void
  1.1644 +nsWindow::HandleSpecialKey(AndroidGeckoEvent *ae)
  1.1645 +{
  1.1646 +    nsRefPtr<nsWindow> kungFuDeathGrip(this);
  1.1647 +    nsCOMPtr<nsIAtom> command;
  1.1648 +    bool isDown = ae->Action() == AKEY_EVENT_ACTION_DOWN;
  1.1649 +    bool isLongPress = !!(ae->Flags() & AKEY_EVENT_FLAG_LONG_PRESS);
  1.1650 +    bool doCommand = false;
  1.1651 +    uint32_t keyCode = ae->KeyCode();
  1.1652 +
  1.1653 +    if (isDown) {
  1.1654 +        switch (keyCode) {
  1.1655 +            case AKEYCODE_BACK:
  1.1656 +                if (isLongPress) {
  1.1657 +                    command = nsGkAtoms::Clear;
  1.1658 +                    doCommand = true;
  1.1659 +                }
  1.1660 +                break;
  1.1661 +            case AKEYCODE_MENU:
  1.1662 +                gMenu = true;
  1.1663 +                gMenuConsumed = isLongPress;
  1.1664 +                break;
  1.1665 +        }
  1.1666 +    } else {
  1.1667 +        switch (keyCode) {
  1.1668 +            case AKEYCODE_BACK: {
  1.1669 +                // XXX Where is the keydown event for this??
  1.1670 +                WidgetKeyboardEvent pressEvent(true, NS_KEY_PRESS, this);
  1.1671 +                ANPEvent pluginEvent;
  1.1672 +                InitKeyEvent(pressEvent, *ae, &pluginEvent);
  1.1673 +                DispatchEvent(&pressEvent);
  1.1674 +                return;
  1.1675 +            }
  1.1676 +            case AKEYCODE_MENU:
  1.1677 +                gMenu = false;
  1.1678 +                if (!gMenuConsumed) {
  1.1679 +                    command = nsGkAtoms::Menu;
  1.1680 +                    doCommand = true;
  1.1681 +                }
  1.1682 +                break;
  1.1683 +            case AKEYCODE_SEARCH:
  1.1684 +                command = nsGkAtoms::Search;
  1.1685 +                doCommand = true;
  1.1686 +                break;
  1.1687 +            default:
  1.1688 +                ALOG("Unknown special key code!");
  1.1689 +                return;
  1.1690 +        }
  1.1691 +    }
  1.1692 +    if (doCommand) {
  1.1693 +        WidgetCommandEvent event(true, nsGkAtoms::onAppCommand, command, this);
  1.1694 +        InitEvent(event);
  1.1695 +        DispatchEvent(&event);
  1.1696 +    }
  1.1697 +}
  1.1698 +
  1.1699 +void
  1.1700 +nsWindow::OnKeyEvent(AndroidGeckoEvent *ae)
  1.1701 +{
  1.1702 +    nsRefPtr<nsWindow> kungFuDeathGrip(this);
  1.1703 +    RemoveIMEComposition();
  1.1704 +    uint32_t msg;
  1.1705 +    switch (ae->Action()) {
  1.1706 +    case AKEY_EVENT_ACTION_DOWN:
  1.1707 +        msg = NS_KEY_DOWN;
  1.1708 +        break;
  1.1709 +    case AKEY_EVENT_ACTION_UP:
  1.1710 +        msg = NS_KEY_UP;
  1.1711 +        break;
  1.1712 +    case AKEY_EVENT_ACTION_MULTIPLE:
  1.1713 +        // Keys with multiple action are handled in Java,
  1.1714 +        // and we should never see one here
  1.1715 +        MOZ_CRASH("Cannot handle key with multiple action");
  1.1716 +    default:
  1.1717 +        ALOG("Unknown key action event!");
  1.1718 +        return;
  1.1719 +    }
  1.1720 +
  1.1721 +    bool firePress = ae->Action() == AKEY_EVENT_ACTION_DOWN;
  1.1722 +    switch (ae->KeyCode()) {
  1.1723 +    case AKEYCODE_SHIFT_LEFT:
  1.1724 +    case AKEYCODE_SHIFT_RIGHT:
  1.1725 +    case AKEYCODE_ALT_LEFT:
  1.1726 +    case AKEYCODE_ALT_RIGHT:
  1.1727 +    case AKEYCODE_CTRL_LEFT:
  1.1728 +    case AKEYCODE_CTRL_RIGHT:
  1.1729 +        firePress = false;
  1.1730 +        break;
  1.1731 +    case AKEYCODE_BACK:
  1.1732 +    case AKEYCODE_MENU:
  1.1733 +    case AKEYCODE_SEARCH:
  1.1734 +        HandleSpecialKey(ae);
  1.1735 +        return;
  1.1736 +    }
  1.1737 +
  1.1738 +    nsEventStatus status;
  1.1739 +    WidgetKeyboardEvent event(true, msg, this);
  1.1740 +    ANPEvent pluginEvent;
  1.1741 +    InitKeyEvent(event, *ae, &pluginEvent);
  1.1742 +    DispatchEvent(&event, status);
  1.1743 +
  1.1744 +    if (Destroyed())
  1.1745 +        return;
  1.1746 +    if (!firePress || status == nsEventStatus_eConsumeNoDefault) {
  1.1747 +        return;
  1.1748 +    }
  1.1749 +
  1.1750 +    WidgetKeyboardEvent pressEvent(true, NS_KEY_PRESS, this);
  1.1751 +    InitKeyEvent(pressEvent, *ae, &pluginEvent);
  1.1752 +#ifdef DEBUG_ANDROID_WIDGET
  1.1753 +    __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());
  1.1754 +#endif
  1.1755 +    DispatchEvent(&pressEvent);
  1.1756 +}
  1.1757 +
  1.1758 +#ifdef DEBUG_ANDROID_IME
  1.1759 +#define ALOGIME(args...) ALOG(args)
  1.1760 +#else
  1.1761 +#define ALOGIME(args...) ((void)0)
  1.1762 +#endif
  1.1763 +
  1.1764 +static nscolor
  1.1765 +ConvertAndroidColor(uint32_t argb)
  1.1766 +{
  1.1767 +    return NS_RGBA((argb & 0x00ff0000) >> 16,
  1.1768 +                   (argb & 0x0000ff00) >> 8,
  1.1769 +                   (argb & 0x000000ff),
  1.1770 +                   (argb & 0xff000000) >> 24);
  1.1771 +}
  1.1772 +
  1.1773 +class AutoIMEMask {
  1.1774 +private:
  1.1775 +    bool mOldMask, *mMask;
  1.1776 +public:
  1.1777 +    AutoIMEMask(bool &mask) : mOldMask(mask), mMask(&mask) {
  1.1778 +        mask = true;
  1.1779 +    }
  1.1780 +    ~AutoIMEMask() {
  1.1781 +        *mMask = mOldMask;
  1.1782 +    }
  1.1783 +};
  1.1784 +
  1.1785 +/*
  1.1786 +    Remove the composition but leave the text content as-is
  1.1787 +*/
  1.1788 +void
  1.1789 +nsWindow::RemoveIMEComposition()
  1.1790 +{
  1.1791 +    // Remove composition on Gecko side
  1.1792 +    if (!mIMEComposing)
  1.1793 +        return;
  1.1794 +
  1.1795 +    nsRefPtr<nsWindow> kungFuDeathGrip(this);
  1.1796 +    AutoIMEMask selMask(mIMEMaskSelectionUpdate);
  1.1797 +    AutoIMEMask textMask(mIMEMaskTextUpdate);
  1.1798 +
  1.1799 +    WidgetTextEvent textEvent(true, NS_TEXT_TEXT, this);
  1.1800 +    InitEvent(textEvent, nullptr);
  1.1801 +    textEvent.theText = mIMEComposingText;
  1.1802 +    DispatchEvent(&textEvent);
  1.1803 +
  1.1804 +    WidgetCompositionEvent event(true, NS_COMPOSITION_END, this);
  1.1805 +    InitEvent(event, nullptr);
  1.1806 +    DispatchEvent(&event);
  1.1807 +}
  1.1808 +
  1.1809 +void
  1.1810 +nsWindow::OnIMEEvent(AndroidGeckoEvent *ae)
  1.1811 +{
  1.1812 +    MOZ_ASSERT(!mIMEMaskTextUpdate);
  1.1813 +    MOZ_ASSERT(!mIMEMaskSelectionUpdate);
  1.1814 +    /*
  1.1815 +        Rules for managing IME between Gecko and Java:
  1.1816 +
  1.1817 +        * Gecko controls the text content, and Java shadows the Gecko text
  1.1818 +           through text updates
  1.1819 +        * Java controls the selection, and Gecko shadows the Java selection
  1.1820 +           through set selection events
  1.1821 +        * Java controls the composition, and Gecko shadows the Java
  1.1822 +           composition through update composition events
  1.1823 +    */
  1.1824 +    nsRefPtr<nsWindow> kungFuDeathGrip(this);
  1.1825 +
  1.1826 +    if (ae->Action() == AndroidGeckoEvent::IME_ACKNOWLEDGE_FOCUS) {
  1.1827 +        MOZ_ASSERT(mIMEMaskEventsCount > 0);
  1.1828 +        mIMEMaskEventsCount--;
  1.1829 +        if (!mIMEMaskEventsCount) {
  1.1830 +            // The focusing handshake sequence is complete, and Java is waiting
  1.1831 +            // on Gecko. Now we can notify Java of the newly focused content
  1.1832 +            mIMETextChanges.Clear();
  1.1833 +            mIMESelectionChanged = false;
  1.1834 +            // NotifyIMEOfTextChange also notifies selection
  1.1835 +            // Use 'INT32_MAX / 2' here because subsequent text changes might
  1.1836 +            // combine with this text change, and overflow might occur if
  1.1837 +            // we just use INT32_MAX
  1.1838 +            IMENotification notification(NOTIFY_IME_OF_TEXT_CHANGE);
  1.1839 +            notification.mTextChangeData.mOldEndOffset =
  1.1840 +                notification.mTextChangeData.mNewEndOffset = INT32_MAX / 2;
  1.1841 +            NotifyIMEOfTextChange(notification);
  1.1842 +            FlushIMEChanges();
  1.1843 +        }
  1.1844 +        mozilla::widget::android::GeckoAppShell::NotifyIME(AndroidBridge::NOTIFY_IME_REPLY_EVENT);
  1.1845 +        return;
  1.1846 +    } else if (ae->Action() == AndroidGeckoEvent::IME_UPDATE_CONTEXT) {
  1.1847 +        mozilla::widget::android::GeckoAppShell::NotifyIMEContext(mInputContext.mIMEState.mEnabled,
  1.1848 +                                        mInputContext.mHTMLInputType,
  1.1849 +                                        mInputContext.mHTMLInputInputmode,
  1.1850 +                                        mInputContext.mActionHint);
  1.1851 +        mIMEUpdatingContext = false;
  1.1852 +        return;
  1.1853 +    }
  1.1854 +    if (mIMEMaskEventsCount > 0) {
  1.1855 +        // Still reply to events, but don't do anything else
  1.1856 +        if (ae->Action() == AndroidGeckoEvent::IME_SYNCHRONIZE ||
  1.1857 +            ae->Action() == AndroidGeckoEvent::IME_REPLACE_TEXT) {
  1.1858 +            mozilla::widget::android::GeckoAppShell::NotifyIME(AndroidBridge::NOTIFY_IME_REPLY_EVENT);
  1.1859 +        }
  1.1860 +        return;
  1.1861 +    }
  1.1862 +    switch (ae->Action()) {
  1.1863 +    case AndroidGeckoEvent::IME_FLUSH_CHANGES:
  1.1864 +        {
  1.1865 +            FlushIMEChanges();
  1.1866 +        }
  1.1867 +        break;
  1.1868 +    case AndroidGeckoEvent::IME_SYNCHRONIZE:
  1.1869 +        {
  1.1870 +            FlushIMEChanges();
  1.1871 +            mozilla::widget::android::GeckoAppShell::NotifyIME(AndroidBridge::NOTIFY_IME_REPLY_EVENT);
  1.1872 +        }
  1.1873 +        break;
  1.1874 +    case AndroidGeckoEvent::IME_REPLACE_TEXT:
  1.1875 +        {
  1.1876 +            /*
  1.1877 +                Replace text in Gecko thread from ae->Start() to ae->End()
  1.1878 +                  with the string ae->Characters()
  1.1879 +
  1.1880 +                Selection updates are masked so the result of our temporary
  1.1881 +                  selection event is not passed on to Java
  1.1882 +
  1.1883 +                Text updates are passed on, so the Java text can shadow the
  1.1884 +                  Gecko text
  1.1885 +            */
  1.1886 +            AutoIMEMask selMask(mIMEMaskSelectionUpdate);
  1.1887 +            RemoveIMEComposition();
  1.1888 +            {
  1.1889 +                WidgetSelectionEvent event(true, NS_SELECTION_SET, this);
  1.1890 +                InitEvent(event, nullptr);
  1.1891 +                event.mOffset = uint32_t(ae->Start());
  1.1892 +                event.mLength = uint32_t(ae->End() - ae->Start());
  1.1893 +                event.mExpandToClusterBoundary = false;
  1.1894 +                DispatchEvent(&event);
  1.1895 +            }
  1.1896 +
  1.1897 +            if (!mIMEKeyEvents.IsEmpty()) {
  1.1898 +                for (uint32_t i = 0; i < mIMEKeyEvents.Length(); i++) {
  1.1899 +                    OnKeyEvent(&mIMEKeyEvents[i]);
  1.1900 +                }
  1.1901 +                mIMEKeyEvents.Clear();
  1.1902 +                FlushIMEChanges();
  1.1903 +                mozilla::widget::android::GeckoAppShell::NotifyIME(AndroidBridge::NOTIFY_IME_REPLY_EVENT);
  1.1904 +                break;
  1.1905 +            }
  1.1906 +
  1.1907 +            {
  1.1908 +                WidgetCompositionEvent event(true, NS_COMPOSITION_START, this);
  1.1909 +                InitEvent(event, nullptr);
  1.1910 +                DispatchEvent(&event);
  1.1911 +            }
  1.1912 +            {
  1.1913 +                WidgetCompositionEvent event(true, NS_COMPOSITION_UPDATE, this);
  1.1914 +                InitEvent(event, nullptr);
  1.1915 +                event.data = ae->Characters();
  1.1916 +                DispatchEvent(&event);
  1.1917 +            }
  1.1918 +            {
  1.1919 +                WidgetTextEvent event(true, NS_TEXT_TEXT, this);
  1.1920 +                InitEvent(event, nullptr);
  1.1921 +                event.theText = ae->Characters();
  1.1922 +                DispatchEvent(&event);
  1.1923 +            }
  1.1924 +            {
  1.1925 +                WidgetCompositionEvent event(true, NS_COMPOSITION_END, this);
  1.1926 +                InitEvent(event, nullptr);
  1.1927 +                event.data = ae->Characters();
  1.1928 +                DispatchEvent(&event);
  1.1929 +            }
  1.1930 +            FlushIMEChanges();
  1.1931 +            mozilla::widget::android::GeckoAppShell::NotifyIME(AndroidBridge::NOTIFY_IME_REPLY_EVENT);
  1.1932 +        }
  1.1933 +        break;
  1.1934 +    case AndroidGeckoEvent::IME_SET_SELECTION:
  1.1935 +        {
  1.1936 +            /*
  1.1937 +                Set Gecko selection to ae->Start() to ae->End()
  1.1938 +
  1.1939 +                Selection updates are masked to prevent Java from being
  1.1940 +                  notified of the new selection
  1.1941 +            */
  1.1942 +            AutoIMEMask selMask(mIMEMaskSelectionUpdate);
  1.1943 +            RemoveIMEComposition();
  1.1944 +            WidgetSelectionEvent selEvent(true, NS_SELECTION_SET, this);
  1.1945 +            InitEvent(selEvent, nullptr);
  1.1946 +
  1.1947 +            int32_t start = ae->Start(), end = ae->End();
  1.1948 +
  1.1949 +            if (start < 0 || end < 0) {
  1.1950 +                WidgetQueryContentEvent event(true, NS_QUERY_SELECTED_TEXT,
  1.1951 +                                              this);
  1.1952 +                InitEvent(event, nullptr);
  1.1953 +                DispatchEvent(&event);
  1.1954 +                MOZ_ASSERT(event.mSucceeded && !event.mWasAsync);
  1.1955 +
  1.1956 +                if (start < 0)
  1.1957 +                    start = int32_t(event.GetSelectionStart());
  1.1958 +                if (end < 0)
  1.1959 +                    end = int32_t(event.GetSelectionEnd());
  1.1960 +            }
  1.1961 +
  1.1962 +            selEvent.mOffset = std::min(start, end);
  1.1963 +            selEvent.mLength = std::max(start, end) - selEvent.mOffset;
  1.1964 +            selEvent.mReversed = start > end;
  1.1965 +            selEvent.mExpandToClusterBoundary = false;
  1.1966 +
  1.1967 +            DispatchEvent(&selEvent);
  1.1968 +
  1.1969 +            // Notify SelectionHandler of final caret position
  1.1970 +            // Required after IME hide via 'Back' button
  1.1971 +            AndroidGeckoEvent* broadcastEvent = AndroidGeckoEvent::MakeBroadcastEvent(
  1.1972 +                NS_LITERAL_CSTRING("TextSelection:UpdateCaretPos"),
  1.1973 +                NS_LITERAL_CSTRING(""));
  1.1974 +            nsAppShell::gAppShell->PostEvent(broadcastEvent);
  1.1975 +        }
  1.1976 +        break;
  1.1977 +    case AndroidGeckoEvent::IME_ADD_COMPOSITION_RANGE:
  1.1978 +        {
  1.1979 +            TextRange range;
  1.1980 +            range.mStartOffset = ae->Start();
  1.1981 +            range.mEndOffset = ae->End();
  1.1982 +            range.mRangeType = ae->RangeType();
  1.1983 +            range.mRangeStyle.mDefinedStyles = ae->RangeStyles();
  1.1984 +            range.mRangeStyle.mLineStyle = ae->RangeLineStyle();
  1.1985 +            range.mRangeStyle.mIsBoldLine = ae->RangeBoldLine();
  1.1986 +            range.mRangeStyle.mForegroundColor =
  1.1987 +                    ConvertAndroidColor(uint32_t(ae->RangeForeColor()));
  1.1988 +            range.mRangeStyle.mBackgroundColor =
  1.1989 +                    ConvertAndroidColor(uint32_t(ae->RangeBackColor()));
  1.1990 +            range.mRangeStyle.mUnderlineColor =
  1.1991 +                    ConvertAndroidColor(uint32_t(ae->RangeLineColor()));
  1.1992 +            mIMERanges->AppendElement(range);
  1.1993 +        }
  1.1994 +        break;
  1.1995 +    case AndroidGeckoEvent::IME_UPDATE_COMPOSITION:
  1.1996 +        {
  1.1997 +            /*
  1.1998 +                Update the composition from ae->Start() to ae->End() using
  1.1999 +                  information from added ranges. This is only used for
  1.2000 +                  visual indication and does not affect the text content.
  1.2001 +                  Only the offsets are specified and not the text content
  1.2002 +                  to eliminate the possibility of this event altering the
  1.2003 +                  text content unintentionally.
  1.2004 +
  1.2005 +                Selection and text updates are masked so the result of
  1.2006 +                  temporary events are not passed on to Java
  1.2007 +            */
  1.2008 +            AutoIMEMask selMask(mIMEMaskSelectionUpdate);
  1.2009 +            AutoIMEMask textMask(mIMEMaskTextUpdate);
  1.2010 +            RemoveIMEComposition();
  1.2011 +
  1.2012 +            WidgetTextEvent event(true, NS_TEXT_TEXT, this);
  1.2013 +            InitEvent(event, nullptr);
  1.2014 +
  1.2015 +            event.mRanges = new TextRangeArray();
  1.2016 +            mIMERanges.swap(event.mRanges);
  1.2017 +
  1.2018 +            {
  1.2019 +                WidgetSelectionEvent event(true, NS_SELECTION_SET, this);
  1.2020 +                InitEvent(event, nullptr);
  1.2021 +                event.mOffset = uint32_t(ae->Start());
  1.2022 +                event.mLength = uint32_t(ae->End() - ae->Start());
  1.2023 +                event.mExpandToClusterBoundary = false;
  1.2024 +                DispatchEvent(&event);
  1.2025 +            }
  1.2026 +            {
  1.2027 +                WidgetQueryContentEvent queryEvent(true,
  1.2028 +                                                   NS_QUERY_SELECTED_TEXT,
  1.2029 +                                                   this);
  1.2030 +                InitEvent(queryEvent, nullptr);
  1.2031 +                DispatchEvent(&queryEvent);
  1.2032 +                MOZ_ASSERT(queryEvent.mSucceeded && !queryEvent.mWasAsync);
  1.2033 +                event.theText = queryEvent.mReply.mString;
  1.2034 +            }
  1.2035 +            {
  1.2036 +                WidgetCompositionEvent event(true, NS_COMPOSITION_START, this);
  1.2037 +                InitEvent(event, nullptr);
  1.2038 +                DispatchEvent(&event);
  1.2039 +            }
  1.2040 +            {
  1.2041 +                WidgetCompositionEvent compositionUpdate(true,
  1.2042 +                                                         NS_COMPOSITION_UPDATE,
  1.2043 +                                                         this);
  1.2044 +                InitEvent(compositionUpdate, nullptr);
  1.2045 +                compositionUpdate.data = event.theText;
  1.2046 +                DispatchEvent(&compositionUpdate);
  1.2047 +            }
  1.2048 +
  1.2049 +#ifdef DEBUG_ANDROID_IME
  1.2050 +            const NS_ConvertUTF16toUTF8 theText8(event.theText);
  1.2051 +            const char* text = theText8.get();
  1.2052 +            ALOGIME("IME: IME_SET_TEXT: text=\"%s\", length=%u, range=%u",
  1.2053 +                    text, event.theText.Length(), event.mRanges->Length());
  1.2054 +#endif // DEBUG_ANDROID_IME
  1.2055 +
  1.2056 +            DispatchEvent(&event);
  1.2057 +
  1.2058 +            // Notify SelectionHandler of final caret position
  1.2059 +            // Required in cases of keyboards providing autoCorrections
  1.2060 +            AndroidGeckoEvent* broadcastEvent = AndroidGeckoEvent::MakeBroadcastEvent(
  1.2061 +                NS_LITERAL_CSTRING("TextSelection:UpdateCaretPos"),
  1.2062 +                NS_LITERAL_CSTRING(""));
  1.2063 +            nsAppShell::gAppShell->PostEvent(broadcastEvent);
  1.2064 +        }
  1.2065 +        break;
  1.2066 +    case AndroidGeckoEvent::IME_REMOVE_COMPOSITION:
  1.2067 +        {
  1.2068 +            /*
  1.2069 +             *  Remove any previous composition.  This is only used for
  1.2070 +             *    visual indication and does not affect the text content.
  1.2071 +             *
  1.2072 +             *  Selection and text updates are masked so the result of
  1.2073 +             *    temporary events are not passed on to Java
  1.2074 +             */
  1.2075 +            AutoIMEMask selMask(mIMEMaskSelectionUpdate);
  1.2076 +            AutoIMEMask textMask(mIMEMaskTextUpdate);
  1.2077 +            RemoveIMEComposition();
  1.2078 +            mIMERanges->Clear();
  1.2079 +        }
  1.2080 +        break;
  1.2081 +    }
  1.2082 +}
  1.2083 +
  1.2084 +nsWindow *
  1.2085 +nsWindow::FindWindowForPoint(const nsIntPoint& pt)
  1.2086 +{
  1.2087 +    if (!mBounds.Contains(pt))
  1.2088 +        return nullptr;
  1.2089 +
  1.2090 +    // children mBounds are relative to their parent
  1.2091 +    nsIntPoint childPoint(pt.x - mBounds.x, pt.y - mBounds.y);
  1.2092 +
  1.2093 +    for (uint32_t i = 0; i < mChildren.Length(); ++i) {
  1.2094 +        if (mChildren[i]->mBounds.Contains(childPoint))
  1.2095 +            return mChildren[i]->FindWindowForPoint(childPoint);
  1.2096 +    }
  1.2097 +
  1.2098 +    return this;
  1.2099 +}
  1.2100 +
  1.2101 +void
  1.2102 +nsWindow::UserActivity()
  1.2103 +{
  1.2104 +  if (!mIdleService) {
  1.2105 +    mIdleService = do_GetService("@mozilla.org/widget/idleservice;1");
  1.2106 +  }
  1.2107 +
  1.2108 +  if (mIdleService) {
  1.2109 +    mIdleService->ResetIdleTimeOut(0);
  1.2110 +  }
  1.2111 +}
  1.2112 +
  1.2113 +NS_IMETHODIMP
  1.2114 +nsWindow::NotifyIME(const IMENotification& aIMENotification)
  1.2115 +{
  1.2116 +    switch (aIMENotification.mMessage) {
  1.2117 +        case REQUEST_TO_COMMIT_COMPOSITION:
  1.2118 +            //ALOGIME("IME: REQUEST_TO_COMMIT_COMPOSITION: s=%d", aState);
  1.2119 +            RemoveIMEComposition();
  1.2120 +            mozilla::widget::android::GeckoAppShell::NotifyIME(REQUEST_TO_COMMIT_COMPOSITION);
  1.2121 +            return NS_OK;
  1.2122 +        case REQUEST_TO_CANCEL_COMPOSITION:
  1.2123 +            ALOGIME("IME: REQUEST_TO_CANCEL_COMPOSITION");
  1.2124 +
  1.2125 +            // Cancel composition on Gecko side
  1.2126 +            if (mIMEComposing) {
  1.2127 +                nsRefPtr<nsWindow> kungFuDeathGrip(this);
  1.2128 +
  1.2129 +                WidgetCompositionEvent updateEvent(true, NS_COMPOSITION_UPDATE,
  1.2130 +                                                   this);
  1.2131 +                InitEvent(updateEvent, nullptr);
  1.2132 +                DispatchEvent(&updateEvent);
  1.2133 +
  1.2134 +                WidgetTextEvent textEvent(true, NS_TEXT_TEXT, this);
  1.2135 +                InitEvent(textEvent, nullptr);
  1.2136 +                DispatchEvent(&textEvent);
  1.2137 +
  1.2138 +                WidgetCompositionEvent compEvent(true, NS_COMPOSITION_END,
  1.2139 +                                                 this);
  1.2140 +                InitEvent(compEvent, nullptr);
  1.2141 +                DispatchEvent(&compEvent);
  1.2142 +            }
  1.2143 +
  1.2144 +            mozilla::widget::android::GeckoAppShell::NotifyIME(REQUEST_TO_CANCEL_COMPOSITION);
  1.2145 +            return NS_OK;
  1.2146 +        case NOTIFY_IME_OF_FOCUS:
  1.2147 +            ALOGIME("IME: NOTIFY_IME_OF_FOCUS");
  1.2148 +            mozilla::widget::android::GeckoAppShell::NotifyIME(NOTIFY_IME_OF_FOCUS);
  1.2149 +            return NS_OK;
  1.2150 +        case NOTIFY_IME_OF_BLUR:
  1.2151 +            ALOGIME("IME: NOTIFY_IME_OF_BLUR");
  1.2152 +
  1.2153 +            // Mask events because we lost focus. On the next focus event,
  1.2154 +            // Gecko will notify Java, and Java will send an acknowledge focus
  1.2155 +            // event back to Gecko. That is where we unmask event handling
  1.2156 +            mIMEMaskEventsCount++;
  1.2157 +            mIMEComposing = false;
  1.2158 +            mIMEComposingText.Truncate();
  1.2159 +
  1.2160 +            mozilla::widget::android::GeckoAppShell::NotifyIME(NOTIFY_IME_OF_BLUR);
  1.2161 +            return NS_OK;
  1.2162 +        case NOTIFY_IME_OF_SELECTION_CHANGE:
  1.2163 +            if (mIMEMaskSelectionUpdate) {
  1.2164 +                return NS_OK;
  1.2165 +            }
  1.2166 +
  1.2167 +            ALOGIME("IME: NOTIFY_IME_OF_SELECTION_CHANGE");
  1.2168 +
  1.2169 +            PostFlushIMEChanges();
  1.2170 +            mIMESelectionChanged = true;
  1.2171 +            return NS_OK;
  1.2172 +        case NOTIFY_IME_OF_TEXT_CHANGE:
  1.2173 +            return NotifyIMEOfTextChange(aIMENotification);
  1.2174 +        default:
  1.2175 +            return NS_ERROR_NOT_IMPLEMENTED;
  1.2176 +    }
  1.2177 +}
  1.2178 +
  1.2179 +NS_IMETHODIMP_(void)
  1.2180 +nsWindow::SetInputContext(const InputContext& aContext,
  1.2181 +                          const InputContextAction& aAction)
  1.2182 +{
  1.2183 +    nsWindow *top = TopWindow();
  1.2184 +    if (top && top->mFocus && this != top->mFocus) {
  1.2185 +        // We are using an IME event later to notify Java, and the IME event
  1.2186 +        // will be processed by the focused window. Therefore, to ensure the
  1.2187 +        // IME event uses the correct mInputContext, we need to let the focused
  1.2188 +        // window process SetInputContext
  1.2189 +        top->mFocus->SetInputContext(aContext, aAction);
  1.2190 +        return;
  1.2191 +    }
  1.2192 +
  1.2193 +    ALOGIME("IME: SetInputContext: s=0x%X, 0x%X, action=0x%X, 0x%X",
  1.2194 +            aContext.mIMEState.mEnabled, aContext.mIMEState.mOpen,
  1.2195 +            aAction.mCause, aAction.mFocusChange);
  1.2196 +
  1.2197 +    mInputContext = aContext;
  1.2198 +
  1.2199 +    // Ensure that opening the virtual keyboard is allowed for this specific
  1.2200 +    // InputContext depending on the content.ime.strict.policy pref
  1.2201 +    if (aContext.mIMEState.mEnabled != IMEState::DISABLED && 
  1.2202 +        aContext.mIMEState.mEnabled != IMEState::PLUGIN &&
  1.2203 +        Preferences::GetBool("content.ime.strict_policy", false) &&
  1.2204 +        !aAction.ContentGotFocusByTrustedCause() &&
  1.2205 +        !aAction.UserMightRequestOpenVKB()) {
  1.2206 +        return;
  1.2207 +    }
  1.2208 +
  1.2209 +    IMEState::Enabled enabled = aContext.mIMEState.mEnabled;
  1.2210 +
  1.2211 +    // Only show the virtual keyboard for plugins if mOpen is set appropriately.
  1.2212 +    // This avoids showing it whenever a plugin is focused. Bug 747492
  1.2213 +    if (aContext.mIMEState.mEnabled == IMEState::PLUGIN &&
  1.2214 +        aContext.mIMEState.mOpen != IMEState::OPEN) {
  1.2215 +        enabled = IMEState::DISABLED;
  1.2216 +    }
  1.2217 +
  1.2218 +    mInputContext.mIMEState.mEnabled = enabled;
  1.2219 +
  1.2220 +    if (enabled == IMEState::ENABLED && aAction.UserMightRequestOpenVKB()) {
  1.2221 +        // Don't reset keyboard when we should simply open the vkb
  1.2222 +        mozilla::widget::android::GeckoAppShell::NotifyIME(AndroidBridge::NOTIFY_IME_OPEN_VKB);
  1.2223 +        return;
  1.2224 +    }
  1.2225 +
  1.2226 +    if (mIMEUpdatingContext) {
  1.2227 +        return;
  1.2228 +    }
  1.2229 +    AndroidGeckoEvent *event = AndroidGeckoEvent::MakeIMEEvent(
  1.2230 +            AndroidGeckoEvent::IME_UPDATE_CONTEXT);
  1.2231 +    nsAppShell::gAppShell->PostEvent(event);
  1.2232 +    mIMEUpdatingContext = true;
  1.2233 +}
  1.2234 +
  1.2235 +NS_IMETHODIMP_(InputContext)
  1.2236 +nsWindow::GetInputContext()
  1.2237 +{
  1.2238 +    nsWindow *top = TopWindow();
  1.2239 +    if (top && top->mFocus && this != top->mFocus) {
  1.2240 +        // We let the focused window process SetInputContext,
  1.2241 +        // so we should let it process GetInputContext as well.
  1.2242 +        return top->mFocus->GetInputContext();
  1.2243 +    }
  1.2244 +    InputContext context = mInputContext;
  1.2245 +    context.mIMEState.mOpen = IMEState::OPEN_STATE_NOT_SUPPORTED;
  1.2246 +    // We assume that there is only one context per process on Android
  1.2247 +    context.mNativeIMEContext = nullptr;
  1.2248 +    return context;
  1.2249 +}
  1.2250 +
  1.2251 +void
  1.2252 +nsWindow::PostFlushIMEChanges()
  1.2253 +{
  1.2254 +    if (!mIMETextChanges.IsEmpty() || mIMESelectionChanged) {
  1.2255 +        // Already posted
  1.2256 +        return;
  1.2257 +    }
  1.2258 +    AndroidGeckoEvent *event = AndroidGeckoEvent::MakeIMEEvent(
  1.2259 +            AndroidGeckoEvent::IME_FLUSH_CHANGES);
  1.2260 +    nsAppShell::gAppShell->PostEvent(event);
  1.2261 +}
  1.2262 +
  1.2263 +void
  1.2264 +nsWindow::FlushIMEChanges()
  1.2265 +{
  1.2266 +    nsRefPtr<nsWindow> kungFuDeathGrip(this);
  1.2267 +    for (uint32_t i = 0; i < mIMETextChanges.Length(); i++) {
  1.2268 +        IMEChange &change = mIMETextChanges[i];
  1.2269 +
  1.2270 +        WidgetQueryContentEvent event(true, NS_QUERY_TEXT_CONTENT, this);
  1.2271 +        InitEvent(event, nullptr);
  1.2272 +        event.InitForQueryTextContent(change.mStart,
  1.2273 +                                      change.mNewEnd - change.mStart);
  1.2274 +        DispatchEvent(&event);
  1.2275 +        if (!event.mSucceeded)
  1.2276 +            return;
  1.2277 +
  1.2278 +        mozilla::widget::android::GeckoAppShell::NotifyIMEChange(event.mReply.mString,
  1.2279 +                                       change.mStart,
  1.2280 +                                       change.mOldEnd,
  1.2281 +                                       change.mNewEnd);
  1.2282 +    }
  1.2283 +    mIMETextChanges.Clear();
  1.2284 +
  1.2285 +    if (mIMESelectionChanged) {
  1.2286 +        WidgetQueryContentEvent event(true, NS_QUERY_SELECTED_TEXT, this);
  1.2287 +        InitEvent(event, nullptr);
  1.2288 +
  1.2289 +        DispatchEvent(&event);
  1.2290 +        if (!event.mSucceeded)
  1.2291 +            return;
  1.2292 +
  1.2293 +        mozilla::widget::android::GeckoAppShell::NotifyIMEChange(EmptyString(),
  1.2294 +                             (int32_t) event.GetSelectionStart(),
  1.2295 +                             (int32_t) event.GetSelectionEnd(), -1);
  1.2296 +        mIMESelectionChanged = false;
  1.2297 +    }
  1.2298 +}
  1.2299 +
  1.2300 +nsresult
  1.2301 +nsWindow::NotifyIMEOfTextChange(const IMENotification& aIMENotification)
  1.2302 +{
  1.2303 +    MOZ_ASSERT(aIMENotification.mMessage == NOTIFY_IME_OF_TEXT_CHANGE,
  1.2304 +               "NotifyIMEOfTextChange() is called with invaild notification");
  1.2305 +
  1.2306 +    if (mIMEMaskTextUpdate)
  1.2307 +        return NS_OK;
  1.2308 +
  1.2309 +    ALOGIME("IME: NotifyIMEOfTextChange: s=%d, oe=%d, ne=%d",
  1.2310 +            aIMENotification.mTextChangeData.mStartOffset,
  1.2311 +            aIMENotification.mTextChangeData.mOldEndOffset,
  1.2312 +            aIMENotification.mTextChangeData.mNewEndOffset);
  1.2313 +
  1.2314 +    /* Make sure Java's selection is up-to-date */
  1.2315 +    mIMESelectionChanged = false;
  1.2316 +    NotifyIME(NOTIFY_IME_OF_SELECTION_CHANGE);
  1.2317 +    PostFlushIMEChanges();
  1.2318 +
  1.2319 +    mIMETextChanges.AppendElement(IMEChange(aIMENotification));
  1.2320 +    // Now that we added a new range we need to go back and
  1.2321 +    // update all the ranges before that.
  1.2322 +    // Ranges that have offsets which follow this new range
  1.2323 +    // need to be updated to reflect new offsets
  1.2324 +    int32_t delta = aIMENotification.mTextChangeData.AdditionalLength();
  1.2325 +    for (int32_t i = mIMETextChanges.Length() - 2; i >= 0; i--) {
  1.2326 +        IMEChange &previousChange = mIMETextChanges[i];
  1.2327 +        if (previousChange.mStart >
  1.2328 +                static_cast<int32_t>(
  1.2329 +                    aIMENotification.mTextChangeData.mOldEndOffset)) {
  1.2330 +            previousChange.mStart += delta;
  1.2331 +            previousChange.mOldEnd += delta;
  1.2332 +            previousChange.mNewEnd += delta;
  1.2333 +        }
  1.2334 +    }
  1.2335 +
  1.2336 +    // Now go through all ranges to merge any ranges that are connected
  1.2337 +    // srcIndex is the index of the range to merge from
  1.2338 +    // dstIndex is the index of the range to potentially merge into
  1.2339 +    int32_t srcIndex = mIMETextChanges.Length() - 1;
  1.2340 +    int32_t dstIndex = srcIndex;
  1.2341 +
  1.2342 +    while (--dstIndex >= 0) {
  1.2343 +        IMEChange &src = mIMETextChanges[srcIndex];
  1.2344 +        IMEChange &dst = mIMETextChanges[dstIndex];
  1.2345 +        // When merging a more recent change into an older
  1.2346 +        // change, we need to compare recent change's (start, oldEnd)
  1.2347 +        // range to the older change's (start, newEnd)
  1.2348 +        if (src.mOldEnd < dst.mStart || dst.mNewEnd < src.mStart) {
  1.2349 +            // No overlap between ranges
  1.2350 +            continue;
  1.2351 +        }
  1.2352 +        // When merging two ranges, there are generally four posibilities:
  1.2353 +        // [----(----]----), (----[----]----),
  1.2354 +        // [----(----)----], (----[----)----]
  1.2355 +        // where [----] is the first range and (----) is the second range
  1.2356 +        // As seen above, the start of the merged range is always the lesser
  1.2357 +        // of the two start offsets. OldEnd and NewEnd then need to be
  1.2358 +        // adjusted separately depending on the case. In any case, the change
  1.2359 +        // in text length of the merged range should be the sum of text length
  1.2360 +        // changes of the two original ranges, i.e.,
  1.2361 +        // newNewEnd - newOldEnd == newEnd1 - oldEnd1 + newEnd2 - oldEnd2
  1.2362 +        dst.mStart = std::min(dst.mStart, src.mStart);
  1.2363 +        if (src.mOldEnd < dst.mNewEnd) {
  1.2364 +            // New range overlaps or is within previous range; merge
  1.2365 +            dst.mNewEnd += src.mNewEnd - src.mOldEnd;
  1.2366 +        } else { // src.mOldEnd >= dst.mNewEnd
  1.2367 +            // New range overlaps previous range; merge
  1.2368 +            dst.mOldEnd += src.mOldEnd - dst.mNewEnd;
  1.2369 +            dst.mNewEnd = src.mNewEnd;
  1.2370 +        }
  1.2371 +        // src merged to dst; delete src.
  1.2372 +        mIMETextChanges.RemoveElementAt(srcIndex);
  1.2373 +        // Any ranges that we skip over between src and dst are not mergeable
  1.2374 +        // so we can safely continue the merge starting at dst
  1.2375 +        srcIndex = dstIndex;
  1.2376 +    }
  1.2377 +    return NS_OK;
  1.2378 +}
  1.2379 +
  1.2380 +nsIMEUpdatePreference
  1.2381 +nsWindow::GetIMEUpdatePreference()
  1.2382 +{
  1.2383 +    return nsIMEUpdatePreference(
  1.2384 +        nsIMEUpdatePreference::NOTIFY_SELECTION_CHANGE |
  1.2385 +        nsIMEUpdatePreference::NOTIFY_TEXT_CHANGE);
  1.2386 +}
  1.2387 +
  1.2388 +void
  1.2389 +nsWindow::DrawWindowUnderlay(LayerManagerComposite* aManager, nsIntRect aRect)
  1.2390 +{
  1.2391 +    JNIEnv *env = GetJNIForThread();
  1.2392 +
  1.2393 +    AutoLocalJNIFrame jniFrame(env);
  1.2394 +
  1.2395 +    mozilla::widget::android::GeckoLayerClient* client = AndroidBridge::Bridge()->GetLayerClient();
  1.2396 +    if (!client || client->isNull()) {
  1.2397 +        ALOG_BRIDGE("Exceptional Exit: %s", __PRETTY_FUNCTION__);
  1.2398 +        return;
  1.2399 +    }
  1.2400 +
  1.2401 +    jobject frameObj = client->CreateFrame();
  1.2402 +    if (!frameObj) {
  1.2403 +        NS_WARNING("Warning: unable to obtain a LayerRenderer frame; aborting window underlay draw");
  1.2404 +        return;
  1.2405 +    }
  1.2406 +
  1.2407 +    mLayerRendererFrame.Init(env, frameObj);
  1.2408 +    if (!WidgetPaintsBackground()) {
  1.2409 +        return;
  1.2410 +    }
  1.2411 +
  1.2412 +    gl::GLContext* gl = static_cast<CompositorOGL*>(aManager->GetCompositor())->gl();
  1.2413 +    gl::ScopedGLState scopedScissorTestState(gl, LOCAL_GL_SCISSOR_TEST);
  1.2414 +    gl::ScopedScissorRect scopedScissorRectState(gl);
  1.2415 +
  1.2416 +    client->ActivateProgram();
  1.2417 +    if (!mLayerRendererFrame.BeginDrawing(&jniFrame)) return;
  1.2418 +    if (!mLayerRendererFrame.DrawBackground(&jniFrame)) return;
  1.2419 +    client->DeactivateProgram(); // redundant, but in case somebody adds code after this...
  1.2420 +}
  1.2421 +
  1.2422 +void
  1.2423 +nsWindow::DrawWindowOverlay(LayerManagerComposite* aManager, nsIntRect aRect)
  1.2424 +{
  1.2425 +    PROFILER_LABEL("nsWindow", "DrawWindowOverlay");
  1.2426 +    JNIEnv *env = GetJNIForThread();
  1.2427 +
  1.2428 +    AutoLocalJNIFrame jniFrame(env);
  1.2429 +
  1.2430 +    if (mLayerRendererFrame.isNull()) {
  1.2431 +        NS_WARNING("Warning: do not have a LayerRenderer frame; aborting window overlay draw");
  1.2432 +        return;
  1.2433 +    }
  1.2434 +
  1.2435 +    mozilla::widget::android::GeckoLayerClient* client = AndroidBridge::Bridge()->GetLayerClient();
  1.2436 +
  1.2437 +    gl::GLContext* gl = static_cast<CompositorOGL*>(aManager->GetCompositor())->gl();
  1.2438 +    gl::ScopedGLState scopedScissorTestState(gl, LOCAL_GL_SCISSOR_TEST);
  1.2439 +    gl::ScopedScissorRect scopedScissorRectState(gl);
  1.2440 +
  1.2441 +    client->ActivateProgram();
  1.2442 +    if (!mLayerRendererFrame.DrawForeground(&jniFrame)) return;
  1.2443 +    if (!mLayerRendererFrame.EndDrawing(&jniFrame)) return;
  1.2444 +    client->DeactivateProgram();
  1.2445 +    mLayerRendererFrame.Dispose(env);
  1.2446 +}
  1.2447 +
  1.2448 +// off-main-thread compositor fields and functions
  1.2449 +
  1.2450 +StaticRefPtr<mozilla::layers::APZCTreeManager> nsWindow::sApzcTreeManager;
  1.2451 +StaticRefPtr<mozilla::layers::LayerManager> nsWindow::sLayerManager;
  1.2452 +StaticRefPtr<mozilla::layers::CompositorParent> nsWindow::sCompositorParent;
  1.2453 +StaticRefPtr<mozilla::layers::CompositorChild> nsWindow::sCompositorChild;
  1.2454 +bool nsWindow::sCompositorPaused = true;
  1.2455 +
  1.2456 +void
  1.2457 +nsWindow::SetCompositor(mozilla::layers::LayerManager* aLayerManager,
  1.2458 +                        mozilla::layers::CompositorParent* aCompositorParent,
  1.2459 +                        mozilla::layers::CompositorChild* aCompositorChild)
  1.2460 +{
  1.2461 +    sLayerManager = aLayerManager;
  1.2462 +    sCompositorParent = aCompositorParent;
  1.2463 +    sCompositorChild = aCompositorChild;
  1.2464 +}
  1.2465 +
  1.2466 +void
  1.2467 +nsWindow::ScheduleComposite()
  1.2468 +{
  1.2469 +    if (sCompositorParent) {
  1.2470 +        sCompositorParent->ScheduleRenderOnCompositorThread();
  1.2471 +    }
  1.2472 +}
  1.2473 +
  1.2474 +void
  1.2475 +nsWindow::ScheduleResumeComposition(int width, int height)
  1.2476 +{
  1.2477 +    if (sCompositorParent && sCompositorParent->ScheduleResumeOnCompositorThread(width, height)) {
  1.2478 +        sCompositorPaused = false;
  1.2479 +    }
  1.2480 +}
  1.2481 +
  1.2482 +void
  1.2483 +nsWindow::ForceIsFirstPaint()
  1.2484 +{
  1.2485 +    if (sCompositorParent) {
  1.2486 +        sCompositorParent->ForceIsFirstPaint();
  1.2487 +    }
  1.2488 +}
  1.2489 +
  1.2490 +float
  1.2491 +nsWindow::ComputeRenderIntegrity()
  1.2492 +{
  1.2493 +    if (sCompositorParent) {
  1.2494 +        return sCompositorParent->ComputeRenderIntegrity();
  1.2495 +    }
  1.2496 +
  1.2497 +    return 1.f;
  1.2498 +}
  1.2499 +
  1.2500 +bool
  1.2501 +nsWindow::WidgetPaintsBackground()
  1.2502 +{
  1.2503 +    static bool sWidgetPaintsBackground = true;
  1.2504 +    static bool sWidgetPaintsBackgroundPrefCached = false;
  1.2505 +
  1.2506 +    if (!sWidgetPaintsBackgroundPrefCached) {
  1.2507 +        sWidgetPaintsBackgroundPrefCached = true;
  1.2508 +        mozilla::Preferences::AddBoolVarCache(&sWidgetPaintsBackground,
  1.2509 +                                              "android.widget_paints_background",
  1.2510 +                                              true);
  1.2511 +    }
  1.2512 +
  1.2513 +    return sWidgetPaintsBackground;
  1.2514 +}
  1.2515 +
  1.2516 +bool
  1.2517 +nsWindow::NeedsPaint()
  1.2518 +{
  1.2519 +  if (sCompositorPaused || FindTopLevel() != nsWindow::TopWindow() || !GetLayerManager(nullptr)) {
  1.2520 +    return false;
  1.2521 +  }
  1.2522 +  return nsIWidget::NeedsPaint();
  1.2523 +}
  1.2524 +
  1.2525 +CompositorParent*
  1.2526 +nsWindow::NewCompositorParent(int aSurfaceWidth, int aSurfaceHeight)
  1.2527 +{
  1.2528 +    return new CompositorParent(this, true, aSurfaceWidth, aSurfaceHeight);
  1.2529 +}
  1.2530 +
  1.2531 +mozilla::layers::APZCTreeManager*
  1.2532 +nsWindow::GetAPZCTreeManager()
  1.2533 +{
  1.2534 +    if (!sApzcTreeManager) {
  1.2535 +        CompositorParent* compositor = sCompositorParent;
  1.2536 +        if (!compositor) {
  1.2537 +            return nullptr;
  1.2538 +        }
  1.2539 +        uint64_t rootLayerTreeId = compositor->RootLayerTreeId();
  1.2540 +        CompositorParent::SetControllerForLayerTree(rootLayerTreeId, AndroidBridge::Bridge());
  1.2541 +        sApzcTreeManager = CompositorParent::GetAPZCTreeManager(rootLayerTreeId);
  1.2542 +    }
  1.2543 +    return sApzcTreeManager;
  1.2544 +}
  1.2545 +
  1.2546 +uint64_t
  1.2547 +nsWindow::RootLayerTreeId()
  1.2548 +{
  1.2549 +    MOZ_ASSERT(sCompositorParent);
  1.2550 +    return sCompositorParent->RootLayerTreeId();
  1.2551 +}

mercurial