Wed, 31 Dec 2014 06:55:50 +0100
Added tag UPSTREAM_283F7C6 for changeset ca08bd8f51b2
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 "nsIDOMCustomEvent.h"
19 #include "nsIInterfaceRequestorUtils.h"
20 #include "nsVariant.h"
21 #include "mozilla/dom/BrowserElementDictionariesBinding.h"
22 #include "nsCxPusher.h"
23 #include "GeneratedEventClasses.h"
25 using namespace mozilla;
26 using namespace mozilla::dom;
28 namespace {
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();
41 nsCOMPtr<nsINodeInfo> nodeInfo =
42 nodeInfoManager->GetNodeInfo(nsGkAtoms::iframe,
43 /* aPrefix = */ nullptr,
44 kNameSpaceID_XHTML,
45 nsIDOMNode::ELEMENT_NODE);
47 nsRefPtr<HTMLIFrameElement> popupFrameElement =
48 static_cast<HTMLIFrameElement*>(
49 NS_NewHTMLIFrameElement(nodeInfo.forget(), mozilla::dom::NOT_FROM_PARSER));
51 popupFrameElement->SetMozbrowser(true);
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 }
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 }
70 // Copy the window name onto the iframe.
71 popupFrameElement->SetAttr(kNameSpaceID_None, nsGkAtoms::name,
72 aName, /* aNotify = */ false);
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);
80 return popupFrameElement.forget();
81 }
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 }
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);
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 }
123 } // anonymous namespace
125 namespace mozilla {
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.
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;
153 AutoJSContext cx;
154 JS::Rooted<JS::Value> val(cx);
156 nsIGlobalObject* sgo = aPopupFrameElement->OwnerDoc()->GetScopeObject();
157 if (!sgo) {
158 return BrowserElementParent::OPEN_WINDOW_IGNORED;
159 }
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 }
168 nsEventStatus status;
169 bool dispatchSucceeded =
170 DispatchCustomDOMEvent(aOpenerFrameElement,
171 NS_LITERAL_STRING("mozbrowseropenwindow"),
172 cx,
173 val, &status);
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 }
185 return BrowserElementParent::OPEN_WINDOW_IGNORED;
186 }
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);
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();
215 OpenWindowResult opened =
216 DispatchOpenWindowEvent(openerFrameElement, popupFrameElement,
217 aURL, aName, aFeatures);
219 if (opened != BrowserElementParent::OPEN_WINDOW_ADDED) {
220 return opened;
221 }
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 }
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;
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));
252 nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(topWindow);
254 nsCOMPtr<Element> openerFrameElement = win->GetFrameElementInternal();
255 NS_ENSURE_TRUE(openerFrameElement, BrowserElementParent::OPEN_WINDOW_IGNORED);
258 nsRefPtr<HTMLIFrameElement> popupFrameElement =
259 CreateIframe(openerFrameElement, aName, /* aRemote = */ false);
260 NS_ENSURE_TRUE(popupFrameElement, BrowserElementParent::OPEN_WINDOW_IGNORED);
262 nsAutoCString spec;
263 if (aURI) {
264 aURI->GetSpec(spec);
265 }
267 OpenWindowResult opened =
268 DispatchOpenWindowEvent(openerFrameElement, popupFrameElement,
269 NS_ConvertUTF8toUTF16(spec),
270 aName,
271 NS_ConvertUTF8toUTF16(aFeatures));
273 if (opened != BrowserElementParent::OPEN_WINDOW_ADDED) {
274 return opened;
275 }
277 // Return popupFrameElement's window.
278 nsCOMPtr<nsIFrameLoader> frameLoader;
279 popupFrameElement->GetFrameLoader(getter_AddRefs(frameLoader));
280 NS_ENSURE_TRUE(frameLoader, BrowserElementParent::OPEN_WINDOW_IGNORED);
282 nsCOMPtr<nsIDocShell> docshell;
283 frameLoader->GetDocShell(getter_AddRefs(docshell));
284 NS_ENSURE_TRUE(docshell, BrowserElementParent::OPEN_WINDOW_IGNORED);
286 nsCOMPtr<nsIDOMWindow> window = do_GetInterface(docshell);
287 window.forget(aReturnWindow);
289 return !!*aReturnWindow ? opened : BrowserElementParent::OPEN_WINDOW_CANCELLED;
290 }
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 {}
303 NS_IMETHOD Run();
305 private:
306 nsRefPtr<TabParent> mTabParent;
307 const CSSRect mContentRect;
308 const CSSSize mContentSize;
309 };
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);
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;
328 AutoSafeJSContext cx;
329 JS::Rooted<JSObject*> globalJSObject(cx, globalObject->GetGlobalJSObject());
330 NS_ENSURE_TRUE(globalJSObject, NS_ERROR_UNEXPECTED);
332 JSAutoCompartment ac(cx, globalJSObject);
333 JS::Rooted<JS::Value> val(cx);
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 }
340 nsEventStatus status = nsEventStatus_eIgnore;
341 DispatchCustomDOMEvent(frameElement,
342 NS_LITERAL_STRING("mozbrowserasyncscroll"),
343 cx,
344 val, &status);
345 return NS_OK;
346 }
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 }
359 } // namespace mozilla