Bug 862519 - [e10s] Better handling of key commands also handled by content (r=smaug)

This commit is contained in:
Bill McCloskey 2014-03-18 08:16:47 -07:00
parent 14278fe362
commit e6388b7f5b
9 changed files with 158 additions and 39 deletions

View File

@ -1186,13 +1186,6 @@ var gBrowserInit = {
WindowsPrefSync.init();
}
if (gMultiProcessBrowser) {
// Bug 862519 - Backspace doesn't work in electrolysis builds.
// We bypass the problem by disabling the backspace-to-go-back command.
document.getElementById("cmd_handleBackspace").setAttribute("disabled", true);
document.getElementById("key_delete").setAttribute("disabled", true);
}
SessionStore.promiseInitialized.then(() => {
// Bail out if the window has been closed in the meantime.
if (window.closed) {

View File

@ -334,6 +334,8 @@ parent:
__delete__();
ReplyKeyEvent(WidgetKeyboardEvent event);
child:
/**
* Notify the remote browser that it has been Show()n on this

View File

@ -1932,6 +1932,10 @@ TabChild::RecvRealKeyEvent(const WidgetKeyboardEvent& event)
mIgnoreKeyPressEvent = status == nsEventStatus_eConsumeNoDefault;
}
if (localEvent.mFlags.mWantReplyFromContentProcess) {
SendReplyKeyEvent(localEvent);
}
return true;
}

View File

@ -1192,6 +1192,28 @@ TabParent::GetChildProcessOffset()
pt, targetFrame->PresContext()->AppUnitsPerDevPixel()));
}
bool
TabParent::RecvReplyKeyEvent(const WidgetKeyboardEvent& event)
{
NS_ENSURE_TRUE(mFrameElement, true);
WidgetKeyboardEvent localEvent(event);
// Set mNoCrossProcessBoundaryForwarding to avoid this event from
// being infinitely redispatched and forwarded to the child again.
localEvent.mFlags.mNoCrossProcessBoundaryForwarding = true;
// Here we convert the WidgetEvent that we received to an nsIDOMEvent
// to be able to dispatch it to the <browser> element as the target element.
nsIDocument* doc = mFrameElement->OwnerDoc();
nsIPresShell* presShell = doc->GetShell();
NS_ENSURE_TRUE(presShell, true);
nsPresContext* presContext = presShell->GetPresContext();
NS_ENSURE_TRUE(presContext, true);
EventDispatcher::Dispatch(mFrameElement, presContext, &localEvent);
return true;
}
/**
* Try to answer query event using cached text.
*

View File

@ -114,6 +114,7 @@ public:
virtual bool RecvMoveFocus(const bool& aForward) MOZ_OVERRIDE;
virtual bool RecvEvent(const RemoteDOMEvent& aEvent) MOZ_OVERRIDE;
virtual bool RecvReplyKeyEvent(const WidgetKeyboardEvent& event);
virtual bool RecvPRenderFrameConstructor(PRenderFrameParent* actor) MOZ_OVERRIDE;
virtual bool RecvInitRenderFrame(PRenderFrameParent* aFrame,
ScrollingBehavior* scrolling,

View File

@ -585,6 +585,15 @@ nsXBLService::AttachGlobalKeyHandler(EventTarget* aTarget)
manager->AddEventListenerByType(handler, NS_LITERAL_STRING("keypress"),
TrustedEventsAtSystemGroupBubble());
// The capturing listener is only used for XUL keysets to properly handle
// shortcut keys in a multi-process environment.
manager->AddEventListenerByType(handler, NS_LITERAL_STRING("keydown"),
TrustedEventsAtSystemGroupCapture());
manager->AddEventListenerByType(handler, NS_LITERAL_STRING("keyup"),
TrustedEventsAtSystemGroupCapture());
manager->AddEventListenerByType(handler, NS_LITERAL_STRING("keypress"),
TrustedEventsAtSystemGroupCapture());
if (contentNode)
return contentNode->SetProperty(nsGkAtoms::listener,
handler.forget().take(),
@ -630,6 +639,13 @@ nsXBLService::DetachGlobalKeyHandler(EventTarget* aTarget)
manager->RemoveEventListenerByType(handler, NS_LITERAL_STRING("keypress"),
TrustedEventsAtSystemGroupBubble());
manager->RemoveEventListenerByType(handler, NS_LITERAL_STRING("keydown"),
TrustedEventsAtSystemGroupCapture());
manager->RemoveEventListenerByType(handler, NS_LITERAL_STRING("keyup"),
TrustedEventsAtSystemGroupCapture());
manager->RemoveEventListenerByType(handler, NS_LITERAL_STRING("keypress"),
TrustedEventsAtSystemGroupCapture());
contentNode->DeleteProperty(nsGkAtoms::listener);
return NS_OK;

View File

@ -26,6 +26,7 @@
#include "mozilla/Preferences.h"
#include "mozilla/TextEvents.h"
#include "mozilla/dom/Element.h"
#include "mozilla/dom/Event.h"
#include "nsEventStateManager.h"
#include "nsIEditor.h"
#include "nsIHTMLEditor.h"
@ -276,24 +277,23 @@ nsXBLWindowKeyHandler::WalkHandlers(nsIDOMKeyEvent* aKeyEvent, nsIAtom* aEventTy
nsresult rv = EnsureHandlers();
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<Element> el = GetElement();
bool isDisabled;
nsCOMPtr<Element> el = GetElement(&isDisabled);
if (!el) {
if (mUserHandler) {
WalkHandlersInternal(aKeyEvent, aEventType, mUserHandler);
WalkHandlersInternal(aKeyEvent, aEventType, mUserHandler, true);
aKeyEvent->GetDefaultPrevented(&prevent);
if (prevent)
return NS_OK; // Handled by the user bindings. Our work here is done.
}
}
nsCOMPtr<nsIContent> content = do_QueryInterface(el);
// skip keysets that are disabled
if (content && content->AttrValueIs(kNameSpaceID_None, nsGkAtoms::disabled,
nsGkAtoms::_true, eCaseMatters)) {
if (isDisabled) {
return NS_OK;
}
WalkHandlersInternal(aKeyEvent, aEventType, mHandler);
WalkHandlersInternal(aKeyEvent, aEventType, mHandler, true);
return NS_OK;
}
@ -304,20 +304,50 @@ nsXBLWindowKeyHandler::HandleEvent(nsIDOMEvent* aEvent)
nsCOMPtr<nsIDOMKeyEvent> keyEvent(do_QueryInterface(aEvent));
NS_ENSURE_TRUE(keyEvent, NS_ERROR_INVALID_ARG);
uint16_t eventPhase;
aEvent->GetEventPhase(&eventPhase);
if (eventPhase == nsIDOMEvent::CAPTURING_PHASE) {
HandleEventOnCapture(keyEvent);
return NS_OK;
}
nsAutoString eventType;
aEvent->GetType(eventType);
nsCOMPtr<nsIAtom> eventTypeAtom = do_GetAtom(eventType);
NS_ENSURE_TRUE(eventTypeAtom, NS_ERROR_OUT_OF_MEMORY);
if (!mWeakPtrForElement) {
nsCOMPtr<mozilla::dom::Element> originalTarget =
do_QueryInterface(aEvent->GetInternalNSEvent()->originalTarget);
if (nsEventStateManager::IsRemoteTarget(originalTarget)) {
return NS_OK;
}
return WalkHandlers(keyEvent, eventTypeAtom);
}
void
nsXBLWindowKeyHandler::HandleEventOnCapture(nsIDOMKeyEvent* aEvent)
{
WidgetKeyboardEvent* widgetEvent =
aEvent->GetInternalNSEvent()->AsKeyboardEvent();
if (widgetEvent->mFlags.mNoCrossProcessBoundaryForwarding) {
return;
}
return WalkHandlers(keyEvent, eventTypeAtom);
nsCOMPtr<mozilla::dom::Element> originalTarget =
do_QueryInterface(aEvent->GetInternalNSEvent()->originalTarget);
if (!nsEventStateManager::IsRemoteTarget(originalTarget)) {
return;
}
if (!HasHandlerForEvent(aEvent)) {
return;
}
// If this event hasn't been marked as mNoCrossProcessBoundaryForwarding
// yet, it means it wasn't processed by content. We'll not call any
// of the handlers at this moment, and will wait for the event to be
// redispatched with mNoCrossProcessBoundaryForwarding = 1 to process it.
// Inform the child process that this is a event that we want a reply
// from.
widgetEvent->mFlags.mWantReplyFromContentProcess = 1;
aEvent->StopPropagation();
}
//
@ -391,29 +421,31 @@ nsXBLWindowKeyHandler::IsHTMLEditableFieldFocused()
// WalkHandlersInternal and WalkHandlersAndExecute
//
// Given a particular DOM event and a pointer to the first handler in the list,
// scan through the list to find something to handle the event and then make it
// so.
// scan through the list to find something to handle the event. If aExecute = true,
// the handler will be executed; otherwise just return an answer telling if a handler
// for that event was found.
//
nsresult
bool
nsXBLWindowKeyHandler::WalkHandlersInternal(nsIDOMKeyEvent* aKeyEvent,
nsIAtom* aEventType,
nsXBLPrototypeHandler* aHandler)
nsXBLPrototypeHandler* aHandler,
bool aExecute)
{
nsAutoTArray<nsShortcutCandidate, 10> accessKeys;
nsContentUtils::GetAccelKeyCandidates(aKeyEvent, accessKeys);
if (accessKeys.IsEmpty()) {
WalkHandlersAndExecute(aKeyEvent, aEventType, aHandler, 0, false);
return NS_OK;
return WalkHandlersAndExecute(aKeyEvent, aEventType, aHandler,
0, false, aExecute);
}
for (uint32_t i = 0; i < accessKeys.Length(); ++i) {
nsShortcutCandidate &key = accessKeys[i];
if (WalkHandlersAndExecute(aKeyEvent, aEventType, aHandler,
key.mCharCode, key.mIgnoreShift))
return NS_OK;
key.mCharCode, key.mIgnoreShift, aExecute))
return true;
}
return NS_OK;
return false;
}
bool
@ -421,7 +453,8 @@ nsXBLWindowKeyHandler::WalkHandlersAndExecute(nsIDOMKeyEvent* aKeyEvent,
nsIAtom* aEventType,
nsXBLPrototypeHandler* aHandler,
uint32_t aCharCode,
bool aIgnoreShiftKey)
bool aIgnoreShiftKey,
bool aExecute)
{
nsresult rv;
@ -491,6 +524,10 @@ nsXBLWindowKeyHandler::WalkHandlersAndExecute(nsIDOMKeyEvent* aKeyEvent,
piTarget = mTarget;
}
if (!aExecute) {
return true;
}
rv = currHandler->ExecuteHandler(piTarget, aKeyEvent);
if (NS_SUCCEEDED(rv)) {
return true;
@ -500,10 +537,38 @@ nsXBLWindowKeyHandler::WalkHandlersAndExecute(nsIDOMKeyEvent* aKeyEvent,
return false;
}
bool
nsXBLWindowKeyHandler::HasHandlerForEvent(nsIDOMKeyEvent* aEvent)
{
if (!aEvent->InternalDOMEvent()->IsTrusted()) {
return false;
}
nsresult rv = EnsureHandlers();
NS_ENSURE_SUCCESS(rv, false);
bool isDisabled;
nsCOMPtr<Element> el = GetElement(&isDisabled);
if (el && isDisabled) {
return false;
}
nsAutoString eventType;
aEvent->GetType(eventType);
nsCOMPtr<nsIAtom> eventTypeAtom = do_GetAtom(eventType);
NS_ENSURE_TRUE(eventTypeAtom, false);
return WalkHandlersInternal(aEvent, eventTypeAtom, mHandler, false);
}
already_AddRefed<Element>
nsXBLWindowKeyHandler::GetElement()
nsXBLWindowKeyHandler::GetElement(bool* aIsDisabled)
{
nsCOMPtr<Element> element = do_QueryReferent(mWeakPtrForElement);
if (element && aIsDisabled) {
*aIsDisabled = element->AttrValueIs(kNameSpaceID_None, nsGkAtoms::disabled,
nsGkAtoms::_true, eCaseMatters);
}
return element.forget();
}

View File

@ -35,14 +35,23 @@ protected:
nsresult WalkHandlers(nsIDOMKeyEvent* aKeyEvent, nsIAtom* aEventType);
// walk the handlers, looking for one to handle the event
nsresult WalkHandlersInternal(nsIDOMKeyEvent* aKeyEvent,
nsIAtom* aEventType,
nsXBLPrototypeHandler* aHandler);
bool WalkHandlersInternal(nsIDOMKeyEvent* aKeyEvent,
nsIAtom* aEventType,
nsXBLPrototypeHandler* aHandler,
bool aExecute);
// walk the handlers for aEvent, aCharCode and aIgnoreShiftKey
// walk the handlers for aEvent, aCharCode and aIgnoreShiftKey. Execute it
// if aExecute = true.
bool WalkHandlersAndExecute(nsIDOMKeyEvent* aKeyEvent, nsIAtom* aEventType,
nsXBLPrototypeHandler* aHandler,
uint32_t aCharCode, bool aIgnoreShiftKey);
nsXBLPrototypeHandler* aHandler,
uint32_t aCharCode, bool aIgnoreShiftKey,
bool aExecute);
// HandleEvent function for the capturing phase.
void HandleEventOnCapture(nsIDOMKeyEvent* aEvent);
// Check if any handler would handle the given event.
bool HasHandlerForEvent(nsIDOMKeyEvent* aEvent);
// lazily load the handlers. Overridden to handle being attached
// to a particular element rather than the document
@ -57,8 +66,10 @@ protected:
bool IsHTMLEditableFieldFocused();
// Returns the element which was passed as a parameter to the constructor,
// unless the element has been removed from the document.
already_AddRefed<mozilla::dom::Element> GetElement();
// unless the element has been removed from the document. Optionally returns
// whether the disabled attribute is set on the element (assuming the element
// is non-null).
already_AddRefed<mozilla::dom::Element> GetElement(bool* aIsDisabled = nullptr);
// Using weak pointer to the DOM Element.
nsWeakPtr mWeakPtrForElement;
mozilla::dom::EventTarget* mTarget; // weak ref

View File

@ -553,6 +553,11 @@ public:
bool mNoContentDispatch : 1;
// If mOnlyChromeDispatch is true, the event is dispatched to only chrome.
bool mOnlyChromeDispatch : 1;
// If mWantReplyFromContentProcess is true, the event will be redispatched
// in the parent process after the content process has handled it. Useful
// for when the parent process need the know first how the event was used
// by content before handling it itself.
bool mWantReplyFromContentProcess : 1;
// If the event is being handled in target phase, returns true.
inline bool InTargetPhase() const