|
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 |