Bumping manifests a=b2g-bump
[gecko.git] / dom / browser-element / BrowserElementParent.cpp
blob62ca3c119d1d65d692fb43e04729ab3a003f4cfb
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/. */
5 #include "TabParent.h"
7 // TabParent.h transitively includes <windows.h>, which does
8 // #define CreateEvent CreateEventW
9 // That messes up our call to EventDispatcher::CreateEvent below.
11 #ifdef CreateEvent
12 #undef CreateEvent
13 #endif
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;
27 namespace {
29 using mozilla::BrowserElementParent;
30 /**
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,
43 kNameSpaceID_XHTML,
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)) {
54 nsAutoString 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,
64 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();
82 bool
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;
90 if (shell) {
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);
102 ErrorResult res;
103 CustomEvent* event = static_cast<CustomEvent*>(customEvent.get());
104 event->InitCustomEvent(cx,
105 aEventName,
106 /* bubbles = */ true,
107 /* cancelable = */ true,
108 aDetailValue,
109 res);
110 if (res.Failed()) {
111 return false;
113 customEvent->SetTrusted(true);
114 // Dispatch the event.
115 *aStatus = nsEventStatus_eConsumeNoDefault;
116 nsresult rv =
117 EventDispatcher::DispatchDOMEvent(aFrameElement, nullptr,
118 domEvent, presContext, aStatus);
119 return NS_SUCCEEDED(rv);
122 } // anonymous namespace
124 namespace mozilla {
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.
133 /*static*/
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
143 // aFeatures.
145 // Create the event's detail object.
146 OpenWindowEventDetail detail;
147 detail.mUrl = aURL;
148 detail.mName = aName;
149 detail.mFeatures = aFeatures;
150 detail.mFrameElement = aPopupFrameElement;
152 AutoJSContext cx;
153 JS::Rooted<JS::Value> val(cx);
155 nsIGlobalObject* sgo = aPopupFrameElement->OwnerDoc()->GetScopeObject();
156 if (!sgo) {
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"),
179 val, &status);
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;
194 /*static*/
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
218 // allowed.
219 popupFrameElement->DisallowCreateFrameLoader();
221 OpenWindowResult opened =
222 DispatchOpenWindowEvent(openerFrameElement, popupFrameElement,
223 aURL, aName, aFeatures);
225 if (opened != BrowserElementParent::OPEN_WINDOW_ADDED) {
226 return opened;
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);
234 return opened;
237 /* static */
238 BrowserElementParent::OpenWindowResult
239 BrowserElementParent::OpenWindowInProcess(nsIDOMWindow* aOpenerWindow,
240 nsIURI* aURI,
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);
268 nsAutoCString spec;
269 if (aURI) {
270 aURI->GetSpec(spec);
273 OpenWindowResult opened =
274 DispatchOpenWindowEvent(openerFrameElement, popupFrameElement,
275 NS_ConvertUTF8toUTF16(spec),
276 aName,
277 NS_ConvertUTF8toUTF16(aFeatures));
279 if (opened != BrowserElementParent::OPEN_WINDOW_ADDED) {
280 return opened;
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
300 public:
301 DispatchAsyncScrollEventRunnable(TabParent* aTabParent,
302 const CSSRect& aContentRect,
303 const CSSSize& aContentSize)
304 : mTabParent(aTabParent)
305 , mContentRect(aContentRect)
306 , mContentSize(aContentSize)
309 NS_IMETHOD Run();
311 private:
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"),
350 val, &status);
351 return NS_OK;
354 bool
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()) {
364 return true;
367 nsRefPtr<DispatchAsyncScrollEventRunnable> runnable =
368 new DispatchAsyncScrollEventRunnable(aTabParent, aContentRect,
369 aContentSize);
370 return NS_SUCCEEDED(NS_DispatchToMainThread(runnable));
373 } // namespace mozilla