diff options
Diffstat (limited to 'dom/inputmethod/HardwareKeyHandler.cpp')
-rw-r--r-- | dom/inputmethod/HardwareKeyHandler.cpp | 562 |
1 files changed, 0 insertions, 562 deletions
diff --git a/dom/inputmethod/HardwareKeyHandler.cpp b/dom/inputmethod/HardwareKeyHandler.cpp deleted file mode 100644 index 737c30e5b..000000000 --- a/dom/inputmethod/HardwareKeyHandler.cpp +++ /dev/null @@ -1,562 +0,0 @@ -/* -*- 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 |