Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 : /* This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this
5 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : #include "mozilla/layers/FocusTarget.h"
8 :
9 : #include "mozilla/dom/EventTarget.h" // for EventTarget
10 : #include "mozilla/dom/TabParent.h" // for TabParent
11 : #include "mozilla/EventDispatcher.h" // for EventDispatcher
12 : #include "mozilla/layout/RenderFrameParent.h" // For RenderFrameParent
13 : #include "nsIContentInlines.h" // for nsINode::IsEditable()
14 : #include "nsIPresShell.h" // for nsIPresShell
15 : #include "nsLayoutUtils.h" // for nsLayoutUtils
16 :
17 : #define ENABLE_FT_LOGGING 0
18 : // #define ENABLE_FT_LOGGING 1
19 :
20 : #if ENABLE_FT_LOGGING
21 : # define FT_LOG(FMT, ...) printf_stderr("FT (%s): " FMT, \
22 : XRE_IsParentProcess() ? "chrome" : "content", \
23 : __VA_ARGS__)
24 : #else
25 : # define FT_LOG(...)
26 : #endif
27 :
28 : using namespace mozilla::dom;
29 : using namespace mozilla::layout;
30 :
31 : namespace mozilla {
32 : namespace layers {
33 :
34 : static already_AddRefed<nsIPresShell>
35 7 : GetRetargetEventPresShell(nsIPresShell* aRootPresShell)
36 : {
37 7 : MOZ_ASSERT(aRootPresShell);
38 :
39 : // Use the last focused window in this PresShell and its
40 : // associated PresShell
41 : nsCOMPtr<nsPIDOMWindowOuter> window =
42 0 : aRootPresShell->GetFocusedDOMWindowInOurWindow();
43 7 : if (!window) {
44 : return nullptr;
45 : }
46 :
47 0 : nsCOMPtr<nsIDocument> retargetEventDoc = window->GetExtantDoc();
48 7 : if (!retargetEventDoc) {
49 : return nullptr;
50 : }
51 :
52 0 : nsCOMPtr<nsIPresShell> presShell = retargetEventDoc->GetShell();
53 7 : return presShell.forget();
54 : }
55 :
56 : static bool
57 0 : HasListenersForKeyEvents(nsIContent* aContent)
58 : {
59 0 : if (!aContent) {
60 : return false;
61 : }
62 :
63 0 : WidgetEvent event(true, eVoidEvent);
64 0 : nsTArray<EventTarget*> targets;
65 : nsresult rv = EventDispatcher::Dispatch(aContent, nullptr, &event, nullptr,
66 0 : nullptr, nullptr, &targets);
67 0 : NS_ENSURE_SUCCESS(rv, false);
68 0 : for (size_t i = 0; i < targets.Length(); i++) {
69 0 : if (targets[i]->HasNonSystemGroupListenersForUntrustedKeyEvents()) {
70 : return true;
71 : }
72 : }
73 : return false;
74 : }
75 :
76 : static bool
77 7 : HasListenersForNonPassiveKeyEvents(nsIContent* aContent)
78 : {
79 7 : if (!aContent) {
80 : return false;
81 : }
82 :
83 0 : WidgetEvent event(true, eVoidEvent);
84 14 : nsTArray<EventTarget*> targets;
85 : nsresult rv = EventDispatcher::Dispatch(aContent, nullptr, &event, nullptr,
86 0 : nullptr, nullptr, &targets);
87 0 : NS_ENSURE_SUCCESS(rv, false);
88 0 : for (size_t i = 0; i < targets.Length(); i++) {
89 28 : if (targets[i]->HasNonPassiveNonSystemGroupListenersForUntrustedKeyEvents()) {
90 : return true;
91 : }
92 : }
93 : return false;
94 : }
95 :
96 : static bool
97 14 : IsEditableNode(nsINode* aNode)
98 : {
99 14 : return aNode && aNode->IsEditable();
100 : }
101 :
102 53 : FocusTarget::FocusTarget()
103 : : mSequenceNumber(0)
104 : , mFocusHasKeyEventListeners(false)
105 159 : , mData(AsVariant(NoFocusTarget()))
106 : {
107 53 : }
108 :
109 0 : FocusTarget::FocusTarget(nsIPresShell* aRootPresShell,
110 7 : uint64_t aFocusSequenceNumber)
111 : : mSequenceNumber(aFocusSequenceNumber)
112 : , mFocusHasKeyEventListeners(false)
113 21 : , mData(AsVariant(NoFocusTarget()))
114 : {
115 0 : MOZ_ASSERT(aRootPresShell);
116 7 : MOZ_ASSERT(NS_IsMainThread());
117 :
118 : // Key events can be retargeted to a child PresShell when there is an iframe
119 7 : nsCOMPtr<nsIPresShell> presShell = GetRetargetEventPresShell(aRootPresShell);
120 :
121 7 : if (!presShell) {
122 : FT_LOG("Creating nil target with seq=%" PRIu64 " (can't find retargeted presshell)\n",
123 : aFocusSequenceNumber);
124 :
125 7 : return;
126 : }
127 :
128 0 : nsCOMPtr<nsIDocument> document = presShell->GetDocument();
129 7 : if (!document) {
130 : FT_LOG("Creating nil target with seq=%" PRIu64 " (no document)\n",
131 : aFocusSequenceNumber);
132 :
133 7 : return;
134 : }
135 :
136 : // Find the focused content and use it to determine whether there are key event
137 : // listeners or whether key events will be targeted at a different process
138 : // through a remote browser.
139 0 : nsCOMPtr<nsIContent> focusedContent = presShell->GetFocusedContentInOurWindow();
140 7 : nsCOMPtr<nsIContent> keyEventTarget = focusedContent;
141 :
142 : // If there is no focused element then event dispatch goes to the body of
143 : // the page if it exists or the root element.
144 0 : if (!keyEventTarget) {
145 0 : keyEventTarget = document->GetUnfocusedKeyEventTarget();
146 : }
147 :
148 : // Check if there are key event listeners that could prevent default or change
149 : // the focus or selection of the page.
150 0 : if (gfxPrefs::APZKeyboardPassiveListeners()) {
151 7 : mFocusHasKeyEventListeners = HasListenersForNonPassiveKeyEvents(keyEventTarget.get());
152 : } else {
153 0 : mFocusHasKeyEventListeners = HasListenersForKeyEvents(keyEventTarget.get());
154 : }
155 :
156 : // Check if the key event target is content editable or if the document
157 : // is in design mode.
158 0 : if (IsEditableNode(keyEventTarget) ||
159 7 : IsEditableNode(document)) {
160 : FT_LOG("Creating nil target with seq=%" PRIu64 ", kl=%d (disabling for editable node)\n",
161 : aFocusSequenceNumber,
162 : static_cast<int>(mFocusHasKeyEventListeners));
163 :
164 7 : return;
165 : }
166 :
167 : // Check if the key event target is a remote browser
168 0 : if (TabParent* browserParent = TabParent::GetFrom(keyEventTarget)) {
169 0 : RenderFrameParent* rfp = browserParent->GetRenderFrame();
170 :
171 : // The globally focused element for scrolling is in a remote layer tree
172 0 : if (rfp) {
173 : FT_LOG("Creating reflayer target with seq=%" PRIu64 ", kl=%d, lt=%" PRIu64 "\n",
174 : aFocusSequenceNumber,
175 : mFocusHasKeyEventListeners,
176 : rfp->GetLayersId());
177 :
178 0 : mData = AsVariant<LayersId>(rfp->GetLayersId());
179 0 : return;
180 : }
181 :
182 : FT_LOG("Creating nil target with seq=%" PRIu64 ", kl=%d (remote browser missing layers id)\n",
183 : aFocusSequenceNumber,
184 : mFocusHasKeyEventListeners);
185 :
186 : return;
187 : }
188 :
189 : // The content to scroll is either the focused element or the focus node of
190 : // the selection. It's difficult to determine if an element is an interactive
191 : // element requiring async keyboard scrolling to be disabled. So we only
192 : // allow async key scrolling based on the selection, which doesn't have
193 : // this problem and is more common.
194 7 : if (focusedContent) {
195 : FT_LOG("Creating nil target with seq=%" PRIu64 ", kl=%d (disabling for focusing an element)\n",
196 : aFocusSequenceNumber,
197 : mFocusHasKeyEventListeners);
198 :
199 : return;
200 : }
201 :
202 0 : nsCOMPtr<nsIContent> selectedContent = presShell->GetSelectedContentForScrolling();
203 :
204 : // Gather the scrollable frames that would be scrolled in each direction
205 : // for this scroll target
206 : nsIScrollableFrame* horizontal =
207 0 : presShell->GetScrollableFrameToScrollForContent(selectedContent.get(),
208 0 : nsIPresShell::eHorizontal);
209 : nsIScrollableFrame* vertical =
210 0 : presShell->GetScrollableFrameToScrollForContent(selectedContent.get(),
211 0 : nsIPresShell::eVertical);
212 :
213 : // We might have the globally focused element for scrolling. Gather a ViewID for
214 : // the horizontal and vertical scroll targets of this element.
215 : ScrollTargets target;
216 0 : target.mHorizontal = nsLayoutUtils::FindIDForScrollableFrame(horizontal);
217 0 : target.mVertical = nsLayoutUtils::FindIDForScrollableFrame(vertical);
218 0 : mData = AsVariant(target);
219 :
220 : FT_LOG("Creating scroll target with seq=%" PRIu64 ", kl=%d, h=%" PRIu64 ", v=%" PRIu64 "\n",
221 : aFocusSequenceNumber,
222 : mFocusHasKeyEventListeners,
223 : target.mHorizontal,
224 : target.mVertical);
225 : }
226 :
227 : bool
228 0 : FocusTarget::operator==(const FocusTarget& aRhs) const
229 : {
230 0 : return mSequenceNumber == aRhs.mSequenceNumber &&
231 0 : mFocusHasKeyEventListeners == aRhs.mFocusHasKeyEventListeners &&
232 0 : mData == aRhs.mData;
233 : }
234 :
235 : const char*
236 0 : FocusTarget::Type() const
237 : {
238 0 : if (mData.is<LayersId>()) {
239 : return "LayersId";
240 : }
241 0 : if (mData.is<ScrollTargets>()) {
242 : return "ScrollTargets";
243 : }
244 0 : if (mData.is<NoFocusTarget>()) {
245 : return "NoFocusTarget";
246 : }
247 : return "<unknown>";
248 : }
249 :
250 : } // namespace layers
251 : } // namespace mozilla
|