Bug 989198, Patch 3: Dispatch BeforeAfterKeyboardEvent on b2g, r=smaug

---
 b2g/app/b2g.js                   |    3 +
 content/base/src/nsGkAtomList.h  |    4 +
 dom/events/EventNameList.h       |   16 +++
 dom/events/EventStateManager.cpp |    6 +
 dom/ipc/PBrowser.ipdl            |    2 +
 dom/ipc/TabChild.cpp             |    6 +-
 dom/ipc/TabParent.cpp            |   24 ++++
 dom/ipc/TabParent.h              |    1 +
 layout/base/nsIPresShell.h       |   12 +-
 layout/base/nsPresShell.cpp      |  250 ++++++++++++++++++++++++++++++++++++++
 layout/base/nsPresShell.h        |   26 ++++
 modules/libpref/init/all.js      |    3 +
 12 files changed, 350 insertions(+), 3 deletions(-)
This commit is contained in:
Gina Yeh 2014-10-14 15:09:24 +08:00
parent abdd389011
commit 485409e98d
12 changed files with 350 additions and 3 deletions

View File

@ -788,6 +788,9 @@ pref("dom.ipc.systemMessageCPULockTimeoutSec", 30);
// Ignore the "dialog=1" feature in window.open.
pref("dom.disable_window_open_dialog_feature", true);
// Enable before keyboard events and after keyboard events.
pref("dom.beforeAfterKeyboardEvent.enabled", true);
// Screen reader support
pref("accessibility.accessfu.activate", 2);
pref("accessibility.accessfu.quicknav_modes", "Link,Heading,FormElement,Landmark,ListItem");

View File

@ -793,6 +793,10 @@ GK_ATOM(onmouseover, "onmouseover")
GK_ATOM(onMozMouseHittest, "onMozMouseHittest")
GK_ATOM(onmouseup, "onmouseup")
GK_ATOM(onMozAfterPaint, "onMozAfterPaint")
GK_ATOM(onmozbrowserafterkeydown, "onmozbrowserafterkeydown")
GK_ATOM(onmozbrowserafterkeyup, "onmozbrowserafterkeyup")
GK_ATOM(onmozbrowserbeforekeydown, "onmozbrowserbeforekeydown")
GK_ATOM(onmozbrowserbeforekeyup, "onmozbrowserbeforekeyup")
GK_ATOM(onmozfullscreenchange, "onmozfullscreenchange")
GK_ATOM(onmozfullscreenerror, "onmozfullscreenerror")
GK_ATOM(onmozpointerlockchange, "onmozpointerlockchange")

View File

@ -237,6 +237,22 @@ EVENT(keyup,
NS_KEY_UP,
EventNameType_HTMLXUL,
eKeyboardEventClass)
NON_IDL_EVENT(mozbrowserbeforekeydown,
NS_KEY_BEFORE_DOWN,
EventNameType_None,
eBeforeAfterKeyboardEventClass)
NON_IDL_EVENT(mozbrowserafterkeydown,
NS_KEY_AFTER_DOWN,
EventNameType_None,
eBeforeAfterKeyboardEventClass)
NON_IDL_EVENT(mozbrowserbeforekeyup,
NS_KEY_BEFORE_UP,
EventNameType_None,
eBeforeAfterKeyboardEventClass)
NON_IDL_EVENT(mozbrowserafterkeyup,
NS_KEY_AFTER_UP,
EventNameType_None,
eBeforeAfterKeyboardEventClass)
EVENT(loadeddata,
NS_LOADEDDATA,
EventNameType_HTML,

View File

@ -651,8 +651,12 @@ EventStateManager::PreHandleEvent(nsPresContext* aPresContext,
}
}
// then fall through...
case NS_KEY_BEFORE_DOWN:
case NS_KEY_DOWN:
case NS_KEY_AFTER_DOWN:
case NS_KEY_BEFORE_UP:
case NS_KEY_UP:
case NS_KEY_AFTER_UP:
{
nsIContent* content = GetFocusedContent();
if (content)
@ -3159,7 +3163,9 @@ EventStateManager::PostHandleEvent(nsPresContext* aPresContext,
GenerateDragDropEnterExit(presContext, aEvent->AsDragEvent());
break;
case NS_KEY_BEFORE_UP:
case NS_KEY_UP:
case NS_KEY_AFTER_UP:
break;
case NS_KEY_PRESS:

View File

@ -398,6 +398,8 @@ parent:
ReplyKeyEvent(WidgetKeyboardEvent event);
DispatchAfterKeyboardEvent(WidgetKeyboardEvent event);
sync RequestNativeKeyBindings(WidgetKeyboardEvent event)
returns (MaybeNativeKeyBinding bindings);

View File

@ -81,8 +81,8 @@
#include "ClientLayerManager.h"
#include "LayersLogging.h"
#include "nsIOService.h"
#include "nsColorPickerProxy.h"
#include "nsPresShell.h"
#define BROWSER_ELEMENT_CHILD_SCRIPT \
NS_LITERAL_STRING("chrome://global/content/BrowserElementChild.js")
@ -2362,6 +2362,10 @@ TabChild::RecvRealKeyEvent(const WidgetKeyboardEvent& event,
SendReplyKeyEvent(localEvent);
}
if (PresShell::BeforeAfterKeyboardEventEnabled()) {
SendDispatchAfterKeyboardEvent(localEvent);
}
return true;
}

View File

@ -52,6 +52,7 @@
#include "nsIWindowWatcher.h"
#include "nsPIDOMWindow.h"
#include "nsPIWindowWatcher.h"
#include "nsPresShell.h"
#include "nsPrintfCString.h"
#include "nsServiceManagerUtils.h"
#include "nsThreadUtils.h"
@ -1463,6 +1464,29 @@ TabParent::RecvReplyKeyEvent(const WidgetKeyboardEvent& event)
return true;
}
bool
TabParent::RecvDispatchAfterKeyboardEvent(const WidgetKeyboardEvent& aEvent)
{
NS_ENSURE_TRUE(mFrameElement, true);
WidgetKeyboardEvent localEvent(aEvent);
localEvent.widget = GetWidget();
nsIDocument* doc = mFrameElement->OwnerDoc();
nsCOMPtr<nsIPresShell> presShell = doc->GetShell();
NS_ENSURE_TRUE(presShell, true);
if (mFrameElement &&
PresShell::BeforeAfterKeyboardEventEnabled() &&
localEvent.message != NS_KEY_PRESS) {
nsCOMPtr<nsINode> node(do_QueryInterface(mFrameElement));
presShell->DispatchAfterKeyboardEvent(mFrameElement, localEvent,
aEvent.mFlags.mDefaultPrevented);
}
return true;
}
/**
* Try to answer query event using cached text.
*

View File

@ -122,6 +122,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 RecvDispatchAfterKeyboardEvent(const WidgetKeyboardEvent& event);
virtual bool RecvPRenderFrameConstructor(PRenderFrameParent* aActor,
ScrollingBehavior* aScrolling,
TextureFactoryIdentifier* aFactoryIdentifier,

View File

@ -78,6 +78,7 @@ class nsDisplayList;
class nsDisplayListBuilder;
class nsPIDOMWindow;
struct nsPoint;
class nsINode;
struct nsIntPoint;
struct nsIntRect;
struct nsRect;
@ -139,8 +140,8 @@ typedef struct CapturingContentInfo {
} CapturingContentInfo;
#define NS_IPRESSHELL_IID \
{ 0x42e9a352, 0x76f3, 0x4ba3, \
{ 0x94, 0x0b, 0x78, 0x9e, 0x58, 0x38, 0x73, 0x4f } }
{ 0xa0a4b515, 0x0b91, 0x4f13, \
{ 0xa0, 0x60, 0x4b, 0xfb, 0x35, 0x00, 0xdc, 0x00 } }
// debug VerifyReflow flags
#define VERIFY_REFLOW_ON 0x01
@ -855,6 +856,13 @@ public:
nsIDOMEvent* aEvent,
nsEventStatus* aStatus) = 0;
/**
* Dispatch AfterKeyboardEvent with specific target.
*/
virtual void DispatchAfterKeyboardEvent(nsINode* aTarget,
const mozilla::WidgetKeyboardEvent& aEvent,
bool aEmbeddedCancelled) = 0;
/**
* Gets the current target event frame from the PresShell
*/

View File

@ -42,6 +42,7 @@
#include "nsPresShell.h"
#include "nsPresContext.h"
#include "nsIContent.h"
#include "mozilla/dom/BeforeAfterKeyboardEvent.h"
#include "mozilla/dom/Element.h"
#include "mozilla/dom/Event.h" // for Event::GetEventPopupControlState()
#include "mozilla/dom/ShadowRoot.h"
@ -72,6 +73,8 @@
#include "nsAutoPtr.h"
#include "nsReadableUtils.h"
#include "nsIPageSequenceFrame.h"
#include "nsIPermissionManager.h"
#include "nsIMozBrowserFrame.h"
#include "nsCaret.h"
#include "TouchCaret.h"
#include "SelectionCarets.h"
@ -81,6 +84,7 @@
#include "nsILayoutHistoryState.h"
#include "nsILineIterator.h" // for ScrollContentIntoView
#include "pldhash.h"
#include "mozilla/dom/BeforeAfterKeyboardEventBinding.h"
#include "mozilla/dom/Touch.h"
#include "mozilla/dom/PointerEventBinding.h"
#include "nsIObserverService.h"
@ -705,6 +709,7 @@ static uint32_t sNextPresShellId;
static bool sPointerEventEnabled = true;
static bool sTouchCaretEnabled = false;
static bool sSelectionCaretEnabled = false;
static bool sBeforeAfterKeyboardEventEnabled = false;
/* static */ bool
PresShell::TouchCaretPrefEnabled()
@ -728,6 +733,18 @@ PresShell::SelectionCaretPrefEnabled()
return sSelectionCaretEnabled;
}
/* static */ bool
PresShell::BeforeAfterKeyboardEventEnabled()
{
static bool sInitialized = false;
if (!sInitialized) {
Preferences::AddBoolVarCache(&sBeforeAfterKeyboardEventEnabled,
"dom.beforeAfterKeyboardEvent.enabled");
sInitialized = true;
}
return sBeforeAfterKeyboardEventEnabled;
}
PresShell::PresShell()
: mMouseLocation(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE)
{
@ -6862,6 +6879,236 @@ private:
nsCOMPtr<nsIContent> mContent;
};
static bool
CheckPermissionForBeforeAfterKeyboardEvent(Element* aElement)
{
// An element which is chrome-privileged should be able to handle before
// events and after events.
nsIPrincipal* principal = aElement->NodePrincipal();
if (nsContentUtils::IsSystemPrincipal(principal)) {
return true;
}
// An element which has "before-after-keyboard-event" permission should be
// able to handle before events and after events.
nsCOMPtr<nsIPermissionManager> permMgr = services::GetPermissionManager();
uint32_t permission = nsIPermissionManager::DENY_ACTION;
if (permMgr) {
permMgr->TestPermissionFromPrincipal(principal, "before-after-keyboard-event", &permission);
if (permission == nsIPermissionManager::ALLOW_ACTION) {
return true;
}
// Check "embed-apps" permission for later use.
permission = nsIPermissionManager::DENY_ACTION;
permMgr->TestPermissionFromPrincipal(principal, "embed-apps", &permission);
}
// An element can handle before events and after events if the following
// conditions are met:
// 1) <iframe mozbrowser mozapp>
// 2) it has "embed-apps" permission.
nsCOMPtr<nsIMozBrowserFrame> browserFrame(do_QueryInterface(aElement));
if ((permission == nsIPermissionManager::ALLOW_ACTION) &&
browserFrame && browserFrame->GetReallyIsApp()) {
return true;
}
return false;
}
static void
BuildTargetChainForBeforeAfterKeyboardEvent(nsINode* aTarget,
nsTArray<nsCOMPtr<Element> >& aChain,
bool& aTargetIsIframe)
{
nsCOMPtr<nsIContent> content(do_QueryInterface(aTarget));
nsCOMPtr<nsPIDOMWindow> window;
Element* frameElement;
// Initialize frameElement.
if (content && content->IsHTML(nsGkAtoms::iframe)) {
aTargetIsIframe = true;
frameElement = aTarget->AsElement();
} else {
// If event target is not an iframe, dispatch keydown/keyup event to its
// window after dispatching before events to its ancestors.
aTargetIsIframe = false;
// And skip the event target and get its parent frame.
window = aTarget->OwnerDoc()->GetWindow();
if (window) {
frameElement = window->GetFrameElementInternal();
}
}
// Check permission for all ancestors and add them into the target chain.
while (frameElement) {
if (CheckPermissionForBeforeAfterKeyboardEvent(frameElement)) {
aChain.AppendElement(frameElement);
}
window = frameElement->OwnerDoc()->GetWindow();
frameElement = window ? window->GetFrameElementInternal() : nullptr;
}
}
void
PresShell::DispatchBeforeKeyboardEventInternal(const nsTArray<nsCOMPtr<Element> >& aChain,
const WidgetKeyboardEvent& aEvent,
size_t& aChainIndex,
bool& aDefaultPrevented)
{
size_t length = aChain.Length();
if (!CanDispatchEvent(&aEvent) || !length) {
return;
}
uint32_t message =
(aEvent.message == NS_KEY_DOWN) ? NS_KEY_BEFORE_DOWN : NS_KEY_BEFORE_UP;
nsCOMPtr<EventTarget> eventTarget;
// Dispatch before events from the outermost element.
for (int32_t i = length - 1; i >= 0; i--) {
eventTarget = do_QueryInterface(aChain[i]->OwnerDoc()->GetWindow());
if (!eventTarget || !CanDispatchEvent(&aEvent)) {
return;
}
aChainIndex = i;
InternalBeforeAfterKeyboardEvent beforeEvent(aEvent.mFlags.mIsTrusted,
message, aEvent.widget);
beforeEvent.AssignBeforeAfterKeyEventData(aEvent, false);
EventDispatcher::Dispatch(eventTarget, mPresContext, &beforeEvent);
if (beforeEvent.mFlags.mDefaultPrevented) {
aDefaultPrevented = true;
return;
}
}
}
void
PresShell::DispatchAfterKeyboardEventInternal(const nsTArray<nsCOMPtr<Element> >& aChain,
const WidgetKeyboardEvent& aEvent,
bool aEmbeddedCancelled,
size_t aStartOffset)
{
size_t length = aChain.Length();
if (!CanDispatchEvent(&aEvent) || !length) {
return;
}
uint32_t message =
(aEvent.message == NS_KEY_DOWN) ? NS_KEY_AFTER_DOWN : NS_KEY_AFTER_UP;
bool embeddedCancelled = aEmbeddedCancelled;
nsCOMPtr<EventTarget> eventTarget;
// Dispatch after events from the innermost element.
for (uint32_t i = aStartOffset; i < length; i++) {
eventTarget = do_QueryInterface(aChain[i]->OwnerDoc()->GetWindow());
if (!eventTarget || !CanDispatchEvent(&aEvent)) {
return;
}
InternalBeforeAfterKeyboardEvent afterEvent(aEvent.mFlags.mIsTrusted,
message, aEvent.widget);
afterEvent.AssignBeforeAfterKeyEventData(aEvent, false);
afterEvent.mEmbeddedCancelled.SetValue(embeddedCancelled);
EventDispatcher::Dispatch(eventTarget, mPresContext, &afterEvent);
embeddedCancelled = afterEvent.mFlags.mDefaultPrevented;
}
}
void
PresShell::DispatchAfterKeyboardEvent(nsINode* aTarget,
const WidgetKeyboardEvent& aEvent,
bool aEmbeddedCancelled)
{
MOZ_ASSERT(aTarget);
MOZ_ASSERT(BeforeAfterKeyboardEventEnabled());
if (NS_WARN_IF(aEvent.message != NS_KEY_DOWN &&
aEvent.message != NS_KEY_UP)) {
return;
}
// Build up a target chain. Each item in the chain will receive an after event.
nsAutoTArray<nsCOMPtr<Element>, 5> chain;
bool targetIsIframe = false;
BuildTargetChainForBeforeAfterKeyboardEvent(aTarget, chain, targetIsIframe);
DispatchAfterKeyboardEventInternal(chain, aEvent, aEmbeddedCancelled);
}
bool
PresShell::CanDispatchEvent(const WidgetGUIEvent* aEvent) const
{
bool rv =
mPresContext && !mHaveShutDown && nsContentUtils::IsSafeToRunScript();
if (aEvent) {
rv &= (aEvent && aEvent->widget && !aEvent->widget->Destroyed());
}
return rv;
}
void
PresShell::HandleKeyboardEvent(nsINode* aTarget,
WidgetKeyboardEvent& aEvent,
bool aEmbeddedCancelled,
nsEventStatus* aStatus,
EventDispatchingCallback* aEventCB)
{
if (aEvent.message == NS_KEY_PRESS ||
!BeforeAfterKeyboardEventEnabled()) {
EventDispatcher::Dispatch(aTarget, mPresContext,
&aEvent, nullptr, aStatus, aEventCB);
return;
}
MOZ_ASSERT(aTarget);
MOZ_ASSERT(aEvent.message == NS_KEY_DOWN || aEvent.message == NS_KEY_UP);
// Build up a target chain. Each item in the chain will receive a before event.
nsAutoTArray<nsCOMPtr<Element>, 5> chain;
bool targetIsIframe = false;
BuildTargetChainForBeforeAfterKeyboardEvent(aTarget, chain, targetIsIframe);
// Dispatch before events. If each item in the chain consumes the before
// event and doesn't prevent the default action, we will go further to
// dispatch the actual key event and after events in the reverse order.
// Otherwise, only items which has handled the before event will receive an
// after event.
size_t chainIndex;
bool defaultPrevented = false;
DispatchBeforeKeyboardEventInternal(chain, aEvent, chainIndex,
defaultPrevented);
// Dispatch after events to partial items.
if (defaultPrevented) {
DispatchAfterKeyboardEventInternal(chain, aEvent,
aEvent.mFlags.mDefaultPrevented, chainIndex);
// No need to forward the event to child process.
aEvent.mFlags.mNoCrossProcessBoundaryForwarding = true;
return;
}
// Event listeners may kill nsPresContext and nsPresShell.
if (!CanDispatchEvent()) {
return;
}
// Dispatch actual key event to event target.
EventDispatcher::Dispatch(aTarget, mPresContext,
&aEvent, nullptr, aStatus, aEventCB);
// Event listeners may kill nsPresContext and nsPresShell.
if (targetIsIframe || !CanDispatchEvent()) {
return;
}
// Dispatch after events to all items in the chain.
DispatchAfterKeyboardEventInternal(chain, aEvent,
aEvent.mFlags.mDefaultPrevented);
}
nsresult
PresShell::HandleEvent(nsIFrame* aFrame,
WidgetGUIEvent* aEvent,
@ -7866,6 +8113,9 @@ PresShell::HandleEventInternal(WidgetEvent* aEvent, nsEventStatus* aStatus)
IMEStateManager::DispatchCompositionEvent(eventTarget,
mPresContext, aEvent->AsCompositionEvent(), aStatus,
eventCBPtr);
} else if (aEvent->mClass == eKeyboardEventClass) {
HandleKeyboardEvent(eventTarget, *(aEvent->AsKeyboardEvent()),
false, aStatus, eventCBPtr);
} else {
EventDispatcher::Dispatch(eventTarget, mPresContext,
aEvent, nullptr, aStatus, eventCBPtr);

View File

@ -50,6 +50,7 @@ class nsAutoCauseReflowNotifier;
namespace mozilla {
class CSSStyleSheet;
class EventDispatchingCallback;
} // namespace mozilla
// 250ms. This is actually pref-controlled, but we use this value if we fail
@ -75,6 +76,9 @@ public:
// Selection caret preference
static bool SelectionCaretPrefEnabled();
// BeforeAfterKeyboardEvent preference
static bool BeforeAfterKeyboardEventEnabled();
void Init(nsIDocument* aDocument, nsPresContext* aPresContext,
nsViewManager* aViewManager, nsStyleSet* aStyleSet,
nsCompatibility aCompatMode);
@ -369,6 +373,10 @@ public:
virtual void RecordShadowStyleChange(mozilla::dom::ShadowRoot* aShadowRoot);
virtual void DispatchAfterKeyboardEvent(nsINode* aTarget,
const mozilla::WidgetKeyboardEvent& aEvent,
bool aEmbeddedCancelled) MOZ_OVERRIDE;
void SetNextPaintCompressed() { mNextPaintCompressed = true; }
protected:
@ -719,6 +727,24 @@ protected:
void EvictTouches();
// Methods for dispatching KeyboardEvent and BeforeAfterKeyboardEvent.
void HandleKeyboardEvent(nsINode* aTarget,
mozilla::WidgetKeyboardEvent& aEvent,
bool aEmbeddedCancelled,
nsEventStatus* aStatus,
mozilla::EventDispatchingCallback* aEventCB);
void DispatchBeforeKeyboardEventInternal(
const nsTArray<nsCOMPtr<mozilla::dom::Element> >& aChain,
const mozilla::WidgetKeyboardEvent& aEvent,
size_t& aChainIndex,
bool& aDefaultPrevented);
void DispatchAfterKeyboardEventInternal(
const nsTArray<nsCOMPtr<mozilla::dom::Element> >& aChain,
const mozilla::WidgetKeyboardEvent& aEvent,
bool aEmbeddedCancelled,
size_t aChainIndex = 0);
bool CanDispatchEvent(const mozilla::WidgetGUIEvent* aEvent = nullptr) const;
// A list of images that are visible or almost visible.
nsTHashtable< nsRefPtrHashKey<nsIImageLoadingContent> > mVisibleImages;

View File

@ -4347,6 +4347,9 @@ pref("camera.control.low_memory_thresholdMB", 404);
// UDPSocket API
pref("dom.udpsocket.enabled", false);
// Disable before keyboard events and after keyboard events by default.
pref("dom.beforeAfterKeyboardEvent.enabled", false);
// Experiment: Get TTL from DNS records.
// Unset initially (0); Randomly chosen on first run; will remain unchanged
// unless adjusted by the user or experiment ends. Variants defined in