mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-24 13:21:05 +00:00
Bug 1110030 - part4 - HardwareKeyHandler component. r=masayuki, r=smaug
This commit is contained in:
parent
1c9ec82591
commit
c602eedc7e
@ -674,6 +674,9 @@
|
||||
; InputMethod API
|
||||
@RESPATH@/components/MozKeyboard.js
|
||||
@RESPATH@/components/InputMethod.manifest
|
||||
#ifdef MOZ_B2G
|
||||
@RESPATH@/components/inputmethod.xpt
|
||||
#endif
|
||||
|
||||
@RESPATH@/components/EngineeringMode.manifest
|
||||
@RESPATH@/components/EngineeringModeAPI.js
|
||||
|
566
dom/inputmethod/HardwareKeyHandler.cpp
Normal file
566
dom/inputmethod/HardwareKeyHandler.cpp
Normal file
@ -0,0 +1,566 @@
|
||||
/* -*- 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, "No event 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");
|
||||
MOZ_ASSERT(aEvent, "No event 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");
|
||||
MOZ_ASSERT(aEvent, "No event 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");
|
||||
MOZ_ASSERT(aEvent, "No event 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
|
222
dom/inputmethod/HardwareKeyHandler.h
Normal file
222
dom/inputmethod/HardwareKeyHandler.h
Normal file
@ -0,0 +1,222 @@
|
||||
/* -*- 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/. */
|
||||
|
||||
#ifndef mozilla_HardwareKeyHandler_h_
|
||||
#define mozilla_HardwareKeyHandler_h_
|
||||
|
||||
#include "mozilla/EventForwards.h" // for nsEventStatus
|
||||
#include "mozilla/StaticPtr.h"
|
||||
#include "mozilla/TextEvents.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsDeque.h"
|
||||
#include "nsIHardwareKeyHandler.h"
|
||||
#include "nsIWeakReferenceUtils.h" // for nsWeakPtr
|
||||
|
||||
class nsIContent;
|
||||
class nsINode;
|
||||
class nsIPresShell;
|
||||
class nsPIDOMWindowOuter;
|
||||
class nsPresContext;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
// This module will copy the events' data into its event queue for reuse
|
||||
// after receiving input-method-app's reply, so we use the following struct
|
||||
// for storing these information.
|
||||
// RefCounted<T> is a helper class for adding reference counting mechanism.
|
||||
struct KeyboardInfo : public RefCounted<KeyboardInfo>
|
||||
{
|
||||
nsINode* mTarget;
|
||||
WidgetKeyboardEvent mEvent;
|
||||
nsEventStatus mStatus;
|
||||
|
||||
KeyboardInfo(nsINode* aTarget,
|
||||
WidgetKeyboardEvent& aEvent,
|
||||
nsEventStatus aStatus)
|
||||
: mTarget(aTarget)
|
||||
, mEvent(aEvent)
|
||||
, mStatus(aStatus)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
// The following is the type-safe wrapper around nsDeque
|
||||
// for storing events' data.
|
||||
// The T must be one class that supports reference counting mechanism.
|
||||
// The EventQueueDeallocator will be called in nsDeque::~nsDeque() or
|
||||
// nsDeque::Erase() to deallocate the objects. nsDeque::Erase() will remove
|
||||
// and delete all items in the queue. See more from nsDeque.h.
|
||||
template <class T>
|
||||
class EventQueueDeallocator : public nsDequeFunctor
|
||||
{
|
||||
virtual void* operator() (void* aObject)
|
||||
{
|
||||
RefPtr<T> releaseMe = dont_AddRef(static_cast<T*>(aObject));
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
// The type-safe queue to be used to store the KeyboardInfo data
|
||||
template <class T>
|
||||
class EventQueue : private nsDeque
|
||||
{
|
||||
public:
|
||||
EventQueue()
|
||||
: nsDeque(new EventQueueDeallocator<T>())
|
||||
{
|
||||
};
|
||||
|
||||
~EventQueue()
|
||||
{
|
||||
Clear();
|
||||
}
|
||||
|
||||
inline size_t GetSize()
|
||||
{
|
||||
return nsDeque::GetSize();
|
||||
}
|
||||
|
||||
bool IsEmpty()
|
||||
{
|
||||
return !nsDeque::GetSize();
|
||||
}
|
||||
|
||||
inline bool Push(T* aItem)
|
||||
{
|
||||
MOZ_ASSERT(aItem);
|
||||
NS_ADDREF(aItem);
|
||||
size_t sizeBefore = GetSize();
|
||||
nsDeque::Push(aItem);
|
||||
if (GetSize() != sizeBefore + 1) {
|
||||
NS_RELEASE(aItem);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
inline already_AddRefed<T> PopFront()
|
||||
{
|
||||
RefPtr<T> rv = dont_AddRef(static_cast<T*>(nsDeque::PopFront()));
|
||||
return rv.forget();
|
||||
}
|
||||
|
||||
inline void RemoveFront()
|
||||
{
|
||||
RefPtr<T> releaseMe = PopFront();
|
||||
}
|
||||
|
||||
inline T* PeekFront()
|
||||
{
|
||||
return static_cast<T*>(nsDeque::PeekFront());
|
||||
}
|
||||
|
||||
void Clear()
|
||||
{
|
||||
while (GetSize() > 0) {
|
||||
RemoveFront();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class HardwareKeyHandler : public nsIHardwareKeyHandler
|
||||
{
|
||||
public:
|
||||
HardwareKeyHandler();
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIHARDWAREKEYHANDLER
|
||||
|
||||
static already_AddRefed<HardwareKeyHandler> GetInstance();
|
||||
|
||||
virtual bool ForwardKeyToInputMethodApp(nsINode* aTarget,
|
||||
WidgetKeyboardEvent* aEvent,
|
||||
nsEventStatus* aEventStatus) override;
|
||||
|
||||
private:
|
||||
virtual ~HardwareKeyHandler();
|
||||
|
||||
// Return true if the keypress is successfully dispatched.
|
||||
// Otherwise, return false.
|
||||
bool DispatchKeyPress(nsINode* aTarget,
|
||||
WidgetKeyboardEvent& aEvent,
|
||||
nsEventStatus& aStatus);
|
||||
|
||||
void DispatchAfterKeyEvent(nsINode* aTarget, WidgetKeyboardEvent& aEvent);
|
||||
|
||||
void DispatchToCurrentProcess(nsIPresShell* aPresShell,
|
||||
nsIContent* aTarget,
|
||||
WidgetKeyboardEvent& aEvent,
|
||||
nsEventStatus& aStatus);
|
||||
|
||||
bool DispatchToCrossProcess(nsINode* aTarget, WidgetKeyboardEvent& aEvent);
|
||||
|
||||
// This method will dispatch not only key* event to its event target,
|
||||
// no mather it's in the current process or in its child process,
|
||||
// but also mozbrowserafterkey* to the corresponding target if it needs.
|
||||
// Return true if the key is successfully dispatched.
|
||||
// Otherwise, return false.
|
||||
bool DispatchToTargetApp(nsINode* aTarget,
|
||||
WidgetKeyboardEvent& aEvent,
|
||||
nsEventStatus& aStatus);
|
||||
|
||||
// This method will be called after dispatching keypress to its target,
|
||||
// if the input-method-app doesn't handle the key.
|
||||
// In normal dispatching path, EventStateManager::PostHandleKeyboardEvent
|
||||
// will be called when event is keypress.
|
||||
// However, the ::PostHandleKeyboardEvent mentioned above will be aborted
|
||||
// when we try to forward key event to the input-method-app.
|
||||
// If the input-method-app consumes the key, then we don't need to do anything
|
||||
// because the input-method-app will generate a new key event by itself.
|
||||
// On the other hand, if the input-method-app doesn't consume the key,
|
||||
// then we need to dispatch the key event by ourselves
|
||||
// and call ::PostHandleKeyboardEvent again after the event is forwarded.
|
||||
// Note that the EventStateManager::PreHandleEvent is already called before
|
||||
// forwarding, so we don't need to call it in this module.
|
||||
void PostHandleKeyboardEvent(nsINode* aTarget,
|
||||
WidgetKeyboardEvent& aEvent,
|
||||
nsEventStatus& aStatus);
|
||||
|
||||
void SetDefaultPrevented(WidgetKeyboardEvent& aEvent,
|
||||
uint16_t aDefaultPrevented);
|
||||
|
||||
// Check whether the event is valid to be fired.
|
||||
// This method should be called every time before dispatching next event.
|
||||
bool CanDispatchEvent(nsINode* aTarget,
|
||||
WidgetKeyboardEvent& aEvent);
|
||||
|
||||
already_AddRefed<nsPIDOMWindowOuter> GetRootWindow(nsINode* aNode);
|
||||
|
||||
already_AddRefed<nsIContent> GetCurrentTarget();
|
||||
|
||||
nsPresContext* GetPresContext(nsINode* aNode);
|
||||
|
||||
already_AddRefed<nsIPresShell> GetPresShell(nsINode* aNode);
|
||||
|
||||
static StaticRefPtr<HardwareKeyHandler> sInstance;
|
||||
|
||||
// The event queue is used to store the forwarded keyboard events.
|
||||
// Those stored events will be dispatched if input-method-app doesn't
|
||||
// consume them.
|
||||
EventQueue<KeyboardInfo> mEventQueue;
|
||||
|
||||
// Hold the pointer to the latest keydown's data
|
||||
RefPtr<KeyboardInfo> mLatestKeyDownInfo;
|
||||
|
||||
// input-method-app needs to register a listener by
|
||||
// |nsIHardwareKeyHandler.registerListener| to receive
|
||||
// the hardware keyboard event, and |nsIHardwareKeyHandler.registerListener|
|
||||
// will set an nsIHardwareKeyEventListener to mHardwareKeyEventListener.
|
||||
// Then, mHardwareKeyEventListener is used to forward the event
|
||||
// to the input-method-app.
|
||||
nsWeakPtr mHardwareKeyEventListener;
|
||||
|
||||
// To keep tracking the input-method-app is active or disabled.
|
||||
bool mInputMethodAppConnected;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // #ifndef mozilla_HardwareKeyHandler_h_
|
@ -4,6 +4,29 @@
|
||||
# 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/.
|
||||
|
||||
if CONFIG['MOZ_B2G']:
|
||||
XPIDL_SOURCES += [
|
||||
'nsIHardwareKeyHandler.idl',
|
||||
]
|
||||
|
||||
XPIDL_MODULE = 'inputmethod'
|
||||
|
||||
EXPORTS.mozilla += [
|
||||
'HardwareKeyHandler.h',
|
||||
]
|
||||
|
||||
SOURCES += [
|
||||
'HardwareKeyHandler.cpp'
|
||||
]
|
||||
|
||||
include('/ipc/chromium/chromium-config.mozbuild')
|
||||
|
||||
FINAL_LIBRARY = 'xul'
|
||||
LOCAL_INCLUDES += [
|
||||
'/dom/base',
|
||||
'/layout/base',
|
||||
]
|
||||
|
||||
EXTRA_COMPONENTS += [
|
||||
'InputMethod.manifest',
|
||||
'MozKeyboard.js',
|
||||
|
142
dom/inputmethod/nsIHardwareKeyHandler.idl
Normal file
142
dom/inputmethod/nsIHardwareKeyHandler.idl
Normal file
@ -0,0 +1,142 @@
|
||||
/* -*- 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 "nsISupports.idl"
|
||||
|
||||
interface nsIDOMKeyEvent;
|
||||
|
||||
%{C++
|
||||
#define NS_HARDWARE_KEY_HANDLER_CID \
|
||||
{ 0xfb45921b, 0xe0a5, 0x45c6, \
|
||||
{ 0x90, 0xd0, 0xa6, 0x97, 0xa7, 0x72, 0xc4, 0x2a } }
|
||||
#define NS_HARDWARE_KEY_HANDLER_CONTRACTID \
|
||||
"@mozilla.org/HardwareKeyHandler;1"
|
||||
|
||||
#include "mozilla/EventForwards.h" /* For nsEventStatus */
|
||||
|
||||
namespace mozilla {
|
||||
class WidgetKeyboardEvent;
|
||||
}
|
||||
|
||||
using mozilla::WidgetKeyboardEvent;
|
||||
|
||||
class nsINode;
|
||||
%}
|
||||
|
||||
/**
|
||||
* This interface is used to be registered to the nsIHardwareKeyHandler through
|
||||
* |nsIHardwareKeyHandler.registerListener|.
|
||||
*/
|
||||
[scriptable, function, uuid(cd5aeee3-b4b9-459d-85e7-c0671c7a8a2e)]
|
||||
interface nsIHardwareKeyEventListener : nsISupports
|
||||
{
|
||||
/**
|
||||
* This method will be invoked by nsIHardwareKeyHandler to forward the native
|
||||
* keyboard event to the active input method
|
||||
*/
|
||||
bool onHardwareKey(in nsIDOMKeyEvent aEvent);
|
||||
};
|
||||
|
||||
/**
|
||||
* This interface has two main roles. One is to send a hardware keyboard event
|
||||
* to the active input method app and the other is to receive its reply result.
|
||||
* If a keyboard event is triggered from a hardware keyboard when an editor has
|
||||
* focus, the event target should be the editor. However, the text input
|
||||
* processor algorithm is implemented in an input method app and it should
|
||||
* handle the event earlier than the real event target to do the mapping such
|
||||
* as character conversion according to the language setting or the type of a
|
||||
* hardware keyboard.
|
||||
*/
|
||||
[scriptable, builtinclass, uuid(25b34270-caad-4d18-a910-860351690639)]
|
||||
interface nsIHardwareKeyHandler : nsISupports
|
||||
{
|
||||
/**
|
||||
* Flags used to set the defaultPrevented's result. The default result
|
||||
* from input-method-app should be set to NO_DEFAULT_PREVENTED.
|
||||
* (It means the forwarded event isn't consumed by input-method-app.)
|
||||
* If the input-method-app consumes the forwarded event,
|
||||
* then the result should be set by DEFAULT_PREVENTED* before reply.
|
||||
*/
|
||||
const unsigned short NO_DEFAULT_PREVENTED = 0x0000;
|
||||
const unsigned short DEFAULT_PREVENTED = 0x0001;
|
||||
const unsigned short DEFAULT_PREVENTED_BY_CHROME = 0x0002;
|
||||
const unsigned short DEFAULT_PREVENTED_BY_CONTENT = 0x0004;
|
||||
|
||||
/**
|
||||
* Registers a listener in input-method-app to receive
|
||||
* the forwarded hardware keyboard events
|
||||
*
|
||||
* @param aListener Listener object to be notified for receiving
|
||||
* the keyboard event fired from hardware
|
||||
* @note A listener object must implement
|
||||
* nsIHardwareKeyEventListener and
|
||||
* nsSupportsWeakReference
|
||||
* @see nsIHardwareKeyEventListener
|
||||
* @see nsSupportsWeakReference
|
||||
*/
|
||||
void registerListener(in nsIHardwareKeyEventListener aListener);
|
||||
|
||||
/**
|
||||
* Unregisters the current listener from input-method-app
|
||||
*/
|
||||
void unregisterListener();
|
||||
|
||||
/**
|
||||
* Notifies nsIHardwareKeyHandler that input-method-app is active.
|
||||
*/
|
||||
void onInputMethodAppConnected();
|
||||
|
||||
/**
|
||||
* Notifies nsIHardwareKeyHandler that input-method-app is disabled.
|
||||
*/
|
||||
void onInputMethodAppDisconnected();
|
||||
|
||||
/**
|
||||
* Input-method-app will pass the processing result that the forwarded
|
||||
* event is handled or not through this method, and the nsIHardwareKeyHandler
|
||||
* can use this to receive the reply of |forwardKeyToInputMethodApp|
|
||||
* from the active input method.
|
||||
*
|
||||
* The result should contain the original event type and the info whether
|
||||
* the default is prevented, also, it is prevented by chrome or content.
|
||||
*
|
||||
* @param aEventType The type of an original event.
|
||||
* @param aDefaultPrevented State that |evt.preventDefault|
|
||||
* is called by content, chrome or not.
|
||||
*/
|
||||
void onHandledByInputMethodApp(in DOMString aType,
|
||||
in unsigned short aDefaultPrevented);
|
||||
|
||||
/**
|
||||
* Sends the native keyboard events triggered from hardware to the
|
||||
* active input method before dispatching to its event target.
|
||||
* This method only forwards keydown and keyup events.
|
||||
* If the event isn't allowed to be forwarded, we should continue the
|
||||
* normal event processing. For those forwarded keydown and keyup events
|
||||
* We will pause the further event processing to wait for the completion
|
||||
* of the event handling in the active input method app.
|
||||
* Once |onHandledByInputMethodApp| is called by the input method app,
|
||||
* the pending event processing can be resumed according to its reply.
|
||||
* On the other hand, the keypress will never be sent to the input-method-app.
|
||||
* Depending on whether the keydown's reply arrives before the keypress event
|
||||
* comes, the keypress event will be handled directly or pushed into
|
||||
* the event queue to wait for its heading keydown's reply.
|
||||
*
|
||||
* This implementation will call |nsIHardwareKeyEventListener.onHardwareKey|,
|
||||
* which is registered through |nsIHardwareKeyEventListener.registerListener|,
|
||||
* to forward the events.
|
||||
*
|
||||
* Returns true, if the event is handled in this module.
|
||||
* Returns false, otherwise.
|
||||
*
|
||||
* If it returns false, we should continue the normal event processing.
|
||||
*/
|
||||
%{C++
|
||||
virtual bool ForwardKeyToInputMethodApp(nsINode* aTarget,
|
||||
WidgetKeyboardEvent* aEvent,
|
||||
nsEventStatus* aEventStatus) = 0;
|
||||
%}
|
||||
};
|
@ -267,6 +267,11 @@ static void Shutdown();
|
||||
|
||||
#include "mozilla/TextInputProcessor.h"
|
||||
|
||||
#ifdef MOZ_B2G
|
||||
#include "nsIHardwareKeyHandler.h"
|
||||
#include "mozilla/HardwareKeyHandler.h"
|
||||
#endif
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::dom;
|
||||
using mozilla::dom::alarm::AlarmHalService;
|
||||
@ -666,6 +671,11 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(UDPSocketChild)
|
||||
|
||||
NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(GeckoMediaPluginService, GeckoMediaPluginService::GetGeckoMediaPluginService)
|
||||
|
||||
#ifdef MOZ_B2G
|
||||
NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsIHardwareKeyHandler,
|
||||
HardwareKeyHandler::GetInstance)
|
||||
#endif
|
||||
|
||||
#ifdef ACCESSIBILITY
|
||||
#include "nsAccessibilityService.h"
|
||||
|
||||
@ -866,6 +876,10 @@ NS_DEFINE_NAMED_CID(PRESENTATION_SESSION_TRANSPORT_CID);
|
||||
|
||||
NS_DEFINE_NAMED_CID(TEXT_INPUT_PROCESSOR_CID);
|
||||
|
||||
#ifdef MOZ_B2G
|
||||
NS_DEFINE_NAMED_CID(NS_HARDWARE_KEY_HANDLER_CID);
|
||||
#endif
|
||||
|
||||
static nsresult
|
||||
CreateWindowCommandTableConstructor(nsISupports *aOuter,
|
||||
REFNSIID aIID, void **aResult)
|
||||
@ -1161,6 +1175,9 @@ static const mozilla::Module::CIDEntry kLayoutCIDs[] = {
|
||||
{ &kTEXT_INPUT_PROCESSOR_CID, false, nullptr, TextInputProcessorConstructor },
|
||||
{ &kFAKE_INPUTPORT_SERVICE_CID, false, nullptr, FakeInputPortServiceConstructor },
|
||||
{ &kINPUTPORT_DATA_CID, false, nullptr, InputPortDataConstructor },
|
||||
#ifdef MOZ_B2G
|
||||
{ &kNS_HARDWARE_KEY_HANDLER_CID, false, nullptr, nsIHardwareKeyHandlerConstructor },
|
||||
#endif
|
||||
{ nullptr }
|
||||
};
|
||||
|
||||
@ -1327,6 +1344,9 @@ static const mozilla::Module::ContractIDEntry kLayoutContracts[] = {
|
||||
{ "@mozilla.org/text-input-processor;1", &kTEXT_INPUT_PROCESSOR_CID },
|
||||
{ FAKE_INPUTPORT_SERVICE_CONTRACTID, &kFAKE_INPUTPORT_SERVICE_CID },
|
||||
{ INPUTPORT_DATA_CONTRACTID, &kINPUTPORT_DATA_CID },
|
||||
#ifdef MOZ_B2G
|
||||
{ NS_HARDWARE_KEY_HANDLER_CONTRACTID, &kNS_HARDWARE_KEY_HANDLER_CID },
|
||||
#endif
|
||||
{ nullptr }
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user