| |
1 /* This Source Code Form is subject to the terms of the Mozilla Public |
| |
2 * License, v. 2.0. If a copy of the MPL was not distributed with this |
| |
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
| |
4 |
| |
5 #include "TabParent.h" |
| |
6 |
| |
7 // TabParent.h transitively includes <windows.h>, which does |
| |
8 // #define CreateEvent CreateEventW |
| |
9 // That messes up our call to EventDispatcher::CreateEvent below. |
| |
10 |
| |
11 #ifdef CreateEvent |
| |
12 #undef CreateEvent |
| |
13 #endif |
| |
14 |
| |
15 #include "BrowserElementParent.h" |
| |
16 #include "mozilla/EventDispatcher.h" |
| |
17 #include "mozilla/dom/HTMLIFrameElement.h" |
| |
18 #include "nsIDOMCustomEvent.h" |
| |
19 #include "nsIInterfaceRequestorUtils.h" |
| |
20 #include "nsVariant.h" |
| |
21 #include "mozilla/dom/BrowserElementDictionariesBinding.h" |
| |
22 #include "nsCxPusher.h" |
| |
23 #include "GeneratedEventClasses.h" |
| |
24 |
| |
25 using namespace mozilla; |
| |
26 using namespace mozilla::dom; |
| |
27 |
| |
28 namespace { |
| |
29 |
| |
30 using mozilla::BrowserElementParent; |
| |
31 /** |
| |
32 * Create an <iframe mozbrowser> owned by the same document as |
| |
33 * aOpenerFrameElement. |
| |
34 */ |
| |
35 already_AddRefed<HTMLIFrameElement> |
| |
36 CreateIframe(Element* aOpenerFrameElement, const nsAString& aName, bool aRemote) |
| |
37 { |
| |
38 nsNodeInfoManager *nodeInfoManager = |
| |
39 aOpenerFrameElement->OwnerDoc()->NodeInfoManager(); |
| |
40 |
| |
41 nsCOMPtr<nsINodeInfo> nodeInfo = |
| |
42 nodeInfoManager->GetNodeInfo(nsGkAtoms::iframe, |
| |
43 /* aPrefix = */ nullptr, |
| |
44 kNameSpaceID_XHTML, |
| |
45 nsIDOMNode::ELEMENT_NODE); |
| |
46 |
| |
47 nsRefPtr<HTMLIFrameElement> popupFrameElement = |
| |
48 static_cast<HTMLIFrameElement*>( |
| |
49 NS_NewHTMLIFrameElement(nodeInfo.forget(), mozilla::dom::NOT_FROM_PARSER)); |
| |
50 |
| |
51 popupFrameElement->SetMozbrowser(true); |
| |
52 |
| |
53 // Copy the opener frame's mozapp attribute to the popup frame. |
| |
54 if (aOpenerFrameElement->HasAttr(kNameSpaceID_None, nsGkAtoms::mozapp)) { |
| |
55 nsAutoString mozapp; |
| |
56 aOpenerFrameElement->GetAttr(kNameSpaceID_None, nsGkAtoms::mozapp, mozapp); |
| |
57 popupFrameElement->SetAttr(kNameSpaceID_None, nsGkAtoms::mozapp, |
| |
58 mozapp, /* aNotify = */ false); |
| |
59 } |
| |
60 |
| |
61 // Copy the opener frame's parentApp attribute to the popup frame. |
| |
62 if (aOpenerFrameElement->HasAttr(kNameSpaceID_None, nsGkAtoms::parentapp)) { |
| |
63 nsAutoString parentApp; |
| |
64 aOpenerFrameElement->GetAttr(kNameSpaceID_None, nsGkAtoms::parentapp, |
| |
65 parentApp); |
| |
66 popupFrameElement->SetAttr(kNameSpaceID_None, nsGkAtoms::parentapp, |
| |
67 parentApp, /* aNotify = */ false); |
| |
68 } |
| |
69 |
| |
70 // Copy the window name onto the iframe. |
| |
71 popupFrameElement->SetAttr(kNameSpaceID_None, nsGkAtoms::name, |
| |
72 aName, /* aNotify = */ false); |
| |
73 |
| |
74 // Indicate whether the iframe is should be remote. |
| |
75 popupFrameElement->SetAttr(kNameSpaceID_None, nsGkAtoms::Remote, |
| |
76 aRemote ? NS_LITERAL_STRING("true") : |
| |
77 NS_LITERAL_STRING("false"), |
| |
78 /* aNotify = */ false); |
| |
79 |
| |
80 return popupFrameElement.forget(); |
| |
81 } |
| |
82 |
| |
83 bool |
| |
84 DispatchCustomDOMEvent(Element* aFrameElement, const nsAString& aEventName, |
| |
85 JSContext* cx, JS::Handle<JS::Value> aDetailValue, |
| |
86 nsEventStatus *aStatus) |
| |
87 { |
| |
88 NS_ENSURE_TRUE(aFrameElement, false); |
| |
89 nsIPresShell *shell = aFrameElement->OwnerDoc()->GetShell(); |
| |
90 nsRefPtr<nsPresContext> presContext; |
| |
91 if (shell) { |
| |
92 presContext = shell->GetPresContext(); |
| |
93 } |
| |
94 |
| |
95 nsCOMPtr<nsIDOMEvent> domEvent; |
| |
96 EventDispatcher::CreateEvent(aFrameElement, presContext, nullptr, |
| |
97 NS_LITERAL_STRING("customevent"), |
| |
98 getter_AddRefs(domEvent)); |
| |
99 NS_ENSURE_TRUE(domEvent, false); |
| |
100 |
| |
101 nsCOMPtr<nsIDOMCustomEvent> customEvent = do_QueryInterface(domEvent); |
| |
102 NS_ENSURE_TRUE(customEvent, false); |
| |
103 ErrorResult res; |
| |
104 CustomEvent* event = static_cast<CustomEvent*>(customEvent.get()); |
| |
105 event->InitCustomEvent(cx, |
| |
106 aEventName, |
| |
107 /* bubbles = */ true, |
| |
108 /* cancelable = */ true, |
| |
109 aDetailValue, |
| |
110 res); |
| |
111 if (res.Failed()) { |
| |
112 return false; |
| |
113 } |
| |
114 customEvent->SetTrusted(true); |
| |
115 // Dispatch the event. |
| |
116 *aStatus = nsEventStatus_eConsumeNoDefault; |
| |
117 nsresult rv = |
| |
118 EventDispatcher::DispatchDOMEvent(aFrameElement, nullptr, |
| |
119 domEvent, presContext, aStatus); |
| |
120 return NS_SUCCEEDED(rv); |
| |
121 } |
| |
122 |
| |
123 } // anonymous namespace |
| |
124 |
| |
125 namespace mozilla { |
| |
126 |
| |
127 /** |
| |
128 * Dispatch a mozbrowseropenwindow event to the given opener frame element. |
| |
129 * The "popup iframe" (event.detail.frameElement) will be |aPopupFrameElement|. |
| |
130 * |
| |
131 * Returns true iff there were no unexpected failures and the window.open call |
| |
132 * was accepted by the embedder. |
| |
133 */ |
| |
134 /*static*/ |
| |
135 BrowserElementParent::OpenWindowResult |
| |
136 BrowserElementParent::DispatchOpenWindowEvent(Element* aOpenerFrameElement, |
| |
137 Element* aPopupFrameElement, |
| |
138 const nsAString& aURL, |
| |
139 const nsAString& aName, |
| |
140 const nsAString& aFeatures) |
| |
141 { |
| |
142 // Dispatch a CustomEvent at aOpenerFrameElement with a detail object |
| |
143 // (OpenWindowEventDetail) containing aPopupFrameElement, aURL, aName, and |
| |
144 // aFeatures. |
| |
145 |
| |
146 // Create the event's detail object. |
| |
147 OpenWindowEventDetail detail; |
| |
148 detail.mUrl = aURL; |
| |
149 detail.mName = aName; |
| |
150 detail.mFeatures = aFeatures; |
| |
151 detail.mFrameElement = aPopupFrameElement; |
| |
152 |
| |
153 AutoJSContext cx; |
| |
154 JS::Rooted<JS::Value> val(cx); |
| |
155 |
| |
156 nsIGlobalObject* sgo = aPopupFrameElement->OwnerDoc()->GetScopeObject(); |
| |
157 if (!sgo) { |
| |
158 return BrowserElementParent::OPEN_WINDOW_IGNORED; |
| |
159 } |
| |
160 |
| |
161 JS::Rooted<JSObject*> global(cx, sgo->GetGlobalJSObject()); |
| |
162 JSAutoCompartment ac(cx, global); |
| |
163 if (!detail.ToObject(cx, &val)) { |
| |
164 MOZ_CRASH("Failed to convert dictionary to JS::Value due to OOM."); |
| |
165 return BrowserElementParent::OPEN_WINDOW_IGNORED; |
| |
166 } |
| |
167 |
| |
168 nsEventStatus status; |
| |
169 bool dispatchSucceeded = |
| |
170 DispatchCustomDOMEvent(aOpenerFrameElement, |
| |
171 NS_LITERAL_STRING("mozbrowseropenwindow"), |
| |
172 cx, |
| |
173 val, &status); |
| |
174 |
| |
175 if (dispatchSucceeded) { |
| |
176 if (aPopupFrameElement->IsInDoc()) { |
| |
177 return BrowserElementParent::OPEN_WINDOW_ADDED; |
| |
178 } else if (status == nsEventStatus_eConsumeNoDefault) { |
| |
179 // If the frame was not added to a document, report to callers whether |
| |
180 // preventDefault was called on or not |
| |
181 return BrowserElementParent::OPEN_WINDOW_CANCELLED; |
| |
182 } |
| |
183 } |
| |
184 |
| |
185 return BrowserElementParent::OPEN_WINDOW_IGNORED; |
| |
186 } |
| |
187 |
| |
188 /*static*/ |
| |
189 BrowserElementParent::OpenWindowResult |
| |
190 BrowserElementParent::OpenWindowOOP(TabParent* aOpenerTabParent, |
| |
191 TabParent* aPopupTabParent, |
| |
192 const nsAString& aURL, |
| |
193 const nsAString& aName, |
| |
194 const nsAString& aFeatures) |
| |
195 { |
| |
196 // Create an iframe owned by the same document which owns openerFrameElement. |
| |
197 nsCOMPtr<Element> openerFrameElement = aOpenerTabParent->GetOwnerElement(); |
| |
198 NS_ENSURE_TRUE(openerFrameElement, |
| |
199 BrowserElementParent::OPEN_WINDOW_IGNORED); |
| |
200 nsRefPtr<HTMLIFrameElement> popupFrameElement = |
| |
201 CreateIframe(openerFrameElement, aName, /* aRemote = */ true); |
| |
202 |
| |
203 // Normally an <iframe> element will try to create a frameLoader when the |
| |
204 // page touches iframe.contentWindow or sets iframe.src. |
| |
205 // |
| |
206 // But in our case, we want to delay the creation of the frameLoader until |
| |
207 // we've verified that the popup has gone through successfully. If the popup |
| |
208 // is "blocked" by the embedder, we don't want to load the popup's url. |
| |
209 // |
| |
210 // Therefore we call DisallowCreateFrameLoader() on the element and call |
| |
211 // AllowCreateFrameLoader() only after we've verified that the popup was |
| |
212 // allowed. |
| |
213 popupFrameElement->DisallowCreateFrameLoader(); |
| |
214 |
| |
215 OpenWindowResult opened = |
| |
216 DispatchOpenWindowEvent(openerFrameElement, popupFrameElement, |
| |
217 aURL, aName, aFeatures); |
| |
218 |
| |
219 if (opened != BrowserElementParent::OPEN_WINDOW_ADDED) { |
| |
220 return opened; |
| |
221 } |
| |
222 |
| |
223 // The popup was not blocked, so hook up the frame element and the popup tab |
| |
224 // parent, and return success. |
| |
225 aPopupTabParent->SetOwnerElement(popupFrameElement); |
| |
226 popupFrameElement->AllowCreateFrameLoader(); |
| |
227 popupFrameElement->CreateRemoteFrameLoader(aPopupTabParent); |
| |
228 return opened; |
| |
229 } |
| |
230 |
| |
231 /* static */ |
| |
232 BrowserElementParent::OpenWindowResult |
| |
233 BrowserElementParent::OpenWindowInProcess(nsIDOMWindow* aOpenerWindow, |
| |
234 nsIURI* aURI, |
| |
235 const nsAString& aName, |
| |
236 const nsACString& aFeatures, |
| |
237 nsIDOMWindow** aReturnWindow) |
| |
238 { |
| |
239 *aReturnWindow = nullptr; |
| |
240 |
| |
241 // If we call window.open from an <iframe> inside an <iframe mozbrowser>, |
| |
242 // it's as though the top-level document inside the <iframe mozbrowser> |
| |
243 // called window.open. (Indeed, in the OOP case, the inner <iframe> lives |
| |
244 // out-of-process, so we couldn't touch it if we tried.) |
| |
245 // |
| |
246 // GetScriptableTop gets us the <iframe mozbrowser>'s window; we'll use its |
| |
247 // frame element, rather than aOpenerWindow's frame element, as our "opener |
| |
248 // frame element" below. |
| |
249 nsCOMPtr<nsIDOMWindow> topWindow; |
| |
250 aOpenerWindow->GetScriptableTop(getter_AddRefs(topWindow)); |
| |
251 |
| |
252 nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(topWindow); |
| |
253 |
| |
254 nsCOMPtr<Element> openerFrameElement = win->GetFrameElementInternal(); |
| |
255 NS_ENSURE_TRUE(openerFrameElement, BrowserElementParent::OPEN_WINDOW_IGNORED); |
| |
256 |
| |
257 |
| |
258 nsRefPtr<HTMLIFrameElement> popupFrameElement = |
| |
259 CreateIframe(openerFrameElement, aName, /* aRemote = */ false); |
| |
260 NS_ENSURE_TRUE(popupFrameElement, BrowserElementParent::OPEN_WINDOW_IGNORED); |
| |
261 |
| |
262 nsAutoCString spec; |
| |
263 if (aURI) { |
| |
264 aURI->GetSpec(spec); |
| |
265 } |
| |
266 |
| |
267 OpenWindowResult opened = |
| |
268 DispatchOpenWindowEvent(openerFrameElement, popupFrameElement, |
| |
269 NS_ConvertUTF8toUTF16(spec), |
| |
270 aName, |
| |
271 NS_ConvertUTF8toUTF16(aFeatures)); |
| |
272 |
| |
273 if (opened != BrowserElementParent::OPEN_WINDOW_ADDED) { |
| |
274 return opened; |
| |
275 } |
| |
276 |
| |
277 // Return popupFrameElement's window. |
| |
278 nsCOMPtr<nsIFrameLoader> frameLoader; |
| |
279 popupFrameElement->GetFrameLoader(getter_AddRefs(frameLoader)); |
| |
280 NS_ENSURE_TRUE(frameLoader, BrowserElementParent::OPEN_WINDOW_IGNORED); |
| |
281 |
| |
282 nsCOMPtr<nsIDocShell> docshell; |
| |
283 frameLoader->GetDocShell(getter_AddRefs(docshell)); |
| |
284 NS_ENSURE_TRUE(docshell, BrowserElementParent::OPEN_WINDOW_IGNORED); |
| |
285 |
| |
286 nsCOMPtr<nsIDOMWindow> window = do_GetInterface(docshell); |
| |
287 window.forget(aReturnWindow); |
| |
288 |
| |
289 return !!*aReturnWindow ? opened : BrowserElementParent::OPEN_WINDOW_CANCELLED; |
| |
290 } |
| |
291 |
| |
292 class DispatchAsyncScrollEventRunnable : public nsRunnable |
| |
293 { |
| |
294 public: |
| |
295 DispatchAsyncScrollEventRunnable(TabParent* aTabParent, |
| |
296 const CSSRect& aContentRect, |
| |
297 const CSSSize& aContentSize) |
| |
298 : mTabParent(aTabParent) |
| |
299 , mContentRect(aContentRect) |
| |
300 , mContentSize(aContentSize) |
| |
301 {} |
| |
302 |
| |
303 NS_IMETHOD Run(); |
| |
304 |
| |
305 private: |
| |
306 nsRefPtr<TabParent> mTabParent; |
| |
307 const CSSRect mContentRect; |
| |
308 const CSSSize mContentSize; |
| |
309 }; |
| |
310 |
| |
311 NS_IMETHODIMP DispatchAsyncScrollEventRunnable::Run() |
| |
312 { |
| |
313 nsCOMPtr<Element> frameElement = mTabParent->GetOwnerElement(); |
| |
314 NS_ENSURE_STATE(frameElement); |
| |
315 nsIDocument *doc = frameElement->OwnerDoc(); |
| |
316 nsCOMPtr<nsIGlobalObject> globalObject = doc->GetScopeObject(); |
| |
317 NS_ENSURE_TRUE(globalObject, NS_ERROR_UNEXPECTED); |
| |
318 |
| |
319 // Create the event's detail object. |
| |
320 AsyncScrollEventDetail detail; |
| |
321 detail.mLeft = mContentRect.x; |
| |
322 detail.mTop = mContentRect.y; |
| |
323 detail.mWidth = mContentRect.width; |
| |
324 detail.mHeight = mContentRect.height; |
| |
325 detail.mScrollWidth = mContentRect.width; |
| |
326 detail.mScrollHeight = mContentRect.height; |
| |
327 |
| |
328 AutoSafeJSContext cx; |
| |
329 JS::Rooted<JSObject*> globalJSObject(cx, globalObject->GetGlobalJSObject()); |
| |
330 NS_ENSURE_TRUE(globalJSObject, NS_ERROR_UNEXPECTED); |
| |
331 |
| |
332 JSAutoCompartment ac(cx, globalJSObject); |
| |
333 JS::Rooted<JS::Value> val(cx); |
| |
334 |
| |
335 if (!detail.ToObject(cx, &val)) { |
| |
336 MOZ_CRASH("Failed to convert dictionary to JS::Value due to OOM."); |
| |
337 return NS_ERROR_FAILURE; |
| |
338 } |
| |
339 |
| |
340 nsEventStatus status = nsEventStatus_eIgnore; |
| |
341 DispatchCustomDOMEvent(frameElement, |
| |
342 NS_LITERAL_STRING("mozbrowserasyncscroll"), |
| |
343 cx, |
| |
344 val, &status); |
| |
345 return NS_OK; |
| |
346 } |
| |
347 |
| |
348 bool |
| |
349 BrowserElementParent::DispatchAsyncScrollEvent(TabParent* aTabParent, |
| |
350 const CSSRect& aContentRect, |
| |
351 const CSSSize& aContentSize) |
| |
352 { |
| |
353 nsRefPtr<DispatchAsyncScrollEventRunnable> runnable = |
| |
354 new DispatchAsyncScrollEventRunnable(aTabParent, aContentRect, |
| |
355 aContentSize); |
| |
356 return NS_SUCCEEDED(NS_DispatchToMainThread(runnable)); |
| |
357 } |
| |
358 |
| |
359 } // namespace mozilla |