Line data Source code
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 0 : CreateIframe(Element* aOpenerFrameElement, const nsAString& aName, bool aRemote)
41 : {
42 : nsNodeInfoManager *nodeInfoManager =
43 0 : aOpenerFrameElement->OwnerDoc()->NodeInfoManager();
44 :
45 : RefPtr<NodeInfo> nodeInfo =
46 0 : nodeInfoManager->GetNodeInfo(nsGkAtoms::iframe,
47 : /* aPrefix = */ nullptr,
48 : kNameSpaceID_XHTML,
49 0 : nsIDOMNode::ELEMENT_NODE);
50 :
51 : RefPtr<HTMLIFrameElement> popupFrameElement =
52 : static_cast<HTMLIFrameElement*>(
53 0 : NS_NewHTMLIFrameElement(nodeInfo.forget(), mozilla::dom::NOT_FROM_PARSER));
54 :
55 0 : popupFrameElement->SetMozbrowser(true);
56 :
57 : // Copy the window name onto the iframe.
58 0 : popupFrameElement->SetAttr(kNameSpaceID_None, nsGkAtoms::name,
59 0 : aName, /* aNotify = */ false);
60 :
61 : // 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 :
67 : // 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 : mozprivatebrowsing)) {
71 0 : popupFrameElement->SetAttr(kNameSpaceID_None, nsGkAtoms::mozprivatebrowsing,
72 0 : mozprivatebrowsing, /* aNotify = */ false);
73 : }
74 :
75 0 : return popupFrameElement.forget();
76 : }
77 :
78 : bool
79 0 : DispatchCustomDOMEvent(Element* aFrameElement, const nsAString& aEventName,
80 : JSContext* cx, JS::Handle<JS::Value> aDetailValue,
81 : nsEventStatus *aStatus)
82 : {
83 0 : NS_ENSURE_TRUE(aFrameElement, false);
84 0 : nsIPresShell *shell = aFrameElement->OwnerDoc()->GetShell();
85 0 : RefPtr<nsPresContext> presContext;
86 0 : if (shell) {
87 0 : presContext = shell->GetPresContext();
88 : }
89 :
90 : RefPtr<CustomEvent> event =
91 0 : NS_NewDOMCustomEvent(aFrameElement, presContext, nullptr);
92 :
93 0 : ErrorResult res;
94 0 : event->InitCustomEvent(cx,
95 : aEventName,
96 : /* aCanBubble = */ true,
97 : /* aCancelable = */ true,
98 : aDetailValue,
99 0 : res);
100 0 : if (res.Failed()) {
101 0 : return false;
102 : }
103 0 : event->SetTrusted(true);
104 : // Dispatch the event.
105 : // We don't initialize aStatus here, as our callers have already done so.
106 : nsresult rv =
107 0 : EventDispatcher::DispatchDOMEvent(aFrameElement, nullptr, event,
108 0 : presContext, aStatus);
109 0 : return NS_SUCCEEDED(rv);
110 : }
111 :
112 : } // namespace
113 :
114 : namespace mozilla {
115 :
116 : /**
117 : * Dispatch a mozbrowseropenwindow event to the given opener frame element.
118 : * The "popup iframe" (event.detail.frameElement) will be |aPopupFrameElement|.
119 : *
120 : * Returns true iff there were no unexpected failures and the window.open call
121 : * was accepted by the embedder.
122 : */
123 : /*static*/
124 : BrowserElementParent::OpenWindowResult
125 0 : BrowserElementParent::DispatchOpenWindowEvent(Element* aOpenerFrameElement,
126 : Element* aPopupFrameElement,
127 : const nsAString& aURL,
128 : const nsAString& aName,
129 : const nsAString& aFeatures)
130 : {
131 : // Dispatch a CustomEvent at aOpenerFrameElement with a detail object
132 : // (OpenWindowEventDetail) containing aPopupFrameElement, aURL, aName, and
133 : // aFeatures.
134 :
135 : // Create the event's detail object.
136 0 : OpenWindowEventDetail detail;
137 0 : if (aURL.IsEmpty()) {
138 : // URL should never be empty. Assign about:blank as default.
139 0 : detail.mUrl = NS_LITERAL_STRING("about:blank");
140 : } else {
141 0 : detail.mUrl = aURL;
142 : }
143 0 : detail.mName = aName;
144 0 : detail.mFeatures = aFeatures;
145 0 : detail.mFrameElement = aPopupFrameElement;
146 :
147 0 : AutoJSContext cx;
148 0 : JS::Rooted<JS::Value> val(cx);
149 :
150 0 : nsIGlobalObject* sgo = aPopupFrameElement->OwnerDoc()->GetScopeObject();
151 0 : if (!sgo) {
152 0 : return BrowserElementParent::OPEN_WINDOW_IGNORED;
153 : }
154 :
155 0 : JS::Rooted<JSObject*> global(cx, sgo->GetGlobalJSObject());
156 0 : JSAutoCompartment ac(cx, global);
157 0 : if (!ToJSValue(cx, detail, &val)) {
158 0 : MOZ_CRASH("Failed to convert dictionary to JS::Value due to OOM.");
159 : return BrowserElementParent::OPEN_WINDOW_IGNORED;
160 : }
161 :
162 0 : nsEventStatus status = nsEventStatus_eIgnore;
163 : bool dispatchSucceeded =
164 0 : DispatchCustomDOMEvent(aOpenerFrameElement,
165 0 : NS_LITERAL_STRING("mozbrowseropenwindow"),
166 : cx,
167 0 : val, &status);
168 :
169 0 : if (dispatchSucceeded) {
170 0 : if (aPopupFrameElement->IsInUncomposedDoc()) {
171 0 : return BrowserElementParent::OPEN_WINDOW_ADDED;
172 : }
173 0 : if (status == nsEventStatus_eConsumeNoDefault) {
174 : // If the frame was not added to a document, report to callers whether
175 : // preventDefault was called on or not
176 0 : return BrowserElementParent::OPEN_WINDOW_CANCELLED;
177 : }
178 : }
179 :
180 0 : return BrowserElementParent::OPEN_WINDOW_IGNORED;
181 : }
182 :
183 : /*static*/
184 : BrowserElementParent::OpenWindowResult
185 0 : BrowserElementParent::OpenWindowOOP(TabParent* aOpenerTabParent,
186 : TabParent* aPopupTabParent,
187 : PRenderFrameParent* aRenderFrame,
188 : const nsAString& aURL,
189 : const nsAString& aName,
190 : const nsAString& aFeatures,
191 : TextureFactoryIdentifier* aTextureFactoryIdentifier,
192 : uint64_t* aLayersId)
193 : {
194 : // Create an iframe owned by the same document which owns openerFrameElement.
195 0 : nsCOMPtr<Element> openerFrameElement = aOpenerTabParent->GetOwnerElement();
196 0 : NS_ENSURE_TRUE(openerFrameElement,
197 : BrowserElementParent::OPEN_WINDOW_IGNORED);
198 : RefPtr<HTMLIFrameElement> popupFrameElement =
199 0 : CreateIframe(openerFrameElement, aName, /* aRemote = */ true);
200 :
201 : // Normally an <iframe> element will try to create a frameLoader when the
202 : // page touches iframe.contentWindow or sets iframe.src.
203 : //
204 : // But in our case, we want to delay the creation of the frameLoader until
205 : // we've verified that the popup has gone through successfully. If the popup
206 : // is "blocked" by the embedder, we don't want to load the popup's url.
207 : //
208 : // Therefore we call DisallowCreateFrameLoader() on the element and call
209 : // AllowCreateFrameLoader() only after we've verified that the popup was
210 : // allowed.
211 0 : popupFrameElement->DisallowCreateFrameLoader();
212 :
213 : OpenWindowResult opened =
214 0 : DispatchOpenWindowEvent(openerFrameElement, popupFrameElement,
215 0 : aURL, aName, aFeatures);
216 :
217 0 : if (opened != BrowserElementParent::OPEN_WINDOW_ADDED) {
218 0 : return opened;
219 : }
220 :
221 : // The popup was not blocked, so hook up the frame element and the popup tab
222 : // parent, and return success.
223 0 : aPopupTabParent->SetOwnerElement(popupFrameElement);
224 0 : popupFrameElement->AllowCreateFrameLoader();
225 0 : popupFrameElement->CreateRemoteFrameLoader(aPopupTabParent);
226 :
227 0 : RenderFrameParent* rfp = static_cast<RenderFrameParent*>(aRenderFrame);
228 0 : if (!aPopupTabParent->SetRenderFrame(rfp) ||
229 0 : !aPopupTabParent->GetRenderFrameInfo(aTextureFactoryIdentifier, aLayersId)) {
230 0 : return BrowserElementParent::OPEN_WINDOW_IGNORED;
231 : }
232 :
233 0 : return opened;
234 : }
235 :
236 : /* static */
237 : BrowserElementParent::OpenWindowResult
238 0 : BrowserElementParent::OpenWindowInProcess(nsPIDOMWindowOuter* aOpenerWindow,
239 : nsIURI* aURI,
240 : const nsAString& aName,
241 : const nsACString& aFeatures,
242 : bool aForceNoOpener,
243 : mozIDOMWindowProxy** aReturnWindow)
244 : {
245 0 : *aReturnWindow = nullptr;
246 :
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.)
251 : //
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 0 : nsCOMPtr<nsPIDOMWindowOuter> win = aOpenerWindow->GetScriptableTop();
256 :
257 0 : nsCOMPtr<Element> openerFrameElement = win->GetFrameElementInternal();
258 0 : NS_ENSURE_TRUE(openerFrameElement, BrowserElementParent::OPEN_WINDOW_IGNORED);
259 :
260 :
261 : RefPtr<HTMLIFrameElement> popupFrameElement =
262 0 : CreateIframe(openerFrameElement, aName, /* aRemote = */ false);
263 0 : NS_ENSURE_TRUE(popupFrameElement, BrowserElementParent::OPEN_WINDOW_IGNORED);
264 :
265 0 : nsAutoCString spec;
266 0 : if (aURI) {
267 0 : aURI->GetSpec(spec);
268 : }
269 :
270 0 : if (!aForceNoOpener) {
271 0 : ErrorResult res;
272 0 : popupFrameElement->PresetOpenerWindow(aOpenerWindow, res);
273 0 : MOZ_ASSERT(!res.Failed());
274 : }
275 :
276 : OpenWindowResult opened =
277 0 : DispatchOpenWindowEvent(openerFrameElement, popupFrameElement,
278 0 : NS_ConvertUTF8toUTF16(spec),
279 : aName,
280 0 : NS_ConvertUTF8toUTF16(aFeatures));
281 :
282 0 : if (opened != BrowserElementParent::OPEN_WINDOW_ADDED) {
283 0 : return opened;
284 : }
285 :
286 : // Return popupFrameElement's window.
287 0 : RefPtr<nsFrameLoader> frameLoader = popupFrameElement->GetFrameLoader();
288 0 : NS_ENSURE_TRUE(frameLoader, BrowserElementParent::OPEN_WINDOW_IGNORED);
289 :
290 0 : nsCOMPtr<nsIDocShell> docshell;
291 0 : frameLoader->GetDocShell(getter_AddRefs(docshell));
292 0 : NS_ENSURE_TRUE(docshell, BrowserElementParent::OPEN_WINDOW_IGNORED);
293 :
294 0 : nsCOMPtr<nsPIDOMWindowOuter> window = docshell->GetWindow();
295 0 : window.forget(aReturnWindow);
296 :
297 0 : return !!*aReturnWindow ? opened : BrowserElementParent::OPEN_WINDOW_CANCELLED;
298 : }
299 :
300 : } // namespace mozilla
|