michael@0: /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- michael@0: * michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "nsPrintPreviewListener.h" michael@0: michael@0: #include "mozilla/TextEvents.h" michael@0: #include "mozilla/dom/Element.h" michael@0: #include "mozilla/dom/Event.h" // for nsIDOMEvent::InternalDOMEvent() michael@0: #include "nsIDOMWindow.h" michael@0: #include "nsPIDOMWindow.h" michael@0: #include "nsIDOMElement.h" michael@0: #include "nsIDOMKeyEvent.h" michael@0: #include "nsIDOMEvent.h" michael@0: #include "nsIDocument.h" michael@0: #include "nsIDocShell.h" michael@0: #include "nsPresContext.h" michael@0: #include "nsFocusManager.h" michael@0: #include "nsLiteralString.h" michael@0: michael@0: using namespace mozilla; michael@0: using namespace mozilla::dom; michael@0: michael@0: NS_IMPL_ISUPPORTS(nsPrintPreviewListener, nsIDOMEventListener) michael@0: michael@0: michael@0: // michael@0: // nsPrintPreviewListener ctor michael@0: // michael@0: nsPrintPreviewListener::nsPrintPreviewListener(EventTarget* aTarget) michael@0: : mEventTarget(aTarget) michael@0: { michael@0: NS_ADDREF_THIS(); michael@0: } // ctor michael@0: michael@0: nsPrintPreviewListener::~nsPrintPreviewListener() michael@0: { michael@0: } michael@0: michael@0: //------------------------------------------------------- michael@0: // michael@0: // AddListeners michael@0: // michael@0: // Subscribe to the events that will allow us to track various events. michael@0: // michael@0: nsresult michael@0: nsPrintPreviewListener::AddListeners() michael@0: { michael@0: if (mEventTarget) { michael@0: mEventTarget->AddEventListener(NS_LITERAL_STRING("click"), this, true); michael@0: mEventTarget->AddEventListener(NS_LITERAL_STRING("contextmenu"), this, true); michael@0: mEventTarget->AddEventListener(NS_LITERAL_STRING("keydown"), this, true); michael@0: mEventTarget->AddEventListener(NS_LITERAL_STRING("keypress"), this, true); michael@0: mEventTarget->AddEventListener(NS_LITERAL_STRING("keyup"), this, true); michael@0: mEventTarget->AddEventListener(NS_LITERAL_STRING("mousedown"), this, true); michael@0: mEventTarget->AddEventListener(NS_LITERAL_STRING("mousemove"), this, true); michael@0: mEventTarget->AddEventListener(NS_LITERAL_STRING("mouseout"), this, true); michael@0: mEventTarget->AddEventListener(NS_LITERAL_STRING("mouseover"), this, true); michael@0: mEventTarget->AddEventListener(NS_LITERAL_STRING("mouseup"), this, true); michael@0: michael@0: mEventTarget->AddSystemEventListener(NS_LITERAL_STRING("keydown"), michael@0: this, true); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: //------------------------------------------------------- michael@0: // michael@0: // RemoveListeners michael@0: // michael@0: // Unsubscribe from all the various events that we were listening to. michael@0: // michael@0: nsresult michael@0: nsPrintPreviewListener::RemoveListeners() michael@0: { michael@0: if (mEventTarget) { michael@0: mEventTarget->RemoveEventListener(NS_LITERAL_STRING("click"), this, true); michael@0: mEventTarget->RemoveEventListener(NS_LITERAL_STRING("contextmenu"), this, true); michael@0: mEventTarget->RemoveEventListener(NS_LITERAL_STRING("keydown"), this, true); michael@0: mEventTarget->RemoveEventListener(NS_LITERAL_STRING("keypress"), this, true); michael@0: mEventTarget->RemoveEventListener(NS_LITERAL_STRING("keyup"), this, true); michael@0: mEventTarget->RemoveEventListener(NS_LITERAL_STRING("mousedown"), this, true); michael@0: mEventTarget->RemoveEventListener(NS_LITERAL_STRING("mousemove"), this, true); michael@0: mEventTarget->RemoveEventListener(NS_LITERAL_STRING("mouseout"), this, true); michael@0: mEventTarget->RemoveEventListener(NS_LITERAL_STRING("mouseover"), this, true); michael@0: mEventTarget->RemoveEventListener(NS_LITERAL_STRING("mouseup"), this, true); michael@0: michael@0: mEventTarget->RemoveSystemEventListener(NS_LITERAL_STRING("keydown"), michael@0: this, true); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: //------------------------------------------------------- michael@0: // michael@0: // GetActionForEvent michael@0: // michael@0: // Helper function to let certain key events through michael@0: // michael@0: enum eEventAction { michael@0: eEventAction_Tab, eEventAction_ShiftTab, michael@0: eEventAction_Propagate, eEventAction_Suppress, michael@0: eEventAction_StopPropagation michael@0: }; michael@0: michael@0: static eEventAction michael@0: GetActionForEvent(nsIDOMEvent* aEvent) michael@0: { michael@0: WidgetKeyboardEvent* keyEvent = michael@0: aEvent->GetInternalNSEvent()->AsKeyboardEvent(); michael@0: if (!keyEvent) { michael@0: return eEventAction_Suppress; michael@0: } michael@0: michael@0: if (keyEvent->mFlags.mInSystemGroup) { michael@0: NS_ASSERTION(keyEvent->message == NS_KEY_DOWN, michael@0: "Assuming we're listening only keydown event in system group"); michael@0: return eEventAction_StopPropagation; michael@0: } michael@0: michael@0: if (keyEvent->IsAlt() || keyEvent->IsControl() || keyEvent->IsMeta()) { michael@0: // Don't consume keydown event because following keypress event may be michael@0: // handled as access key or shortcut key. michael@0: return (keyEvent->message == NS_KEY_DOWN) ? eEventAction_StopPropagation : michael@0: eEventAction_Suppress; michael@0: } michael@0: michael@0: static const uint32_t kOKKeyCodes[] = { michael@0: nsIDOMKeyEvent::DOM_VK_PAGE_UP, nsIDOMKeyEvent::DOM_VK_PAGE_DOWN, michael@0: nsIDOMKeyEvent::DOM_VK_UP, nsIDOMKeyEvent::DOM_VK_DOWN, michael@0: nsIDOMKeyEvent::DOM_VK_HOME, nsIDOMKeyEvent::DOM_VK_END michael@0: }; michael@0: michael@0: if (keyEvent->keyCode == nsIDOMKeyEvent::DOM_VK_TAB) { michael@0: return keyEvent->IsShift() ? eEventAction_ShiftTab : eEventAction_Tab; michael@0: } michael@0: michael@0: if (keyEvent->charCode == ' ' || keyEvent->keyCode == NS_VK_SPACE) { michael@0: return eEventAction_Propagate; michael@0: } michael@0: michael@0: if (keyEvent->IsShift()) { michael@0: return eEventAction_Suppress; michael@0: } michael@0: michael@0: for (uint32_t i = 0; i < ArrayLength(kOKKeyCodes); ++i) { michael@0: if (keyEvent->keyCode == kOKKeyCodes[i]) { michael@0: return eEventAction_Propagate; michael@0: } michael@0: } michael@0: michael@0: return eEventAction_Suppress; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsPrintPreviewListener::HandleEvent(nsIDOMEvent* aEvent) michael@0: { michael@0: nsCOMPtr content = do_QueryInterface( michael@0: aEvent ? aEvent->InternalDOMEvent()->GetOriginalTarget() : nullptr); michael@0: if (content && !content->IsXUL()) { michael@0: eEventAction action = ::GetActionForEvent(aEvent); michael@0: switch (action) { michael@0: case eEventAction_Tab: michael@0: case eEventAction_ShiftTab: michael@0: { michael@0: nsAutoString eventString; michael@0: aEvent->GetType(eventString); michael@0: if (eventString == NS_LITERAL_STRING("keydown")) { michael@0: // Handle tabbing explicitly here since we don't want focus ending up michael@0: // inside the content document, bug 244128. michael@0: nsIDocument* doc = content->GetCurrentDoc(); michael@0: NS_ASSERTION(doc, "no document"); michael@0: michael@0: nsIDocument* parentDoc = doc->GetParentDocument(); michael@0: NS_ASSERTION(parentDoc, "no parent document"); michael@0: michael@0: nsCOMPtr win = do_QueryInterface(parentDoc->GetWindow()); michael@0: michael@0: nsIFocusManager* fm = nsFocusManager::GetFocusManager(); michael@0: if (fm && win) { michael@0: dom::Element* fromElement = parentDoc->FindContentForSubDocument(doc); michael@0: nsCOMPtr from = do_QueryInterface(fromElement); michael@0: michael@0: bool forward = (action == eEventAction_Tab); michael@0: nsCOMPtr result; michael@0: fm->MoveFocus(win, from, michael@0: forward ? nsIFocusManager::MOVEFOCUS_FORWARD : michael@0: nsIFocusManager::MOVEFOCUS_BACKWARD, michael@0: nsIFocusManager::FLAG_BYKEY, getter_AddRefs(result)); michael@0: } michael@0: } michael@0: } michael@0: // fall-through michael@0: case eEventAction_Suppress: michael@0: aEvent->StopPropagation(); michael@0: aEvent->PreventDefault(); michael@0: break; michael@0: case eEventAction_StopPropagation: michael@0: aEvent->StopPropagation(); michael@0: break; michael@0: case eEventAction_Propagate: michael@0: // intentionally empty michael@0: break; michael@0: } michael@0: } michael@0: return NS_OK; michael@0: }