Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* This Source Code Form is subject to the terms of the Mozilla Public
3 : * License, v. 2.0. If a copy of the MPL was not distributed with this
4 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 :
6 : #include "mozilla/layers/FocusTarget.h"
7 :
8 : #include "mozilla/dom/EventTarget.h" // for EventTarget
9 : #include "mozilla/dom/TabParent.h" // for TabParent
10 : #include "mozilla/EventDispatcher.h" // for EventDispatcher
11 : #include "mozilla/layout/RenderFrameParent.h" // For RenderFrameParent
12 : #include "nsIPresShell.h" // for nsIPresShell
13 : #include "nsLayoutUtils.h" // for nsLayoutUtils
14 :
15 : #define ENABLE_FT_LOGGING 0
16 : // #define ENABLE_FT_LOGGING 1
17 :
18 : #if ENABLE_FT_LOGGING
19 : # define FT_LOG(FMT, ...) printf_stderr("FT (%s): " FMT, \
20 : XRE_IsParentProcess() ? "chrome" : "content", \
21 : __VA_ARGS__)
22 : #else
23 : # define FT_LOG(...)
24 : #endif
25 :
26 : using namespace mozilla::dom;
27 : using namespace mozilla::layout;
28 :
29 : namespace mozilla {
30 : namespace layers {
31 :
32 : static already_AddRefed<nsIPresShell>
33 0 : GetRetargetEventPresShell(nsIPresShell* aRootPresShell)
34 : {
35 0 : MOZ_ASSERT(aRootPresShell);
36 :
37 : // Use the last focused window in this PresShell and its
38 : // associated PresShell
39 : nsCOMPtr<nsPIDOMWindowOuter> window =
40 0 : aRootPresShell->GetFocusedDOMWindowInOurWindow();
41 0 : if (!window) {
42 0 : return nullptr;
43 : }
44 :
45 0 : nsCOMPtr<nsIDocument> retargetEventDoc = window->GetExtantDoc();
46 0 : if (!retargetEventDoc) {
47 0 : return nullptr;
48 : }
49 :
50 0 : nsCOMPtr<nsIPresShell> presShell = retargetEventDoc->GetShell();
51 0 : return presShell.forget();
52 : }
53 :
54 : static bool
55 0 : HasListenersForKeyEvents(nsIContent* aContent)
56 : {
57 0 : if (!aContent) {
58 0 : return false;
59 : }
60 :
61 0 : WidgetEvent event(true, eVoidEvent);
62 0 : nsTArray<EventTarget*> targets;
63 : nsresult rv = EventDispatcher::Dispatch(aContent, nullptr, &event, nullptr,
64 0 : nullptr, nullptr, &targets);
65 0 : NS_ENSURE_SUCCESS(rv, false);
66 0 : for (size_t i = 0; i < targets.Length(); i++) {
67 0 : if (targets[i]->HasUntrustedOrNonSystemGroupKeyEventListeners()) {
68 0 : return true;
69 : }
70 : }
71 0 : return false;
72 : }
73 :
74 : static bool
75 0 : IsEditableNode(nsINode* aNode)
76 : {
77 0 : return aNode && aNode->IsEditable();
78 : }
79 :
80 115 : FocusTarget::FocusTarget()
81 : : mSequenceNumber(0)
82 : , mFocusHasKeyEventListeners(false)
83 115 : , mType(FocusTarget::eNone)
84 : {
85 115 : }
86 :
87 0 : FocusTarget::FocusTarget(nsIPresShell* aRootPresShell,
88 0 : uint64_t aFocusSequenceNumber)
89 : : mSequenceNumber(aFocusSequenceNumber)
90 0 : , mFocusHasKeyEventListeners(false)
91 : {
92 0 : MOZ_ASSERT(aRootPresShell);
93 0 : MOZ_ASSERT(NS_IsMainThread());
94 :
95 : // Key events can be retargeted to a child PresShell when there is an iframe
96 0 : nsCOMPtr<nsIPresShell> presShell = GetRetargetEventPresShell(aRootPresShell);
97 :
98 0 : if (!presShell) {
99 : FT_LOG("Creating nil target with seq=%" PRIu64 " (can't find retargeted presshell)\n",
100 : aFocusSequenceNumber);
101 :
102 0 : mType = FocusTarget::eNone;
103 0 : return;
104 : }
105 :
106 : // Get the content that should be scrolled for this PresShell, which is
107 : // the current focused element or the current DOM selection
108 0 : nsCOMPtr<nsIContent> scrollTarget = presShell->GetContentForScrolling();
109 :
110 : // Collect event listener information so we can track what is potentially focus
111 : // changing
112 0 : mFocusHasKeyEventListeners = HasListenersForKeyEvents(scrollTarget);
113 :
114 : // Check if the scroll target is a remote browser
115 0 : if (TabParent* browserParent = TabParent::GetFrom(scrollTarget)) {
116 0 : RenderFrameParent* rfp = browserParent->GetRenderFrame();
117 :
118 : // The globally focused element for scrolling is in a remote layer tree
119 0 : if (rfp) {
120 : FT_LOG("Creating reflayer target with seq=%" PRIu64 ", lt=%" PRIu64 "\n",
121 : aFocusSequenceNumber,
122 : rfp->GetLayersId());
123 :
124 0 : mType = FocusTarget::eRefLayer;
125 0 : mData.mRefLayerId = rfp->GetLayersId();
126 0 : return;
127 : }
128 :
129 : FT_LOG("Creating nil target with seq=%" PRIu64 " (remote browser missing layers id)\n",
130 : aFocusSequenceNumber);
131 :
132 0 : mType = FocusTarget::eNone;
133 0 : return;
134 : }
135 :
136 : // If the focus isn't on a remote browser then check for scrollable targets
137 0 : if (IsEditableNode(scrollTarget) ||
138 0 : IsEditableNode(presShell->GetDocument())) {
139 : FT_LOG("Creating nil target with seq=%" PRIu64 " (disabling for editable node)\n",
140 : aFocusSequenceNumber);
141 :
142 0 : mType = FocusTarget::eNone;
143 0 : return;
144 : }
145 :
146 : // Gather the scrollable frames that would be scrolled in each direction
147 : // for this scroll target
148 : nsIScrollableFrame* horizontal =
149 0 : presShell->GetScrollableFrameToScrollForContent(scrollTarget.get(),
150 0 : nsIPresShell::eHorizontal);
151 : nsIScrollableFrame* vertical =
152 0 : presShell->GetScrollableFrameToScrollForContent(scrollTarget.get(),
153 0 : nsIPresShell::eVertical);
154 :
155 : // We might have the globally focused element for scrolling. Gather a ViewID for
156 : // the horizontal and vertical scroll targets of this element.
157 0 : mType = FocusTarget::eScrollLayer;
158 0 : mData.mScrollTargets.mHorizontal =
159 0 : nsLayoutUtils::FindIDForScrollableFrame(horizontal);
160 0 : mData.mScrollTargets.mVertical =
161 0 : nsLayoutUtils::FindIDForScrollableFrame(vertical);
162 :
163 : FT_LOG("Creating scroll target with seq=%" PRIu64 ", h=%" PRIu64 ", v=%" PRIu64 "\n",
164 : aFocusSequenceNumber,
165 : mData.mScrollTargets.mHorizontal,
166 : mData.mScrollTargets.mVertical);
167 : }
168 :
169 : bool
170 0 : FocusTarget::operator==(const FocusTarget& aRhs) const
171 : {
172 0 : if (mSequenceNumber != aRhs.mSequenceNumber ||
173 0 : mFocusHasKeyEventListeners != aRhs.mFocusHasKeyEventListeners ||
174 0 : mType != aRhs.mType) {
175 0 : return false;
176 : }
177 :
178 0 : if (mType == FocusTarget::eRefLayer) {
179 0 : return mData.mRefLayerId == aRhs.mData.mRefLayerId;
180 0 : } else if (mType == FocusTarget::eScrollLayer) {
181 0 : return mData.mScrollTargets.mHorizontal == aRhs.mData.mScrollTargets.mHorizontal &&
182 0 : mData.mScrollTargets.mVertical == aRhs.mData.mScrollTargets.mVertical;
183 : }
184 0 : return true;
185 : }
186 :
187 : } // namespace layers
188 : } // namespace mozilla
|