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/. */
7 // TabParent.h transitively includes <windows.h>, which does
8 // #define CreateEvent CreateEventW
9 // That messes up our call to EventDispatcher::CreateEvent below.
15 #include "BrowserElementParent.h"
16 #include "mozilla/EventDispatcher.h"
17 #include "mozilla/dom/HTMLIFrameElement.h"
18 #include "mozilla/dom/ToJSValue.h"
19 #include "nsIInterfaceRequestorUtils.h"
20 #include "nsVariant.h"
21 #include "mozilla/dom/BrowserElementDictionariesBinding.h"
22 #include "mozilla/dom/CustomEvent.h"
24 using namespace mozilla
;
25 using namespace mozilla::dom
;
29 using mozilla::BrowserElementParent
;
31 * Create an <iframe mozbrowser> owned by the same document as
32 * aOpenerFrameElement.
34 already_AddRefed
<HTMLIFrameElement
>
35 CreateIframe(Element
* aOpenerFrameElement
, const nsAString
& aName
, bool aRemote
)
37 nsNodeInfoManager
*nodeInfoManager
=
38 aOpenerFrameElement
->OwnerDoc()->NodeInfoManager();
40 nsRefPtr
<NodeInfo
> nodeInfo
=
41 nodeInfoManager
->GetNodeInfo(nsGkAtoms::iframe
,
42 /* aPrefix = */ nullptr,
44 nsIDOMNode::ELEMENT_NODE
);
46 nsRefPtr
<HTMLIFrameElement
> popupFrameElement
=
47 static_cast<HTMLIFrameElement
*>(
48 NS_NewHTMLIFrameElement(nodeInfo
.forget(), mozilla::dom::NOT_FROM_PARSER
));
50 popupFrameElement
->SetMozbrowser(true);
52 // Copy the opener frame's mozapp attribute to the popup frame.
53 if (aOpenerFrameElement
->HasAttr(kNameSpaceID_None
, nsGkAtoms::mozapp
)) {
55 aOpenerFrameElement
->GetAttr(kNameSpaceID_None
, nsGkAtoms::mozapp
, mozapp
);
56 popupFrameElement
->SetAttr(kNameSpaceID_None
, nsGkAtoms::mozapp
,
57 mozapp
, /* aNotify = */ false);
60 // Copy the opener frame's parentApp attribute to the popup frame.
61 if (aOpenerFrameElement
->HasAttr(kNameSpaceID_None
, nsGkAtoms::parentapp
)) {
62 nsAutoString parentApp
;
63 aOpenerFrameElement
->GetAttr(kNameSpaceID_None
, nsGkAtoms::parentapp
,
65 popupFrameElement
->SetAttr(kNameSpaceID_None
, nsGkAtoms::parentapp
,
66 parentApp
, /* aNotify = */ false);
69 // Copy the window name onto the iframe.
70 popupFrameElement
->SetAttr(kNameSpaceID_None
, nsGkAtoms::name
,
71 aName
, /* aNotify = */ false);
73 // Indicate whether the iframe is should be remote.
74 popupFrameElement
->SetAttr(kNameSpaceID_None
, nsGkAtoms::Remote
,
75 aRemote
? NS_LITERAL_STRING("true") :
76 NS_LITERAL_STRING("false"),
77 /* aNotify = */ false);
79 return popupFrameElement
.forget();
83 DispatchCustomDOMEvent(Element
* aFrameElement
, const nsAString
& aEventName
,
84 JSContext
* cx
, JS::Handle
<JS::Value
> aDetailValue
,
85 nsEventStatus
*aStatus
)
87 NS_ENSURE_TRUE(aFrameElement
, false);
88 nsIPresShell
*shell
= aFrameElement
->OwnerDoc()->GetShell();
89 nsRefPtr
<nsPresContext
> presContext
;
91 presContext
= shell
->GetPresContext();
94 nsCOMPtr
<nsIDOMEvent
> domEvent
;
95 EventDispatcher::CreateEvent(aFrameElement
, presContext
, nullptr,
96 NS_LITERAL_STRING("customevent"),
97 getter_AddRefs(domEvent
));
98 NS_ENSURE_TRUE(domEvent
, false);
100 nsCOMPtr
<nsIDOMCustomEvent
> customEvent
= do_QueryInterface(domEvent
);
101 NS_ENSURE_TRUE(customEvent
, false);
103 CustomEvent
* event
= static_cast<CustomEvent
*>(customEvent
.get());
104 event
->InitCustomEvent(cx
,
106 /* bubbles = */ true,
107 /* cancelable = */ true,
113 customEvent
->SetTrusted(true);
114 // Dispatch the event.
115 *aStatus
= nsEventStatus_eConsumeNoDefault
;
117 EventDispatcher::DispatchDOMEvent(aFrameElement
, nullptr,
118 domEvent
, presContext
, aStatus
);
119 return NS_SUCCEEDED(rv
);
122 } // anonymous namespace
127 * Dispatch a mozbrowseropenwindow event to the given opener frame element.
128 * The "popup iframe" (event.detail.frameElement) will be |aPopupFrameElement|.
130 * Returns true iff there were no unexpected failures and the window.open call
131 * was accepted by the embedder.
134 BrowserElementParent::OpenWindowResult
135 BrowserElementParent::DispatchOpenWindowEvent(Element
* aOpenerFrameElement
,
136 Element
* aPopupFrameElement
,
137 const nsAString
& aURL
,
138 const nsAString
& aName
,
139 const nsAString
& aFeatures
)
141 // Dispatch a CustomEvent at aOpenerFrameElement with a detail object
142 // (OpenWindowEventDetail) containing aPopupFrameElement, aURL, aName, and
145 // Create the event's detail object.
146 OpenWindowEventDetail detail
;
148 detail
.mName
= aName
;
149 detail
.mFeatures
= aFeatures
;
150 detail
.mFrameElement
= aPopupFrameElement
;
153 JS::Rooted
<JS::Value
> val(cx
);
155 nsIGlobalObject
* sgo
= aPopupFrameElement
->OwnerDoc()->GetScopeObject();
157 return BrowserElementParent::OPEN_WINDOW_IGNORED
;
160 JS::Rooted
<JSObject
*> global(cx
, sgo
->GetGlobalJSObject());
161 JSAutoCompartment
ac(cx
, global
);
162 if (!ToJSValue(cx
, detail
, &val
)) {
163 MOZ_CRASH("Failed to convert dictionary to JS::Value due to OOM.");
164 return BrowserElementParent::OPEN_WINDOW_IGNORED
;
167 // Do not dispatch a mozbrowseropenwindow event of a widget to its embedder
168 nsCOMPtr
<nsIMozBrowserFrame
> browserFrame
=
169 do_QueryInterface(aOpenerFrameElement
);
170 if (browserFrame
&& browserFrame
->GetReallyIsWidget()) {
171 return BrowserElementParent::OPEN_WINDOW_CANCELLED
;
174 nsEventStatus status
;
175 bool dispatchSucceeded
=
176 DispatchCustomDOMEvent(aOpenerFrameElement
,
177 NS_LITERAL_STRING("mozbrowseropenwindow"),
181 if (dispatchSucceeded
) {
182 if (aPopupFrameElement
->IsInDoc()) {
183 return BrowserElementParent::OPEN_WINDOW_ADDED
;
184 } else if (status
== nsEventStatus_eConsumeNoDefault
) {
185 // If the frame was not added to a document, report to callers whether
186 // preventDefault was called on or not
187 return BrowserElementParent::OPEN_WINDOW_CANCELLED
;
191 return BrowserElementParent::OPEN_WINDOW_IGNORED
;
195 BrowserElementParent::OpenWindowResult
196 BrowserElementParent::OpenWindowOOP(TabParent
* aOpenerTabParent
,
197 TabParent
* aPopupTabParent
,
198 const nsAString
& aURL
,
199 const nsAString
& aName
,
200 const nsAString
& aFeatures
)
202 // Create an iframe owned by the same document which owns openerFrameElement.
203 nsCOMPtr
<Element
> openerFrameElement
= aOpenerTabParent
->GetOwnerElement();
204 NS_ENSURE_TRUE(openerFrameElement
,
205 BrowserElementParent::OPEN_WINDOW_IGNORED
);
206 nsRefPtr
<HTMLIFrameElement
> popupFrameElement
=
207 CreateIframe(openerFrameElement
, aName
, /* aRemote = */ true);
209 // Normally an <iframe> element will try to create a frameLoader when the
210 // page touches iframe.contentWindow or sets iframe.src.
212 // But in our case, we want to delay the creation of the frameLoader until
213 // we've verified that the popup has gone through successfully. If the popup
214 // is "blocked" by the embedder, we don't want to load the popup's url.
216 // Therefore we call DisallowCreateFrameLoader() on the element and call
217 // AllowCreateFrameLoader() only after we've verified that the popup was
219 popupFrameElement
->DisallowCreateFrameLoader();
221 OpenWindowResult opened
=
222 DispatchOpenWindowEvent(openerFrameElement
, popupFrameElement
,
223 aURL
, aName
, aFeatures
);
225 if (opened
!= BrowserElementParent::OPEN_WINDOW_ADDED
) {
229 // The popup was not blocked, so hook up the frame element and the popup tab
230 // parent, and return success.
231 aPopupTabParent
->SetOwnerElement(popupFrameElement
);
232 popupFrameElement
->AllowCreateFrameLoader();
233 popupFrameElement
->CreateRemoteFrameLoader(aPopupTabParent
);
238 BrowserElementParent::OpenWindowResult
239 BrowserElementParent::OpenWindowInProcess(nsIDOMWindow
* aOpenerWindow
,
241 const nsAString
& aName
,
242 const nsACString
& aFeatures
,
243 nsIDOMWindow
** aReturnWindow
)
245 *aReturnWindow
= nullptr;
247 // If we call window.open from an <iframe> inside an <iframe mozbrowser>,
248 // it's as though the top-level document inside the <iframe mozbrowser>
249 // called window.open. (Indeed, in the OOP case, the inner <iframe> lives
250 // out-of-process, so we couldn't touch it if we tried.)
252 // GetScriptableTop gets us the <iframe mozbrowser>'s window; we'll use its
253 // frame element, rather than aOpenerWindow's frame element, as our "opener
254 // frame element" below.
255 nsCOMPtr
<nsIDOMWindow
> topWindow
;
256 aOpenerWindow
->GetScriptableTop(getter_AddRefs(topWindow
));
258 nsCOMPtr
<nsPIDOMWindow
> win
= do_QueryInterface(topWindow
);
260 nsCOMPtr
<Element
> openerFrameElement
= win
->GetFrameElementInternal();
261 NS_ENSURE_TRUE(openerFrameElement
, BrowserElementParent::OPEN_WINDOW_IGNORED
);
264 nsRefPtr
<HTMLIFrameElement
> popupFrameElement
=
265 CreateIframe(openerFrameElement
, aName
, /* aRemote = */ false);
266 NS_ENSURE_TRUE(popupFrameElement
, BrowserElementParent::OPEN_WINDOW_IGNORED
);
273 OpenWindowResult opened
=
274 DispatchOpenWindowEvent(openerFrameElement
, popupFrameElement
,
275 NS_ConvertUTF8toUTF16(spec
),
277 NS_ConvertUTF8toUTF16(aFeatures
));
279 if (opened
!= BrowserElementParent::OPEN_WINDOW_ADDED
) {
283 // Return popupFrameElement's window.
284 nsCOMPtr
<nsIFrameLoader
> frameLoader
;
285 popupFrameElement
->GetFrameLoader(getter_AddRefs(frameLoader
));
286 NS_ENSURE_TRUE(frameLoader
, BrowserElementParent::OPEN_WINDOW_IGNORED
);
288 nsCOMPtr
<nsIDocShell
> docshell
;
289 frameLoader
->GetDocShell(getter_AddRefs(docshell
));
290 NS_ENSURE_TRUE(docshell
, BrowserElementParent::OPEN_WINDOW_IGNORED
);
292 nsCOMPtr
<nsIDOMWindow
> window
= docshell
->GetWindow();
293 window
.forget(aReturnWindow
);
295 return !!*aReturnWindow
? opened
: BrowserElementParent::OPEN_WINDOW_CANCELLED
;
298 class DispatchAsyncScrollEventRunnable
: public nsRunnable
301 DispatchAsyncScrollEventRunnable(TabParent
* aTabParent
,
302 const CSSRect
& aContentRect
,
303 const CSSSize
& aContentSize
)
304 : mTabParent(aTabParent
)
305 , mContentRect(aContentRect
)
306 , mContentSize(aContentSize
)
312 nsRefPtr
<TabParent
> mTabParent
;
313 const CSSRect mContentRect
;
314 const CSSSize mContentSize
;
317 NS_IMETHODIMP
DispatchAsyncScrollEventRunnable::Run()
319 nsCOMPtr
<Element
> frameElement
= mTabParent
->GetOwnerElement();
320 NS_ENSURE_STATE(frameElement
);
321 nsIDocument
*doc
= frameElement
->OwnerDoc();
322 nsCOMPtr
<nsIGlobalObject
> globalObject
= doc
->GetScopeObject();
323 NS_ENSURE_TRUE(globalObject
, NS_ERROR_UNEXPECTED
);
325 // Create the event's detail object.
326 AsyncScrollEventDetail detail
;
327 detail
.mLeft
= mContentRect
.x
;
328 detail
.mTop
= mContentRect
.y
;
329 detail
.mWidth
= mContentRect
.width
;
330 detail
.mHeight
= mContentRect
.height
;
331 detail
.mScrollWidth
= mContentRect
.width
;
332 detail
.mScrollHeight
= mContentRect
.height
;
334 AutoSafeJSContext cx
;
335 JS::Rooted
<JSObject
*> globalJSObject(cx
, globalObject
->GetGlobalJSObject());
336 NS_ENSURE_TRUE(globalJSObject
, NS_ERROR_UNEXPECTED
);
338 JSAutoCompartment
ac(cx
, globalJSObject
);
339 JS::Rooted
<JS::Value
> val(cx
);
341 if (!ToJSValue(cx
, detail
, &val
)) {
342 MOZ_CRASH("Failed to convert dictionary to JS::Value due to OOM.");
343 return NS_ERROR_FAILURE
;
346 nsEventStatus status
= nsEventStatus_eIgnore
;
347 DispatchCustomDOMEvent(frameElement
,
348 NS_LITERAL_STRING("mozbrowserasyncscroll"),
355 BrowserElementParent::DispatchAsyncScrollEvent(TabParent
* aTabParent
,
356 const CSSRect
& aContentRect
,
357 const CSSSize
& aContentSize
)
359 // Do not dispatch a mozbrowserasyncscroll event of a widget to its embedder
360 nsCOMPtr
<Element
> frameElement
= aTabParent
->GetOwnerElement();
361 NS_ENSURE_TRUE(frameElement
, false);
362 nsCOMPtr
<nsIMozBrowserFrame
> browserFrame
= do_QueryInterface(frameElement
);
363 if (browserFrame
&& browserFrame
->GetReallyIsWidget()) {
367 nsRefPtr
<DispatchAsyncScrollEventRunnable
> runnable
=
368 new DispatchAsyncScrollEventRunnable(aTabParent
, aContentRect
,
370 return NS_SUCCEEDED(NS_DispatchToMainThread(runnable
));
373 } // namespace mozilla