forked from mirrors/gecko-dev
		
	
		
			
				
	
	
		
			562 lines
		
	
	
	
		
			17 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			562 lines
		
	
	
	
		
			17 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 | 
						|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
 | 
						|
/* This Source Code Form is subject to the terms of the Mozilla Public
 | 
						|
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 | 
						|
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 | 
						|
 | 
						|
#include "HardwareKeyHandler.h"
 | 
						|
#include "mozilla/BasicEvents.h"
 | 
						|
#include "mozilla/ClearOnShutdown.h"
 | 
						|
#include "mozilla/dom/KeyboardEvent.h"
 | 
						|
#include "mozilla/dom/TabParent.h"
 | 
						|
#include "mozilla/EventDispatcher.h"
 | 
						|
#include "mozilla/EventStateManager.h"
 | 
						|
#include "mozilla/TextEvents.h"
 | 
						|
#include "nsDeque.h"
 | 
						|
#include "nsFocusManager.h"
 | 
						|
#include "nsFrameLoader.h"
 | 
						|
#include "nsIContent.h"
 | 
						|
#include "nsIDOMHTMLDocument.h"
 | 
						|
#include "nsIDOMHTMLElement.h"
 | 
						|
#include "nsPIDOMWindow.h"
 | 
						|
#include "nsPresContext.h"
 | 
						|
#include "nsPresShell.h"
 | 
						|
 | 
						|
namespace mozilla {
 | 
						|
 | 
						|
using namespace dom;
 | 
						|
 | 
						|
NS_IMPL_ISUPPORTS(HardwareKeyHandler, nsIHardwareKeyHandler)
 | 
						|
 | 
						|
StaticRefPtr<HardwareKeyHandler> HardwareKeyHandler::sInstance;
 | 
						|
 | 
						|
HardwareKeyHandler::HardwareKeyHandler()
 | 
						|
  : mInputMethodAppConnected(false)
 | 
						|
{
 | 
						|
}
 | 
						|
 | 
						|
HardwareKeyHandler::~HardwareKeyHandler()
 | 
						|
{
 | 
						|
}
 | 
						|
 | 
						|
NS_IMETHODIMP
 | 
						|
HardwareKeyHandler::OnInputMethodAppConnected()
 | 
						|
{
 | 
						|
  if (NS_WARN_IF(mInputMethodAppConnected)) {
 | 
						|
    return NS_ERROR_UNEXPECTED;
 | 
						|
  }
 | 
						|
 | 
						|
  mInputMethodAppConnected = true;
 | 
						|
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
NS_IMETHODIMP
 | 
						|
HardwareKeyHandler::OnInputMethodAppDisconnected()
 | 
						|
{
 | 
						|
  if (NS_WARN_IF(!mInputMethodAppConnected)) {
 | 
						|
    return NS_ERROR_UNEXPECTED;
 | 
						|
  }
 | 
						|
 | 
						|
  mInputMethodAppConnected = false;
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
NS_IMETHODIMP
 | 
						|
HardwareKeyHandler::RegisterListener(nsIHardwareKeyEventListener* aListener)
 | 
						|
{
 | 
						|
  // Make sure the listener is not nullptr and there is no available
 | 
						|
  // hardwareKeyEventListener now
 | 
						|
  if (NS_WARN_IF(!aListener)) {
 | 
						|
    return NS_ERROR_NULL_POINTER;
 | 
						|
  }
 | 
						|
 | 
						|
  if (NS_WARN_IF(mHardwareKeyEventListener)) {
 | 
						|
    return NS_ERROR_ALREADY_INITIALIZED;
 | 
						|
  }
 | 
						|
 | 
						|
  mHardwareKeyEventListener = do_GetWeakReference(aListener);
 | 
						|
 | 
						|
  if (NS_WARN_IF(!mHardwareKeyEventListener)) {
 | 
						|
    return NS_ERROR_NULL_POINTER;
 | 
						|
  }
 | 
						|
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
NS_IMETHODIMP
 | 
						|
HardwareKeyHandler::UnregisterListener()
 | 
						|
{
 | 
						|
  // Clear the HardwareKeyEventListener
 | 
						|
  mHardwareKeyEventListener = nullptr;
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
bool
 | 
						|
HardwareKeyHandler::ForwardKeyToInputMethodApp(nsINode* aTarget,
 | 
						|
                                               WidgetKeyboardEvent* aEvent,
 | 
						|
                                               nsEventStatus* aEventStatus)
 | 
						|
{
 | 
						|
  MOZ_ASSERT(aTarget, "No target provided");
 | 
						|
  MOZ_ASSERT(aEvent, "No event provided");
 | 
						|
 | 
						|
  // No need to forward hardware key event to IME
 | 
						|
  // if key's defaultPrevented is true
 | 
						|
  if (aEvent->mFlags.mDefaultPrevented) {
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
 | 
						|
  // No need to forward hardware key event to IME if IME is disabled
 | 
						|
  if (!mInputMethodAppConnected) {
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
 | 
						|
  // No need to forward hardware key event to IME
 | 
						|
  // if this key event is generated by IME itself(from nsITextInputProcessor)
 | 
						|
  if (aEvent->mIsSynthesizedByTIP) {
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
 | 
						|
  // No need to forward hardware key event to IME
 | 
						|
  // if the key event is handling or already handled
 | 
						|
  if (aEvent->mInputMethodAppState != WidgetKeyboardEvent::eNotHandled) {
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
 | 
						|
  // No need to forward hardware key event to IME
 | 
						|
  // if there is no nsIHardwareKeyEventListener in use
 | 
						|
  nsCOMPtr<nsIHardwareKeyEventListener>
 | 
						|
    keyHandler(do_QueryReferent(mHardwareKeyEventListener));
 | 
						|
  if (!keyHandler) {
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
 | 
						|
  // Set the flags to specify the keyboard event is in forwarding phase.
 | 
						|
  aEvent->mInputMethodAppState = WidgetKeyboardEvent::eHandling;
 | 
						|
 | 
						|
  // For those keypress events coming after their heading keydown's reply
 | 
						|
  // already arrives, they should be dispatched directly instead of
 | 
						|
  // being stored into the event queue. Otherwise, without the heading keydown
 | 
						|
  // in the event queue, the stored keypress will never be withdrawn to be fired.
 | 
						|
  if (aEvent->mMessage == eKeyPress && mEventQueue.IsEmpty()) {
 | 
						|
    DispatchKeyPress(aTarget, *aEvent, *aEventStatus);
 | 
						|
    return true;
 | 
						|
  }
 | 
						|
 | 
						|
  // Push the key event into queue for reuse when its reply arrives.
 | 
						|
  KeyboardInfo* copiedInfo =
 | 
						|
    new KeyboardInfo(aTarget,
 | 
						|
                     *aEvent,
 | 
						|
                     aEventStatus ? *aEventStatus : nsEventStatus_eIgnore);
 | 
						|
 | 
						|
  // No need to forward hardware key event to IME if the event queue is full
 | 
						|
  if (!mEventQueue.Push(copiedInfo)) {
 | 
						|
    delete copiedInfo;
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
 | 
						|
  // We only forward keydown and keyup event to input-method-app
 | 
						|
  // because input-method-app will generate keypress by itself.
 | 
						|
  if (aEvent->mMessage == eKeyPress) {
 | 
						|
    return true;
 | 
						|
  }
 | 
						|
 | 
						|
  // Create a keyboard event to pass into
 | 
						|
  // nsIHardwareKeyEventListener.onHardwareKey
 | 
						|
  nsCOMPtr<EventTarget> eventTarget = do_QueryInterface(aTarget);
 | 
						|
  nsPresContext* presContext = GetPresContext(aTarget);
 | 
						|
  RefPtr<KeyboardEvent> keyboardEvent =
 | 
						|
    NS_NewDOMKeyboardEvent(eventTarget, presContext, aEvent->AsKeyboardEvent());
 | 
						|
  // Duplicate the internal event data in the heap for the keyboardEvent,
 | 
						|
  // or the internal data from |aEvent| in the stack may be destroyed by others.
 | 
						|
  keyboardEvent->DuplicatePrivateData();
 | 
						|
 | 
						|
  // Forward the created keyboard event to input-method-app
 | 
						|
  bool isSent = false;
 | 
						|
  keyHandler->OnHardwareKey(keyboardEvent, &isSent);
 | 
						|
 | 
						|
  // Pop the pending key event if it can't be forwarded
 | 
						|
  if (!isSent) {
 | 
						|
    mEventQueue.RemoveFront();
 | 
						|
  }
 | 
						|
 | 
						|
  return isSent;
 | 
						|
}
 | 
						|
 | 
						|
NS_IMETHODIMP
 | 
						|
HardwareKeyHandler::OnHandledByInputMethodApp(const nsAString& aType,
 | 
						|
                                              uint16_t aDefaultPrevented)
 | 
						|
{
 | 
						|
  // We can not handle this reply because the pending events had been already
 | 
						|
  // removed from the forwarding queue before this reply arrives.
 | 
						|
  if (mEventQueue.IsEmpty()) {
 | 
						|
    return NS_OK;
 | 
						|
  }
 | 
						|
 | 
						|
  RefPtr<KeyboardInfo> keyInfo = mEventQueue.PopFront();
 | 
						|
 | 
						|
  // Only allow keydown and keyup to call this method
 | 
						|
  if (NS_WARN_IF(aType.EqualsLiteral("keydown") &&
 | 
						|
                 keyInfo->mEvent.mMessage != eKeyDown) ||
 | 
						|
      NS_WARN_IF(aType.EqualsLiteral("keyup") &&
 | 
						|
                 keyInfo->mEvent.mMessage != eKeyUp)) {
 | 
						|
    return NS_ERROR_INVALID_ARG;
 | 
						|
  }
 | 
						|
 | 
						|
  // The value of defaultPrevented depends on whether or not
 | 
						|
  // the key is consumed by input-method-app
 | 
						|
  SetDefaultPrevented(keyInfo->mEvent, aDefaultPrevented);
 | 
						|
 | 
						|
  // Set the flag to specify the reply phase
 | 
						|
  keyInfo->mEvent.mInputMethodAppState = WidgetKeyboardEvent::eHandled;
 | 
						|
 | 
						|
  // Check whether the event is still valid to be fired
 | 
						|
  if (CanDispatchEvent(keyInfo->mTarget, keyInfo->mEvent)) {
 | 
						|
    // If the key's defaultPrevented is true, it means that the
 | 
						|
    // input-method-app has already consumed this key,
 | 
						|
    // so we can dispatch |mozbrowserafterkey*| directly if
 | 
						|
    // preference "dom.beforeAfterKeyboardEvent.enabled" is enabled.
 | 
						|
    if (keyInfo->mEvent.mFlags.mDefaultPrevented) {
 | 
						|
      DispatchAfterKeyEvent(keyInfo->mTarget, keyInfo->mEvent);
 | 
						|
    // Otherwise, it means that input-method-app doesn't handle this key,
 | 
						|
    // so we need to dispatch it to its current event target.
 | 
						|
    } else {
 | 
						|
      DispatchToTargetApp(keyInfo->mTarget,
 | 
						|
                          keyInfo->mEvent,
 | 
						|
                          keyInfo->mStatus);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  // No need to do further processing if the event is not keydown
 | 
						|
  if (keyInfo->mEvent.mMessage != eKeyDown) {
 | 
						|
    return NS_OK;
 | 
						|
  }
 | 
						|
 | 
						|
  // Update the latest keydown data:
 | 
						|
  //   Release the holding reference to the previous keydown's data and
 | 
						|
  //   add a reference count to the current keydown's data.
 | 
						|
  mLatestKeyDownInfo = keyInfo;
 | 
						|
 | 
						|
  // Handle the pending keypress event once keydown's reply arrives:
 | 
						|
  // It may have many keypress events per keydown on some platforms,
 | 
						|
  // so we use loop to dispatch keypress events.
 | 
						|
  // (But Gonk dispatch only one keypress per keydown)
 | 
						|
  // However, if there is no keypress after this keydown,
 | 
						|
  // then those following keypress will be handled in
 | 
						|
  // ForwardKeyToInputMethodApp directly.
 | 
						|
  for (KeyboardInfo* keypressInfo;
 | 
						|
       !mEventQueue.IsEmpty() &&
 | 
						|
       (keypressInfo = mEventQueue.PeekFront()) &&
 | 
						|
       keypressInfo->mEvent.mMessage == eKeyPress;
 | 
						|
       mEventQueue.RemoveFront()) {
 | 
						|
    DispatchKeyPress(keypressInfo->mTarget,
 | 
						|
                     keypressInfo->mEvent,
 | 
						|
                     keypressInfo->mStatus);
 | 
						|
  }
 | 
						|
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
bool
 | 
						|
HardwareKeyHandler::DispatchKeyPress(nsINode* aTarget,
 | 
						|
                                     WidgetKeyboardEvent& aEvent,
 | 
						|
                                     nsEventStatus& aStatus)
 | 
						|
{
 | 
						|
  MOZ_ASSERT(aTarget, "No target provided");
 | 
						|
  MOZ_ASSERT(aEvent.mMessage == eKeyPress, "Event is not keypress");
 | 
						|
 | 
						|
  // No need to dispatch keypress to the event target
 | 
						|
  // if the keydown event is consumed by the input-method-app.
 | 
						|
  if (mLatestKeyDownInfo &&
 | 
						|
      mLatestKeyDownInfo->mEvent.mFlags.mDefaultPrevented) {
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
 | 
						|
  // No need to dispatch keypress to the event target
 | 
						|
  // if the previous keydown event is modifier key's
 | 
						|
  if (mLatestKeyDownInfo &&
 | 
						|
      mLatestKeyDownInfo->mEvent.IsModifierKeyEvent()) {
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
 | 
						|
  // No need to dispatch keypress to the event target
 | 
						|
  // if it's invalid to be dispatched
 | 
						|
  if (!CanDispatchEvent(aTarget, aEvent)) {
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
 | 
						|
  // Set the flag to specify the reply phase.
 | 
						|
  aEvent.mInputMethodAppState = WidgetKeyboardEvent::eHandled;
 | 
						|
 | 
						|
  // Dispatch the pending keypress event
 | 
						|
  bool ret = DispatchToTargetApp(aTarget, aEvent, aStatus);
 | 
						|
 | 
						|
  // Re-trigger EventStateManager::PostHandleKeyboardEvent for keypress
 | 
						|
  PostHandleKeyboardEvent(aTarget, aEvent, aStatus);
 | 
						|
 | 
						|
  return ret;
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
HardwareKeyHandler::DispatchAfterKeyEvent(nsINode* aTarget,
 | 
						|
                                          WidgetKeyboardEvent& aEvent)
 | 
						|
{
 | 
						|
  MOZ_ASSERT(aTarget, "No target provided");
 | 
						|
 | 
						|
  if (!PresShell::BeforeAfterKeyboardEventEnabled() ||
 | 
						|
      aEvent.mMessage == eKeyPress) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  nsCOMPtr<nsIPresShell> presShell = GetPresShell(aTarget);
 | 
						|
  if (NS_WARN_IF(presShell)) {
 | 
						|
    presShell->DispatchAfterKeyboardEvent(aTarget,
 | 
						|
                                          aEvent,
 | 
						|
                                          aEvent.mFlags.mDefaultPrevented);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
bool
 | 
						|
HardwareKeyHandler::DispatchToTargetApp(nsINode* aTarget,
 | 
						|
                                        WidgetKeyboardEvent& aEvent,
 | 
						|
                                        nsEventStatus& aStatus)
 | 
						|
{
 | 
						|
  MOZ_ASSERT(aTarget, "No target provided");
 | 
						|
 | 
						|
  // Get current focused element as the event target
 | 
						|
  nsCOMPtr<nsIContent> currentTarget = GetCurrentTarget();
 | 
						|
  if (NS_WARN_IF(!currentTarget)) {
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
 | 
						|
  // The event target should be set to the current focused element.
 | 
						|
  // However, it might have security issue if the event is dispatched to
 | 
						|
  // the unexpected application, and it might cause unexpected operation
 | 
						|
  // in the new app.
 | 
						|
  nsCOMPtr<nsPIDOMWindowOuter> originalRootWindow = GetRootWindow(aTarget);
 | 
						|
  nsCOMPtr<nsPIDOMWindowOuter> currentRootWindow = GetRootWindow(currentTarget);
 | 
						|
  if (currentRootWindow != originalRootWindow) {
 | 
						|
    NS_WARNING("The root window is changed during the event is dispatching");
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
 | 
						|
  // If the current focused element is still in the same app,
 | 
						|
  // then we can use it as the current target to dispatch event.
 | 
						|
  nsCOMPtr<nsIPresShell> presShell = GetPresShell(currentTarget);
 | 
						|
  if (!presShell) {
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
 | 
						|
  if (!presShell->CanDispatchEvent(&aEvent)) {
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
 | 
						|
  // In-process case: the event target is in the current process
 | 
						|
  if (!PresShell::IsTargetIframe(currentTarget)) {
 | 
						|
    DispatchToCurrentProcess(presShell, currentTarget, aEvent, aStatus);
 | 
						|
 | 
						|
    if (presShell->CanDispatchEvent(&aEvent)) {
 | 
						|
      DispatchAfterKeyEvent(aTarget, aEvent);
 | 
						|
    }
 | 
						|
 | 
						|
    return true;
 | 
						|
  }
 | 
						|
 | 
						|
  // OOP case: the event target is in the child process
 | 
						|
  return DispatchToCrossProcess(aTarget, aEvent);
 | 
						|
 | 
						|
  // After the oop target receives the event from TabChild::RecvRealKeyEvent
 | 
						|
  // and return the result through TabChild::SendDispatchAfterKeyboardEvent,
 | 
						|
  // the |mozbrowserafterkey*| will be fired from
 | 
						|
  // TabParent::RecvDispatchAfterKeyboardEvent, so we don't need to dispatch
 | 
						|
  // |mozbrowserafterkey*| by ourselves in this module.
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
HardwareKeyHandler::DispatchToCurrentProcess(nsIPresShell* presShell,
 | 
						|
                                             nsIContent* aTarget,
 | 
						|
                                             WidgetKeyboardEvent& aEvent,
 | 
						|
                                             nsEventStatus& aStatus)
 | 
						|
{
 | 
						|
  EventDispatcher::Dispatch(aTarget, presShell->GetPresContext(),
 | 
						|
                            &aEvent, nullptr, &aStatus, nullptr);
 | 
						|
}
 | 
						|
 | 
						|
bool
 | 
						|
HardwareKeyHandler::DispatchToCrossProcess(nsINode* aTarget,
 | 
						|
                                           WidgetKeyboardEvent& aEvent)
 | 
						|
{
 | 
						|
  nsCOMPtr<nsIFrameLoaderOwner> remoteLoaderOwner = do_QueryInterface(aTarget);
 | 
						|
  if (NS_WARN_IF(!remoteLoaderOwner)) {
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
 | 
						|
  RefPtr<nsFrameLoader> remoteFrameLoader =
 | 
						|
    remoteLoaderOwner->GetFrameLoader();
 | 
						|
  if (NS_WARN_IF(!remoteFrameLoader)) {
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
 | 
						|
  uint32_t eventMode;
 | 
						|
  remoteFrameLoader->GetEventMode(&eventMode);
 | 
						|
  if (eventMode == nsIFrameLoader::EVENT_MODE_DONT_FORWARD_TO_CHILD) {
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
 | 
						|
  PBrowserParent* remoteBrowser = remoteFrameLoader->GetRemoteBrowser();
 | 
						|
  TabParent* remote = static_cast<TabParent*>(remoteBrowser);
 | 
						|
  if (NS_WARN_IF(!remote)) {
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
 | 
						|
  return remote->SendRealKeyEvent(aEvent);
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
HardwareKeyHandler::PostHandleKeyboardEvent(nsINode* aTarget,
 | 
						|
                                            WidgetKeyboardEvent& aEvent,
 | 
						|
                                            nsEventStatus& aStatus)
 | 
						|
{
 | 
						|
  MOZ_ASSERT(aTarget, "No target provided");
 | 
						|
 | 
						|
  nsPresContext* presContext = GetPresContext(aTarget);
 | 
						|
 | 
						|
  RefPtr<mozilla::EventStateManager> esm = presContext->EventStateManager();
 | 
						|
  bool dispatchedToChildProcess = PresShell::IsTargetIframe(aTarget);
 | 
						|
  esm->PostHandleKeyboardEvent(&aEvent, aStatus, dispatchedToChildProcess);
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
HardwareKeyHandler::SetDefaultPrevented(WidgetKeyboardEvent& aEvent,
 | 
						|
                                        uint16_t aDefaultPrevented) {
 | 
						|
  if (aDefaultPrevented & DEFAULT_PREVENTED) {
 | 
						|
    aEvent.mFlags.mDefaultPrevented = true;
 | 
						|
  }
 | 
						|
 | 
						|
  if (aDefaultPrevented & DEFAULT_PREVENTED_BY_CHROME) {
 | 
						|
    aEvent.mFlags.mDefaultPreventedByChrome = true;
 | 
						|
  }
 | 
						|
 | 
						|
  if (aDefaultPrevented & DEFAULT_PREVENTED_BY_CONTENT) {
 | 
						|
    aEvent.mFlags.mDefaultPreventedByContent = true;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
bool
 | 
						|
HardwareKeyHandler::CanDispatchEvent(nsINode* aTarget,
 | 
						|
                                     WidgetKeyboardEvent& aEvent)
 | 
						|
{
 | 
						|
  nsCOMPtr<nsIPresShell> presShell = GetPresShell(aTarget);
 | 
						|
  if (NS_WARN_IF(!presShell)) {
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
  return presShell->CanDispatchEvent(&aEvent);
 | 
						|
}
 | 
						|
 | 
						|
already_AddRefed<nsPIDOMWindowOuter>
 | 
						|
HardwareKeyHandler::GetRootWindow(nsINode* aNode)
 | 
						|
{
 | 
						|
  // Get nsIPresShell's pointer first
 | 
						|
  nsCOMPtr<nsIPresShell> presShell = GetPresShell(aNode);
 | 
						|
  if (NS_WARN_IF(!presShell)) {
 | 
						|
    return nullptr;
 | 
						|
  }
 | 
						|
  nsCOMPtr<nsPIDOMWindowOuter> rootWindow = presShell->GetRootWindow();
 | 
						|
  return rootWindow.forget();
 | 
						|
}
 | 
						|
 | 
						|
already_AddRefed<nsIContent>
 | 
						|
HardwareKeyHandler::GetCurrentTarget()
 | 
						|
{
 | 
						|
  nsFocusManager* fm = nsFocusManager::GetFocusManager();
 | 
						|
  if (NS_WARN_IF(!fm)) {
 | 
						|
    return nullptr;
 | 
						|
  }
 | 
						|
 | 
						|
  nsCOMPtr<mozIDOMWindowProxy> focusedWindow;
 | 
						|
  fm->GetFocusedWindow(getter_AddRefs(focusedWindow));
 | 
						|
  if (NS_WARN_IF(!focusedWindow)) {
 | 
						|
    return nullptr;
 | 
						|
  }
 | 
						|
 | 
						|
  auto* ourWindow = nsPIDOMWindowOuter::From(focusedWindow);
 | 
						|
 | 
						|
  nsCOMPtr<nsPIDOMWindowOuter> rootWindow = ourWindow->GetPrivateRoot();
 | 
						|
  if (NS_WARN_IF(!rootWindow)) {
 | 
						|
    return nullptr;
 | 
						|
  }
 | 
						|
 | 
						|
  nsCOMPtr<nsPIDOMWindowOuter> focusedFrame;
 | 
						|
  nsCOMPtr<nsIContent> focusedContent =
 | 
						|
    fm->GetFocusedDescendant(rootWindow, true, getter_AddRefs(focusedFrame));
 | 
						|
 | 
						|
  // If there is no focus, then we use document body instead
 | 
						|
  if (NS_WARN_IF(!focusedContent || !focusedContent->GetPrimaryFrame())) {
 | 
						|
    nsIDocument* document = ourWindow->GetExtantDoc();
 | 
						|
    if (NS_WARN_IF(!document)) {
 | 
						|
      return nullptr;
 | 
						|
    }
 | 
						|
 | 
						|
    focusedContent = document->GetRootElement();
 | 
						|
 | 
						|
    nsCOMPtr<nsIDOMHTMLDocument> htmlDocument = do_QueryInterface(document);
 | 
						|
    if (htmlDocument) {
 | 
						|
      nsCOMPtr<nsIDOMHTMLElement> body;
 | 
						|
      htmlDocument->GetBody(getter_AddRefs(body));
 | 
						|
      nsCOMPtr<nsIContent> bodyContent = do_QueryInterface(body);
 | 
						|
      if (bodyContent) {
 | 
						|
        focusedContent = bodyContent;
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  return focusedContent ? focusedContent.forget() : nullptr;
 | 
						|
}
 | 
						|
 | 
						|
nsPresContext*
 | 
						|
HardwareKeyHandler::GetPresContext(nsINode* aNode)
 | 
						|
{
 | 
						|
  // Get nsIPresShell's pointer first
 | 
						|
  nsCOMPtr<nsIPresShell> presShell = GetPresShell(aNode);
 | 
						|
  if (NS_WARN_IF(!presShell)) {
 | 
						|
    return nullptr;
 | 
						|
  }
 | 
						|
 | 
						|
  // then use nsIPresShell to get nsPresContext's pointer
 | 
						|
  return presShell->GetPresContext();
 | 
						|
}
 | 
						|
 | 
						|
already_AddRefed<nsIPresShell>
 | 
						|
HardwareKeyHandler::GetPresShell(nsINode* aNode)
 | 
						|
{
 | 
						|
  nsIDocument* doc = aNode->OwnerDoc();
 | 
						|
  if (NS_WARN_IF(!doc)) {
 | 
						|
    return nullptr;
 | 
						|
  }
 | 
						|
 | 
						|
  nsCOMPtr<nsIPresShell> presShell = doc->GetShell();
 | 
						|
  if (NS_WARN_IF(!presShell)) {
 | 
						|
    return nullptr;
 | 
						|
  }
 | 
						|
 | 
						|
  return presShell.forget();
 | 
						|
}
 | 
						|
 | 
						|
/* static */
 | 
						|
already_AddRefed<HardwareKeyHandler>
 | 
						|
HardwareKeyHandler::GetInstance()
 | 
						|
{
 | 
						|
  if (!XRE_IsParentProcess()) {
 | 
						|
    return nullptr;
 | 
						|
  }
 | 
						|
 | 
						|
  if (!sInstance) {
 | 
						|
    sInstance = new HardwareKeyHandler();
 | 
						|
    ClearOnShutdown(&sInstance);
 | 
						|
  }
 | 
						|
 | 
						|
  RefPtr<HardwareKeyHandler> service = sInstance.get();
 | 
						|
  return service.forget();
 | 
						|
}
 | 
						|
 | 
						|
} // namespace mozilla
 |