/src/mozilla-central/dom/browser-element/BrowserElementParent.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
2 | | /* vim: set ts=8 sts=2 et sw=2 tw=80: */ |
3 | | /* This Source Code Form is subject to the terms of the Mozilla Public |
4 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
5 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
6 | | |
7 | | #include "TabParent.h" |
8 | | |
9 | | // TabParent.h transitively includes <windows.h>, which does |
10 | | // #define CreateEvent CreateEventW |
11 | | // That messes up our call to EventDispatcher::CreateEvent below. |
12 | | |
13 | | #ifdef CreateEvent |
14 | | #undef CreateEvent |
15 | | #endif |
16 | | |
17 | | #include "BrowserElementParent.h" |
18 | | #include "mozilla/EventDispatcher.h" |
19 | | #include "mozilla/dom/HTMLIFrameElement.h" |
20 | | #include "mozilla/dom/ToJSValue.h" |
21 | | #include "nsIInterfaceRequestorUtils.h" |
22 | | #include "nsVariant.h" |
23 | | #include "mozilla/dom/BrowserElementDictionariesBinding.h" |
24 | | #include "mozilla/dom/CustomEvent.h" |
25 | | #include "mozilla/layout/RenderFrameParent.h" |
26 | | |
27 | | using namespace mozilla; |
28 | | using namespace mozilla::dom; |
29 | | using namespace mozilla::layers; |
30 | | using namespace mozilla::layout; |
31 | | |
32 | | namespace { |
33 | | |
34 | | using mozilla::BrowserElementParent; |
35 | | /** |
36 | | * Create an <iframe mozbrowser> owned by the same document as |
37 | | * aOpenerFrameElement. |
38 | | */ |
39 | | already_AddRefed<HTMLIFrameElement> |
40 | | CreateIframe(Element* aOpenerFrameElement, const nsAString& aName, bool aRemote) |
41 | 0 | { |
42 | 0 | nsNodeInfoManager *nodeInfoManager = |
43 | 0 | aOpenerFrameElement->OwnerDoc()->NodeInfoManager(); |
44 | 0 |
|
45 | 0 | RefPtr<NodeInfo> nodeInfo = |
46 | 0 | nodeInfoManager->GetNodeInfo(nsGkAtoms::iframe, |
47 | 0 | /* aPrefix = */ nullptr, |
48 | 0 | kNameSpaceID_XHTML, |
49 | 0 | nsINode::ELEMENT_NODE); |
50 | 0 |
|
51 | 0 | RefPtr<HTMLIFrameElement> popupFrameElement = |
52 | 0 | static_cast<HTMLIFrameElement*>( |
53 | 0 | NS_NewHTMLIFrameElement(nodeInfo.forget(), mozilla::dom::NOT_FROM_PARSER)); |
54 | 0 |
|
55 | 0 | popupFrameElement->SetMozbrowser(true); |
56 | 0 |
|
57 | 0 | // Copy the window name onto the iframe. |
58 | 0 | popupFrameElement->SetAttr(kNameSpaceID_None, nsGkAtoms::name, |
59 | 0 | aName, /* aNotify = */ false); |
60 | 0 |
|
61 | 0 | // Indicate whether the iframe is should be remote. |
62 | 0 | popupFrameElement->SetAttr(kNameSpaceID_None, nsGkAtoms::remote, |
63 | 0 | aRemote ? NS_LITERAL_STRING("true") : |
64 | 0 | NS_LITERAL_STRING("false"), |
65 | 0 | /* aNotify = */ false); |
66 | 0 |
|
67 | 0 | // Copy the opener frame's mozprivatebrowsing attribute to the popup frame. |
68 | 0 | nsAutoString mozprivatebrowsing; |
69 | 0 | if (aOpenerFrameElement->GetAttr(kNameSpaceID_None, nsGkAtoms::mozprivatebrowsing, |
70 | 0 | mozprivatebrowsing)) { |
71 | 0 | popupFrameElement->SetAttr(kNameSpaceID_None, nsGkAtoms::mozprivatebrowsing, |
72 | 0 | mozprivatebrowsing, /* aNotify = */ false); |
73 | 0 | } |
74 | 0 |
|
75 | 0 | return popupFrameElement.forget(); |
76 | 0 | } |
77 | | |
78 | | bool |
79 | | DispatchCustomDOMEvent(Element* aFrameElement, const nsAString& aEventName, |
80 | | JSContext* cx, JS::Handle<JS::Value> aDetailValue, |
81 | | nsEventStatus *aStatus) |
82 | 0 | { |
83 | 0 | NS_ENSURE_TRUE(aFrameElement, false); |
84 | 0 | RefPtr<nsPresContext> presContext = |
85 | 0 | aFrameElement->OwnerDoc()->GetPresContext(); |
86 | 0 |
|
87 | 0 | RefPtr<CustomEvent> event = |
88 | 0 | NS_NewDOMCustomEvent(aFrameElement, presContext, nullptr); |
89 | 0 |
|
90 | 0 | event->InitCustomEvent(cx, |
91 | 0 | aEventName, |
92 | 0 | /* aCanBubble = */ true, |
93 | 0 | /* aCancelable = */ true, |
94 | 0 | aDetailValue); |
95 | 0 |
|
96 | 0 | event->SetTrusted(true); |
97 | 0 | // Dispatch the event. |
98 | 0 | // We don't initialize aStatus here, as our callers have already done so. |
99 | 0 | nsresult rv = |
100 | 0 | EventDispatcher::DispatchDOMEvent(aFrameElement, nullptr, event, |
101 | 0 | presContext, aStatus); |
102 | 0 | return NS_SUCCEEDED(rv); |
103 | 0 | } |
104 | | |
105 | | } // namespace |
106 | | |
107 | | namespace mozilla { |
108 | | |
109 | | /** |
110 | | * Dispatch a mozbrowseropenwindow event to the given opener frame element. |
111 | | * The "popup iframe" (event.detail.frameElement) will be |aPopupFrameElement|. |
112 | | * |
113 | | * Returns true iff there were no unexpected failures and the window.open call |
114 | | * was accepted by the embedder. |
115 | | */ |
116 | | /*static*/ |
117 | | BrowserElementParent::OpenWindowResult |
118 | | BrowserElementParent::DispatchOpenWindowEvent(Element* aOpenerFrameElement, |
119 | | Element* aPopupFrameElement, |
120 | | const nsAString& aURL, |
121 | | const nsAString& aName, |
122 | | const nsAString& aFeatures) |
123 | 0 | { |
124 | 0 | // Dispatch a CustomEvent at aOpenerFrameElement with a detail object |
125 | 0 | // (OpenWindowEventDetail) containing aPopupFrameElement, aURL, aName, and |
126 | 0 | // aFeatures. |
127 | 0 |
|
128 | 0 | // Create the event's detail object. |
129 | 0 | OpenWindowEventDetail detail; |
130 | 0 | if (aURL.IsEmpty()) { |
131 | 0 | // URL should never be empty. Assign about:blank as default. |
132 | 0 | detail.mUrl = NS_LITERAL_STRING("about:blank"); |
133 | 0 | } else { |
134 | 0 | detail.mUrl = aURL; |
135 | 0 | } |
136 | 0 | detail.mName = aName; |
137 | 0 | detail.mFeatures = aFeatures; |
138 | 0 | detail.mFrameElement = aPopupFrameElement; |
139 | 0 |
|
140 | 0 | AutoJSContext cx; |
141 | 0 | JS::Rooted<JS::Value> val(cx); |
142 | 0 |
|
143 | 0 | nsIGlobalObject* sgo = aPopupFrameElement->OwnerDoc()->GetScopeObject(); |
144 | 0 | if (!sgo) { |
145 | 0 | return BrowserElementParent::OPEN_WINDOW_IGNORED; |
146 | 0 | } |
147 | 0 | |
148 | 0 | JS::Rooted<JSObject*> global(cx, sgo->GetGlobalJSObject()); |
149 | 0 | JSAutoRealm ar(cx, global); |
150 | 0 | if (!ToJSValue(cx, detail, &val)) { |
151 | 0 | MOZ_CRASH("Failed to convert dictionary to JS::Value due to OOM."); |
152 | 0 | return BrowserElementParent::OPEN_WINDOW_IGNORED; |
153 | 0 | } |
154 | 0 | |
155 | 0 | nsEventStatus status = nsEventStatus_eIgnore; |
156 | 0 | bool dispatchSucceeded = |
157 | 0 | DispatchCustomDOMEvent(aOpenerFrameElement, |
158 | 0 | NS_LITERAL_STRING("mozbrowseropenwindow"), |
159 | 0 | cx, |
160 | 0 | val, &status); |
161 | 0 |
|
162 | 0 | if (dispatchSucceeded) { |
163 | 0 | if (aPopupFrameElement->IsInComposedDoc()) { |
164 | 0 | return BrowserElementParent::OPEN_WINDOW_ADDED; |
165 | 0 | } |
166 | 0 | if (status == nsEventStatus_eConsumeNoDefault) { |
167 | 0 | // If the frame was not added to a document, report to callers whether |
168 | 0 | // preventDefault was called on or not |
169 | 0 | return BrowserElementParent::OPEN_WINDOW_CANCELLED; |
170 | 0 | } |
171 | 0 | } |
172 | 0 | |
173 | 0 | return BrowserElementParent::OPEN_WINDOW_IGNORED; |
174 | 0 | } |
175 | | |
176 | | /*static*/ |
177 | | BrowserElementParent::OpenWindowResult |
178 | | BrowserElementParent::OpenWindowOOP(TabParent* aOpenerTabParent, |
179 | | TabParent* aPopupTabParent, |
180 | | PRenderFrameParent* aRenderFrame, |
181 | | const nsAString& aURL, |
182 | | const nsAString& aName, |
183 | | const nsAString& aFeatures, |
184 | | TextureFactoryIdentifier* aTextureFactoryIdentifier, |
185 | | layers::LayersId* aLayersId) |
186 | 0 | { |
187 | 0 | // Create an iframe owned by the same document which owns openerFrameElement. |
188 | 0 | nsCOMPtr<Element> openerFrameElement = aOpenerTabParent->GetOwnerElement(); |
189 | 0 | NS_ENSURE_TRUE(openerFrameElement, |
190 | 0 | BrowserElementParent::OPEN_WINDOW_IGNORED); |
191 | 0 | RefPtr<HTMLIFrameElement> popupFrameElement = |
192 | 0 | CreateIframe(openerFrameElement, aName, /* aRemote = */ true); |
193 | 0 |
|
194 | 0 | // Normally an <iframe> element will try to create a frameLoader when the |
195 | 0 | // page touches iframe.contentWindow or sets iframe.src. |
196 | 0 | // |
197 | 0 | // But in our case, we want to delay the creation of the frameLoader until |
198 | 0 | // we've verified that the popup has gone through successfully. If the popup |
199 | 0 | // is "blocked" by the embedder, we don't want to load the popup's url. |
200 | 0 | // |
201 | 0 | // Therefore we call DisallowCreateFrameLoader() on the element and call |
202 | 0 | // AllowCreateFrameLoader() only after we've verified that the popup was |
203 | 0 | // allowed. |
204 | 0 | popupFrameElement->DisallowCreateFrameLoader(); |
205 | 0 |
|
206 | 0 | OpenWindowResult opened = |
207 | 0 | DispatchOpenWindowEvent(openerFrameElement, popupFrameElement, |
208 | 0 | aURL, aName, aFeatures); |
209 | 0 |
|
210 | 0 | if (opened != BrowserElementParent::OPEN_WINDOW_ADDED) { |
211 | 0 | return opened; |
212 | 0 | } |
213 | 0 | |
214 | 0 | // The popup was not blocked, so hook up the frame element and the popup tab |
215 | 0 | // parent, and return success. |
216 | 0 | aPopupTabParent->SetOwnerElement(popupFrameElement); |
217 | 0 | popupFrameElement->AllowCreateFrameLoader(); |
218 | 0 | popupFrameElement->CreateRemoteFrameLoader(aPopupTabParent); |
219 | 0 |
|
220 | 0 | RenderFrameParent* rfp = static_cast<RenderFrameParent*>(aRenderFrame); |
221 | 0 | if (!aPopupTabParent->SetRenderFrame(rfp) || |
222 | 0 | !aPopupTabParent->GetRenderFrameInfo(aTextureFactoryIdentifier, aLayersId)) { |
223 | 0 | return BrowserElementParent::OPEN_WINDOW_IGNORED; |
224 | 0 | } |
225 | 0 | |
226 | 0 | return opened; |
227 | 0 | } |
228 | | |
229 | | /* static */ |
230 | | BrowserElementParent::OpenWindowResult |
231 | | BrowserElementParent::OpenWindowInProcess(nsPIDOMWindowOuter* aOpenerWindow, |
232 | | nsIURI* aURI, |
233 | | const nsAString& aName, |
234 | | const nsACString& aFeatures, |
235 | | bool aForceNoOpener, |
236 | | mozIDOMWindowProxy** aReturnWindow) |
237 | 0 | { |
238 | 0 | *aReturnWindow = nullptr; |
239 | 0 |
|
240 | 0 | // If we call window.open from an <iframe> inside an <iframe mozbrowser>, |
241 | 0 | // it's as though the top-level document inside the <iframe mozbrowser> |
242 | 0 | // called window.open. (Indeed, in the OOP case, the inner <iframe> lives |
243 | 0 | // out-of-process, so we couldn't touch it if we tried.) |
244 | 0 | // |
245 | 0 | // GetScriptableTop gets us the <iframe mozbrowser>'s window; we'll use its |
246 | 0 | // frame element, rather than aOpenerWindow's frame element, as our "opener |
247 | 0 | // frame element" below. |
248 | 0 | nsCOMPtr<nsPIDOMWindowOuter> win = aOpenerWindow->GetScriptableTop(); |
249 | 0 |
|
250 | 0 | nsCOMPtr<Element> openerFrameElement = win->GetFrameElementInternal(); |
251 | 0 | NS_ENSURE_TRUE(openerFrameElement, BrowserElementParent::OPEN_WINDOW_IGNORED); |
252 | 0 |
|
253 | 0 |
|
254 | 0 | RefPtr<HTMLIFrameElement> popupFrameElement = |
255 | 0 | CreateIframe(openerFrameElement, aName, /* aRemote = */ false); |
256 | 0 | NS_ENSURE_TRUE(popupFrameElement, BrowserElementParent::OPEN_WINDOW_IGNORED); |
257 | 0 |
|
258 | 0 | nsAutoCString spec; |
259 | 0 | if (aURI) { |
260 | 0 | aURI->GetSpec(spec); |
261 | 0 | } |
262 | 0 |
|
263 | 0 | if (!aForceNoOpener) { |
264 | 0 | ErrorResult res; |
265 | 0 | popupFrameElement->PresetOpenerWindow(aOpenerWindow, res); |
266 | 0 | MOZ_ASSERT(!res.Failed()); |
267 | 0 | } |
268 | 0 |
|
269 | 0 | OpenWindowResult opened = |
270 | 0 | DispatchOpenWindowEvent(openerFrameElement, popupFrameElement, |
271 | 0 | NS_ConvertUTF8toUTF16(spec), |
272 | 0 | aName, |
273 | 0 | NS_ConvertUTF8toUTF16(aFeatures)); |
274 | 0 |
|
275 | 0 | if (opened != BrowserElementParent::OPEN_WINDOW_ADDED) { |
276 | 0 | return opened; |
277 | 0 | } |
278 | 0 | |
279 | 0 | // Return popupFrameElement's window. |
280 | 0 | RefPtr<nsFrameLoader> frameLoader = popupFrameElement->GetFrameLoader(); |
281 | 0 | NS_ENSURE_TRUE(frameLoader, BrowserElementParent::OPEN_WINDOW_IGNORED); |
282 | 0 |
|
283 | 0 | nsCOMPtr<nsIDocShell> docshell = frameLoader->GetDocShell(IgnoreErrors()); |
284 | 0 | NS_ENSURE_TRUE(docshell, BrowserElementParent::OPEN_WINDOW_IGNORED); |
285 | 0 |
|
286 | 0 | nsCOMPtr<nsPIDOMWindowOuter> window = docshell->GetWindow(); |
287 | 0 | window.forget(aReturnWindow); |
288 | 0 |
|
289 | 0 | return !!*aReturnWindow ? opened : BrowserElementParent::OPEN_WINDOW_CANCELLED; |
290 | 0 | } |
291 | | |
292 | | } // namespace mozilla |