/src/mozilla-central/dom/base/nsFrameLoader.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 | | /* |
8 | | * Class for managing loading of a subframe (creation of the docshell, |
9 | | * handling of loads in it, recursion-checking). |
10 | | */ |
11 | | |
12 | | #include "base/basictypes.h" |
13 | | |
14 | | #include "prenv.h" |
15 | | |
16 | | #include "nsDocShell.h" |
17 | | #include "nsIDOMMozBrowserFrame.h" |
18 | | #include "nsIDOMWindow.h" |
19 | | #include "nsIPresShell.h" |
20 | | #include "nsIContentInlines.h" |
21 | | #include "nsIContentViewer.h" |
22 | | #include "nsIDocument.h" |
23 | | #include "nsPIDOMWindow.h" |
24 | | #include "nsIWebNavigation.h" |
25 | | #include "nsIWebProgress.h" |
26 | | #include "nsIDocShell.h" |
27 | | #include "nsIDocShellTreeOwner.h" |
28 | | #include "nsDocShellLoadInfo.h" |
29 | | #include "nsIBaseWindow.h" |
30 | | #include "nsIBrowser.h" |
31 | | #include "nsContentUtils.h" |
32 | | #include "nsIXPConnect.h" |
33 | | #include "nsUnicharUtils.h" |
34 | | #include "nsIScriptGlobalObject.h" |
35 | | #include "nsIScriptSecurityManager.h" |
36 | | #include "nsIScrollable.h" |
37 | | #include "nsFrameLoader.h" |
38 | | #include "nsIFrame.h" |
39 | | #include "nsIScrollableFrame.h" |
40 | | #include "nsSubDocumentFrame.h" |
41 | | #include "nsError.h" |
42 | | #include "nsISHistory.h" |
43 | | #include "nsIXULWindow.h" |
44 | | #include "nsIMozBrowserFrame.h" |
45 | | #include "nsISHistory.h" |
46 | | #include "nsIScriptError.h" |
47 | | #include "nsGlobalWindow.h" |
48 | | #include "nsHTMLDocument.h" |
49 | | #include "nsPIWindowRoot.h" |
50 | | #include "nsLayoutUtils.h" |
51 | | #include "nsMappedAttributes.h" |
52 | | #include "nsView.h" |
53 | | #include "nsBaseWidget.h" |
54 | | #include "nsQueryObject.h" |
55 | | |
56 | | #include "nsIURI.h" |
57 | | #include "nsIURL.h" |
58 | | #include "nsNetUtil.h" |
59 | | |
60 | | #include "nsGkAtoms.h" |
61 | | #include "nsNameSpaceManager.h" |
62 | | |
63 | | #include "nsThreadUtils.h" |
64 | | |
65 | | #include "nsIDOMChromeWindow.h" |
66 | | #include "InProcessTabChildMessageManager.h" |
67 | | |
68 | | #include "Layers.h" |
69 | | #include "ClientLayerManager.h" |
70 | | |
71 | | #include "ContentParent.h" |
72 | | #include "TabParent.h" |
73 | | #include "mozilla/AsyncEventDispatcher.h" |
74 | | #include "mozilla/BasePrincipal.h" |
75 | | #include "mozilla/GuardObjects.h" |
76 | | #include "mozilla/HTMLEditor.h" |
77 | | #include "mozilla/NullPrincipal.h" |
78 | | #include "mozilla/Preferences.h" |
79 | | #include "mozilla/Unused.h" |
80 | | #include "mozilla/dom/ChromeMessageSender.h" |
81 | | #include "mozilla/dom/Element.h" |
82 | | #include "mozilla/dom/FrameLoaderBinding.h" |
83 | | #include "mozilla/jsipc/CrossProcessObjectWrappers.h" |
84 | | #include "mozilla/layout/RenderFrameParent.h" |
85 | | #include "nsGenericHTMLFrameElement.h" |
86 | | #include "GeckoProfiler.h" |
87 | | |
88 | | #include "jsapi.h" |
89 | | #include "mozilla/dom/HTMLIFrameElement.h" |
90 | | #include "nsSandboxFlags.h" |
91 | | #include "mozilla/layers/CompositorBridgeChild.h" |
92 | | #include "mozilla/dom/CustomEvent.h" |
93 | | |
94 | | #include "mozilla/dom/ipc/StructuredCloneData.h" |
95 | | #include "mozilla/WebBrowserPersistLocalDocument.h" |
96 | | #include "mozilla/dom/Promise.h" |
97 | | #include "mozilla/dom/PromiseNativeHandler.h" |
98 | | #include "mozilla/dom/GroupedHistoryEvent.h" |
99 | | #include "mozilla/dom/ParentSHistory.h" |
100 | | #include "mozilla/dom/ChildSHistory.h" |
101 | | |
102 | | #include "mozilla/dom/HTMLBodyElement.h" |
103 | | |
104 | | #include "mozilla/ContentPrincipal.h" |
105 | | |
106 | | #ifdef XP_WIN |
107 | | #include "mozilla/plugins/PPluginWidgetParent.h" |
108 | | #include "../plugins/ipc/PluginWidgetParent.h" |
109 | | #endif |
110 | | |
111 | | #ifdef MOZ_XUL |
112 | | #include "nsXULPopupManager.h" |
113 | | #endif |
114 | | |
115 | | #ifdef NS_PRINTING |
116 | | #include "mozilla/embedding/printingui/PrintingParent.h" |
117 | | #include "nsIWebBrowserPrint.h" |
118 | | #endif |
119 | | |
120 | | using namespace mozilla; |
121 | | using namespace mozilla::hal; |
122 | | using namespace mozilla::dom; |
123 | | using namespace mozilla::dom::ipc; |
124 | | using namespace mozilla::layers; |
125 | | using namespace mozilla::layout; |
126 | | typedef FrameMetrics::ViewID ViewID; |
127 | | |
128 | | // Bug 136580: Limit to the number of nested content frames that can have the |
129 | | // same URL. This is to stop content that is recursively loading |
130 | | // itself. Note that "#foo" on the end of URL doesn't affect |
131 | | // whether it's considered identical, but "?foo" or ";foo" are |
132 | | // considered and compared. |
133 | | // Bug 228829: Limit this to 1, like IE does. |
134 | 0 | #define MAX_SAME_URL_CONTENT_FRAMES 1 |
135 | | |
136 | | // Bug 8065: Limit content frame depth to some reasonable level. This |
137 | | // does not count chrome frames when determining depth, nor does it |
138 | | // prevent chrome recursion. Number is fairly arbitrary, but meant to |
139 | | // keep number of shells to a reasonable number on accidental recursion with a |
140 | | // small (but not 1) branching factor. With large branching factors the number |
141 | | // of shells can rapidly become huge and run us out of memory. To solve that, |
142 | | // we'd need to re-institute a fixed version of bug 98158. |
143 | 0 | #define MAX_DEPTH_CONTENT_FRAMES 10 |
144 | | |
145 | | NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(nsFrameLoader, |
146 | | mDocShell, |
147 | | mMessageManager, |
148 | | mChildMessageManager, |
149 | | mOpener, |
150 | | mParentSHistory) |
151 | | NS_IMPL_CYCLE_COLLECTING_ADDREF(nsFrameLoader) |
152 | | NS_IMPL_CYCLE_COLLECTING_RELEASE(nsFrameLoader) |
153 | | |
154 | 0 | NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsFrameLoader) |
155 | 0 | NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY |
156 | 0 | NS_INTERFACE_MAP_ENTRY_CONCRETE(nsFrameLoader) |
157 | 0 | NS_INTERFACE_MAP_ENTRY(nsIMutationObserver) |
158 | 0 | NS_INTERFACE_MAP_ENTRY(nsISupports) |
159 | 0 | NS_INTERFACE_MAP_END |
160 | | |
161 | | nsFrameLoader::nsFrameLoader(Element* aOwner, nsPIDOMWindowOuter* aOpener, |
162 | | bool aNetworkCreated, int32_t aJSPluginID) |
163 | | : mOwnerContent(aOwner) |
164 | | , mDetachedSubdocFrame(nullptr) |
165 | | , mOpener(aOpener) |
166 | | , mRemoteBrowser(nullptr) |
167 | | , mChildID(0) |
168 | | , mJSPluginID(aJSPluginID) |
169 | | , mBrowserChangingProcessBlockers(nullptr) |
170 | | , mDepthTooGreat(false) |
171 | | , mIsTopLevelContent(false) |
172 | | , mDestroyCalled(false) |
173 | | , mNeedsAsyncDestroy(false) |
174 | | , mInSwap(false) |
175 | | , mInShow(false) |
176 | | , mHideCalled(false) |
177 | | , mNetworkCreated(aNetworkCreated) |
178 | | , mLoadingOriginalSrc(false) |
179 | | , mRemoteBrowserShown(false) |
180 | | , mRemoteFrame(false) |
181 | | , mClipSubdocument(true) |
182 | | , mClampScrollPosition(true) |
183 | | , mObservingOwnerContent(false) |
184 | 0 | { |
185 | 0 | mRemoteFrame = ShouldUseRemoteProcess(); |
186 | 0 | MOZ_ASSERT(!mRemoteFrame || !aOpener, |
187 | 0 | "Cannot pass aOpener for a remote frame!"); |
188 | 0 | } |
189 | | |
190 | | nsFrameLoader::~nsFrameLoader() |
191 | 0 | { |
192 | 0 | if (mMessageManager) { |
193 | 0 | mMessageManager->Disconnect(); |
194 | 0 | } |
195 | 0 | MOZ_RELEASE_ASSERT(mDestroyCalled); |
196 | 0 | } |
197 | | |
198 | | nsFrameLoader* |
199 | | nsFrameLoader::Create(Element* aOwner, nsPIDOMWindowOuter* aOpener, bool aNetworkCreated, |
200 | | int32_t aJSPluginId) |
201 | 0 | { |
202 | 0 | NS_ENSURE_TRUE(aOwner, nullptr); |
203 | 0 | nsIDocument* doc = aOwner->OwnerDoc(); |
204 | 0 |
|
205 | 0 | // We never create nsFrameLoaders for elements in resource documents. |
206 | 0 | // |
207 | 0 | // We never create nsFrameLoaders for elements in data documents, unless the |
208 | 0 | // document is a static document. |
209 | 0 | // Static documents are an exception because any sub-documents need an |
210 | 0 | // nsFrameLoader to keep the relevant docShell alive, even though the |
211 | 0 | // nsFrameLoader isn't used to load anything (the sub-document is created by |
212 | 0 | // the static clone process). |
213 | 0 | // |
214 | 0 | // We never create nsFrameLoaders for elements that are not |
215 | 0 | // in-composed-document, unless the element belongs to a static document. |
216 | 0 | // Static documents are an exception because this method is called at a point |
217 | 0 | // in the static clone process before aOwner has been inserted into its |
218 | 0 | // document. For other types of documents this wouldn't be a problem since |
219 | 0 | // we'd create the nsFrameLoader as necessary after aOwner is inserted into a |
220 | 0 | // document, but the mechanisms that take care of that don't apply for static |
221 | 0 | // documents so we need to create the nsFrameLoader now. (This isn't wasteful |
222 | 0 | // since for a static document we know aOwner will end up in a document and |
223 | 0 | // the nsFrameLoader will be used for its docShell.) |
224 | 0 | // |
225 | 0 | NS_ENSURE_TRUE(!doc->IsResourceDoc() && |
226 | 0 | ((!doc->IsLoadedAsData() && aOwner->IsInComposedDoc()) || |
227 | 0 | doc->IsStaticDocument()), |
228 | 0 | nullptr); |
229 | 0 |
|
230 | 0 | return new nsFrameLoader(aOwner, aOpener, aNetworkCreated, aJSPluginId); |
231 | 0 | } |
232 | | |
233 | | void |
234 | | nsFrameLoader::LoadFrame(bool aOriginalSrc) |
235 | 0 | { |
236 | 0 | if (NS_WARN_IF(!mOwnerContent)) { |
237 | 0 | return; |
238 | 0 | } |
239 | 0 | |
240 | 0 | nsAutoString src; |
241 | 0 | nsCOMPtr<nsIPrincipal> principal; |
242 | 0 |
|
243 | 0 | bool isSrcdoc = mOwnerContent->IsHTMLElement(nsGkAtoms::iframe) && |
244 | 0 | mOwnerContent->HasAttr(kNameSpaceID_None, nsGkAtoms::srcdoc); |
245 | 0 | if (isSrcdoc) { |
246 | 0 | src.AssignLiteral("about:srcdoc"); |
247 | 0 | } |
248 | 0 | else { |
249 | 0 | GetURL(src, getter_AddRefs(principal)); |
250 | 0 |
|
251 | 0 | src.Trim(" \t\n\r"); |
252 | 0 |
|
253 | 0 | if (src.IsEmpty()) { |
254 | 0 | // If the frame is a XUL element and has the attribute 'nodefaultsrc=true' |
255 | 0 | // then we will not use 'about:blank' as fallback but return early without |
256 | 0 | // starting a load if no 'src' attribute is given (or it's empty). |
257 | 0 | if (mOwnerContent->IsXULElement() && |
258 | 0 | mOwnerContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::nodefaultsrc, |
259 | 0 | nsGkAtoms::_true, eCaseMatters)) { |
260 | 0 | return; |
261 | 0 | } |
262 | 0 | src.AssignLiteral("about:blank"); |
263 | 0 | } |
264 | 0 | } |
265 | 0 |
|
266 | 0 | nsIDocument* doc = mOwnerContent->OwnerDoc(); |
267 | 0 | if (doc->IsStaticDocument()) { |
268 | 0 | return; |
269 | 0 | } |
270 | 0 | |
271 | 0 | if (doc->IsLoadedAsInteractiveData()) { |
272 | 0 | // XBL bindings doc shouldn't load sub-documents. |
273 | 0 | return; |
274 | 0 | } |
275 | 0 | |
276 | 0 | nsCOMPtr<nsIURI> base_uri = mOwnerContent->GetBaseURI(); |
277 | 0 | auto encoding = doc->GetDocumentCharacterSet(); |
278 | 0 |
|
279 | 0 | nsCOMPtr<nsIURI> uri; |
280 | 0 | nsresult rv = NS_NewURI(getter_AddRefs(uri), src, encoding, base_uri); |
281 | 0 |
|
282 | 0 | // If the URI was malformed, try to recover by loading about:blank. |
283 | 0 | if (rv == NS_ERROR_MALFORMED_URI) { |
284 | 0 | rv = NS_NewURI(getter_AddRefs(uri), NS_LITERAL_STRING("about:blank"), |
285 | 0 | encoding, base_uri); |
286 | 0 | } |
287 | 0 |
|
288 | 0 | if (NS_SUCCEEDED(rv)) { |
289 | 0 | rv = LoadURI(uri, principal, aOriginalSrc); |
290 | 0 | } |
291 | 0 |
|
292 | 0 | if (NS_FAILED(rv)) { |
293 | 0 | FireErrorEvent(); |
294 | 0 | } |
295 | 0 | } |
296 | | |
297 | | void |
298 | | nsFrameLoader::FireErrorEvent() |
299 | 0 | { |
300 | 0 | if (!mOwnerContent) { |
301 | 0 | return; |
302 | 0 | } |
303 | 0 | RefPtr<AsyncEventDispatcher> loadBlockingAsyncDispatcher = |
304 | 0 | new LoadBlockingAsyncEventDispatcher(mOwnerContent, |
305 | 0 | NS_LITERAL_STRING("error"), |
306 | 0 | CanBubble::eNo, |
307 | 0 | ChromeOnlyDispatch::eNo); |
308 | 0 | loadBlockingAsyncDispatcher->PostDOMEvent(); |
309 | 0 | } |
310 | | |
311 | | nsresult |
312 | | nsFrameLoader::LoadURI(nsIURI* aURI, bool aOriginalSrc) |
313 | 0 | { |
314 | 0 | return LoadURI(aURI, nullptr, aOriginalSrc); |
315 | 0 | } |
316 | | |
317 | | nsresult |
318 | | nsFrameLoader::LoadURI(nsIURI* aURI, nsIPrincipal* aTriggeringPrincipal, |
319 | | bool aOriginalSrc) |
320 | 0 | { |
321 | 0 | if (!aURI) |
322 | 0 | return NS_ERROR_INVALID_POINTER; |
323 | 0 | NS_ENSURE_STATE(!mDestroyCalled && mOwnerContent); |
324 | 0 |
|
325 | 0 | mLoadingOriginalSrc = aOriginalSrc; |
326 | 0 |
|
327 | 0 | nsCOMPtr<nsIDocument> doc = mOwnerContent->OwnerDoc(); |
328 | 0 |
|
329 | 0 | nsresult rv; |
330 | 0 | // If IsForJSPlugin() returns true then we want to allow the load. We're just |
331 | 0 | // loading the source for the implementation of the JS plugin from a URI |
332 | 0 | // that's under our control. We will already have done the security checks for |
333 | 0 | // loading the plugin content itself in the object/embed loading code. |
334 | 0 | if (!IsForJSPlugin()) { |
335 | 0 | rv = CheckURILoad(aURI, aTriggeringPrincipal); |
336 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
337 | 0 | } |
338 | 0 |
|
339 | 0 | mURIToLoad = aURI; |
340 | 0 | mTriggeringPrincipal = aTriggeringPrincipal; |
341 | 0 | rv = doc->InitializeFrameLoader(this); |
342 | 0 | if (NS_FAILED(rv)) { |
343 | 0 | mURIToLoad = nullptr; |
344 | 0 | mTriggeringPrincipal = nullptr; |
345 | 0 | } |
346 | 0 | return rv; |
347 | 0 | } |
348 | | |
349 | | bool |
350 | | nsFrameLoader::SwapBrowsersAndNotify(nsFrameLoader* aOther) |
351 | 0 | { |
352 | 0 | // Cache the owner content before calling SwapBrowsers, which will change |
353 | 0 | // these member variables. |
354 | 0 | RefPtr<mozilla::dom::Element> primaryContent = mOwnerContent; |
355 | 0 | RefPtr<mozilla::dom::Element> secondaryContent = aOther->mOwnerContent; |
356 | 0 |
|
357 | 0 | // Swap loaders through our owner, so the owner's listeners will be correctly |
358 | 0 | // setup. |
359 | 0 | nsCOMPtr<nsIBrowser> ourBrowser = do_QueryInterface(primaryContent); |
360 | 0 | nsCOMPtr<nsIBrowser> otherBrowser = do_QueryInterface(secondaryContent); |
361 | 0 | if (NS_WARN_IF(!ourBrowser || !otherBrowser)) { |
362 | 0 | return false; |
363 | 0 | } |
364 | 0 | nsresult rv = ourBrowser->SwapBrowsers(otherBrowser, nsIBrowser::SWAP_KEEP_PERMANENT_KEY); |
365 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
366 | 0 | return false; |
367 | 0 | } |
368 | 0 | |
369 | 0 | // Dispatch the BrowserChangedProcess event to tell JS that the process swap |
370 | 0 | // has occurred. |
371 | 0 | GroupedHistoryEventInit eventInit; |
372 | 0 | eventInit.mBubbles = true; |
373 | 0 | eventInit.mCancelable= false; |
374 | 0 | eventInit.mOtherBrowser = secondaryContent; |
375 | 0 | RefPtr<GroupedHistoryEvent> event = |
376 | 0 | GroupedHistoryEvent::Constructor(primaryContent, |
377 | 0 | NS_LITERAL_STRING("BrowserChangedProcess"), |
378 | 0 | eventInit); |
379 | 0 | event->SetTrusted(true); |
380 | 0 | primaryContent->DispatchEvent(*event); |
381 | 0 |
|
382 | 0 | return true; |
383 | 0 | } |
384 | | |
385 | | already_AddRefed<Promise> |
386 | | nsFrameLoader::FireWillChangeProcessEvent() |
387 | 0 | { |
388 | 0 | AutoJSAPI jsapi; |
389 | 0 | if (NS_WARN_IF(!jsapi.Init(mOwnerContent->GetOwnerGlobal()))) { |
390 | 0 | return nullptr; |
391 | 0 | } |
392 | 0 | JSContext* cx = jsapi.cx(); |
393 | 0 |
|
394 | 0 | // Set our mBrowserChangingProcessBlockers property to refer to the blockers |
395 | 0 | // list. We will synchronously dispatch a DOM event to collect this list of |
396 | 0 | // blockers. |
397 | 0 | nsTArray<RefPtr<Promise>> blockers; |
398 | 0 | mBrowserChangingProcessBlockers = &blockers; |
399 | 0 |
|
400 | 0 | GroupedHistoryEventInit eventInit; |
401 | 0 | eventInit.mBubbles = true; |
402 | 0 | eventInit.mCancelable = false; |
403 | 0 | eventInit.mOtherBrowser = nullptr; |
404 | 0 | RefPtr<GroupedHistoryEvent> event = |
405 | 0 | GroupedHistoryEvent::Constructor(mOwnerContent, |
406 | 0 | NS_LITERAL_STRING("BrowserWillChangeProcess"), |
407 | 0 | eventInit); |
408 | 0 | event->SetTrusted(true); |
409 | 0 | mOwnerContent->DispatchEvent(*event); |
410 | 0 |
|
411 | 0 | mBrowserChangingProcessBlockers = nullptr; |
412 | 0 |
|
413 | 0 | ErrorResult rv; |
414 | 0 | RefPtr<Promise> allPromise = Promise::All(cx, blockers, rv); |
415 | 0 | return allPromise.forget(); |
416 | 0 | } |
417 | | |
418 | | void |
419 | | nsFrameLoader::AddProcessChangeBlockingPromise(Promise& aPromise, ErrorResult& aRv) |
420 | 0 | { |
421 | 0 | if (NS_WARN_IF(!mBrowserChangingProcessBlockers)) { |
422 | 0 | aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); |
423 | 0 | } else { |
424 | 0 | mBrowserChangingProcessBlockers->AppendElement(&aPromise); |
425 | 0 | } |
426 | 0 | } |
427 | | |
428 | | nsresult |
429 | | nsFrameLoader::ReallyStartLoading() |
430 | 0 | { |
431 | 0 | nsresult rv = ReallyStartLoadingInternal(); |
432 | 0 | if (NS_FAILED(rv)) { |
433 | 0 | FireErrorEvent(); |
434 | 0 | } |
435 | 0 |
|
436 | 0 | return rv; |
437 | 0 | } |
438 | | |
439 | | nsresult |
440 | | nsFrameLoader::ReallyStartLoadingInternal() |
441 | 0 | { |
442 | 0 | NS_ENSURE_STATE(mURIToLoad && mOwnerContent && mOwnerContent->IsInComposedDoc()); |
443 | 0 |
|
444 | 0 | AUTO_PROFILER_LABEL("nsFrameLoader::ReallyStartLoadingInternal", OTHER); |
445 | 0 |
|
446 | 0 | if (IsRemoteFrame()) { |
447 | 0 | if (!mRemoteBrowser && !TryRemoteBrowser()) { |
448 | 0 | NS_WARNING("Couldn't create child process for iframe."); |
449 | 0 | return NS_ERROR_FAILURE; |
450 | 0 | } |
451 | 0 |
|
452 | 0 | // FIXME get error codes from child |
453 | 0 | mRemoteBrowser->LoadURL(mURIToLoad); |
454 | 0 |
|
455 | 0 | if (!mRemoteBrowserShown) { |
456 | 0 | // This can fail if it's too early to show the frame, we will retry later. |
457 | 0 | Unused << ShowRemoteFrame(ScreenIntSize(0, 0)); |
458 | 0 | } |
459 | 0 |
|
460 | 0 | return NS_OK; |
461 | 0 | } |
462 | 0 |
|
463 | 0 | nsresult rv = MaybeCreateDocShell(); |
464 | 0 | if (NS_FAILED(rv)) { |
465 | 0 | return rv; |
466 | 0 | } |
467 | 0 | NS_ASSERTION(mDocShell, |
468 | 0 | "MaybeCreateDocShell succeeded with a null mDocShell"); |
469 | 0 |
|
470 | 0 | // Just to be safe, recheck uri. |
471 | 0 | rv = CheckURILoad(mURIToLoad, mTriggeringPrincipal); |
472 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
473 | 0 |
|
474 | 0 | RefPtr<nsDocShellLoadInfo> loadInfo = new nsDocShellLoadInfo(); |
475 | 0 |
|
476 | 0 | loadInfo->SetOriginalFrameSrc(mLoadingOriginalSrc); |
477 | 0 | mLoadingOriginalSrc = false; |
478 | 0 |
|
479 | 0 | // If this frame is sandboxed with respect to origin we will set it up with |
480 | 0 | // a null principal later in nsDocShell::DoURILoad. |
481 | 0 | // We do it there to correctly sandbox content that was loaded into |
482 | 0 | // the frame via other methods than the src attribute. |
483 | 0 | // We'll use our principal, not that of the document loaded inside us. This |
484 | 0 | // is very important; needed to prevent XSS attacks on documents loaded in |
485 | 0 | // subframes! |
486 | 0 | if (mTriggeringPrincipal) { |
487 | 0 | loadInfo->SetTriggeringPrincipal(mTriggeringPrincipal); |
488 | 0 | } else { |
489 | 0 | loadInfo->SetTriggeringPrincipal(mOwnerContent->NodePrincipal()); |
490 | 0 | } |
491 | 0 |
|
492 | 0 | nsCOMPtr<nsIURI> referrer; |
493 | 0 |
|
494 | 0 | nsAutoString srcdoc; |
495 | 0 | bool isSrcdoc = mOwnerContent->IsHTMLElement(nsGkAtoms::iframe) && |
496 | 0 | mOwnerContent->GetAttr(kNameSpaceID_None, nsGkAtoms::srcdoc, |
497 | 0 | srcdoc); |
498 | 0 |
|
499 | 0 | if (isSrcdoc) { |
500 | 0 | nsAutoString referrerStr; |
501 | 0 | mOwnerContent->OwnerDoc()->GetReferrer(referrerStr); |
502 | 0 | rv = NS_NewURI(getter_AddRefs(referrer), referrerStr); |
503 | 0 |
|
504 | 0 | loadInfo->SetSrcdocData(srcdoc); |
505 | 0 | nsCOMPtr<nsIURI> baseURI = mOwnerContent->GetBaseURI(); |
506 | 0 | loadInfo->SetBaseURI(baseURI); |
507 | 0 | } |
508 | 0 | else { |
509 | 0 | rv = mOwnerContent->NodePrincipal()->GetURI(getter_AddRefs(referrer)); |
510 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
511 | 0 | } |
512 | 0 |
|
513 | 0 | // Use referrer as long as it is not an NullPrincipalURI. |
514 | 0 | // We could add a method such as GetReferrerURI to principals to make this |
515 | 0 | // cleaner, but given that we need to start using Source Browsing Context for |
516 | 0 | // referrer (see Bug 960639) this may be wasted effort at this stage. |
517 | 0 | if (referrer) { |
518 | 0 | bool isNullPrincipalScheme; |
519 | 0 | rv = referrer->SchemeIs(NS_NULLPRINCIPAL_SCHEME, &isNullPrincipalScheme); |
520 | 0 | if (NS_SUCCEEDED(rv) && !isNullPrincipalScheme) { |
521 | 0 | loadInfo->SetReferrer(referrer); |
522 | 0 | } |
523 | 0 | } |
524 | 0 |
|
525 | 0 | // get referrer policy for this iframe: |
526 | 0 | // first load document wide policy, then |
527 | 0 | // load iframe referrer attribute if enabled in preferences |
528 | 0 | // per element referrer overrules document wide referrer if enabled |
529 | 0 | net::ReferrerPolicy referrerPolicy = mOwnerContent->OwnerDoc()->GetReferrerPolicy(); |
530 | 0 | HTMLIFrameElement* iframe = HTMLIFrameElement::FromNode(mOwnerContent); |
531 | 0 | if (iframe) { |
532 | 0 | net::ReferrerPolicy iframeReferrerPolicy = iframe->GetReferrerPolicyAsEnum(); |
533 | 0 | if (iframeReferrerPolicy != net::RP_Unset) { |
534 | 0 | referrerPolicy = iframeReferrerPolicy; |
535 | 0 | } |
536 | 0 | } |
537 | 0 | loadInfo->SetReferrerPolicy(referrerPolicy); |
538 | 0 |
|
539 | 0 | // Default flags: |
540 | 0 | int32_t flags = nsIWebNavigation::LOAD_FLAGS_NONE; |
541 | 0 |
|
542 | 0 | // Flags for browser frame: |
543 | 0 | if (OwnerIsMozBrowserFrame()) { |
544 | 0 | flags = nsIWebNavigation::LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP | |
545 | 0 | nsIWebNavigation::LOAD_FLAGS_DISALLOW_INHERIT_PRINCIPAL; |
546 | 0 | } |
547 | 0 |
|
548 | 0 | // Kick off the load... |
549 | 0 | bool tmpState = mNeedsAsyncDestroy; |
550 | 0 | mNeedsAsyncDestroy = true; |
551 | 0 | nsCOMPtr<nsIURI> uriToLoad = mURIToLoad; |
552 | 0 | rv = mDocShell->LoadURI(uriToLoad, loadInfo, flags, false); |
553 | 0 | mNeedsAsyncDestroy = tmpState; |
554 | 0 | mURIToLoad = nullptr; |
555 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
556 | 0 |
|
557 | 0 | return NS_OK; |
558 | 0 | } |
559 | | |
560 | | nsresult |
561 | | nsFrameLoader::CheckURILoad(nsIURI* aURI, nsIPrincipal* aTriggeringPrincipal) |
562 | 0 | { |
563 | 0 | // Check for security. The fun part is trying to figure out what principals |
564 | 0 | // to use. The way I figure it, if we're doing a LoadFrame() accidentally |
565 | 0 | // (eg someone created a frame/iframe node, we're being parsed, XUL iframes |
566 | 0 | // are being reframed, etc.) then we definitely want to use the node |
567 | 0 | // principal of mOwnerContent for security checks. If, on the other hand, |
568 | 0 | // someone's setting the src on our owner content, or created it via script, |
569 | 0 | // or whatever, then they can clearly access it... and we should still use |
570 | 0 | // the principal of mOwnerContent. I don't think that leads to privilege |
571 | 0 | // escalation, and it's reasonably guaranteed to not lead to XSS issues |
572 | 0 | // (since caller can already access mOwnerContent in this case). So just use |
573 | 0 | // the principal of mOwnerContent no matter what. If script wants to run |
574 | 0 | // things with its own permissions, which differ from those of mOwnerContent |
575 | 0 | // (which means the script is privileged in some way) it should set |
576 | 0 | // window.location instead. |
577 | 0 | nsIScriptSecurityManager *secMan = nsContentUtils::GetSecurityManager(); |
578 | 0 |
|
579 | 0 | // Get our principal |
580 | 0 | nsIPrincipal* principal = (aTriggeringPrincipal |
581 | 0 | ? aTriggeringPrincipal |
582 | 0 | : mOwnerContent->NodePrincipal()); |
583 | 0 |
|
584 | 0 | // Check if we are allowed to load absURL |
585 | 0 | nsresult rv = |
586 | 0 | secMan->CheckLoadURIWithPrincipal(principal, aURI, |
587 | 0 | nsIScriptSecurityManager::STANDARD); |
588 | 0 | if (NS_FAILED(rv)) { |
589 | 0 | return rv; // We're not |
590 | 0 | } |
591 | 0 | |
592 | 0 | // Bail out if this is an infinite recursion scenario |
593 | 0 | if (IsRemoteFrame()) { |
594 | 0 | return NS_OK; |
595 | 0 | } |
596 | 0 | return CheckForRecursiveLoad(aURI); |
597 | 0 | } |
598 | | |
599 | | nsIDocShell* |
600 | | nsFrameLoader::GetDocShell(ErrorResult& aRv) |
601 | 0 | { |
602 | 0 | if (IsRemoteFrame()) { |
603 | 0 | return nullptr; |
604 | 0 | } |
605 | 0 | |
606 | 0 | // If we have an owner, make sure we have a docshell and return |
607 | 0 | // that. If not, we're most likely in the middle of being torn down, |
608 | 0 | // then we just return null. |
609 | 0 | if (mOwnerContent) { |
610 | 0 | nsresult rv = MaybeCreateDocShell(); |
611 | 0 | if (NS_FAILED(rv)) { |
612 | 0 | aRv.Throw(rv); |
613 | 0 | return nullptr; |
614 | 0 | } |
615 | 0 | NS_ASSERTION(mDocShell, |
616 | 0 | "MaybeCreateDocShell succeeded, but null mDocShell"); |
617 | 0 | } |
618 | 0 |
|
619 | 0 | return mDocShell; |
620 | 0 | } |
621 | | |
622 | | static void |
623 | | SetTreeOwnerAndChromeEventHandlerOnDocshellTree(nsIDocShellTreeItem* aItem, |
624 | | nsIDocShellTreeOwner* aOwner, |
625 | | EventTarget* aHandler) |
626 | 0 | { |
627 | 0 | MOZ_ASSERT(aItem, "Must have item"); |
628 | 0 |
|
629 | 0 | aItem->SetTreeOwner(aOwner); |
630 | 0 |
|
631 | 0 | int32_t childCount = 0; |
632 | 0 | aItem->GetChildCount(&childCount); |
633 | 0 | for (int32_t i = 0; i < childCount; ++i) { |
634 | 0 | nsCOMPtr<nsIDocShellTreeItem> item; |
635 | 0 | aItem->GetChildAt(i, getter_AddRefs(item)); |
636 | 0 | if (aHandler) { |
637 | 0 | nsCOMPtr<nsIDocShell> shell(do_QueryInterface(item)); |
638 | 0 | shell->SetChromeEventHandler(aHandler); |
639 | 0 | } |
640 | 0 | SetTreeOwnerAndChromeEventHandlerOnDocshellTree(item, aOwner, aHandler); |
641 | 0 | } |
642 | 0 | } |
643 | | |
644 | | /** |
645 | | * Set the type of the treeitem and hook it up to the treeowner. |
646 | | * @param aItem the treeitem we're working with |
647 | | * @param aTreeOwner the relevant treeowner; might be null |
648 | | * @param aParentType the nsIDocShellTreeItem::GetType of our parent docshell |
649 | | * @param aParentNode if non-null, the docshell we should be added as a child to |
650 | | * |
651 | | * @return whether aItem is top-level content |
652 | | */ |
653 | | bool |
654 | | nsFrameLoader::AddTreeItemToTreeOwner(nsIDocShellTreeItem* aItem, |
655 | | nsIDocShellTreeOwner* aOwner, |
656 | | int32_t aParentType, |
657 | | nsIDocShell* aParentNode) |
658 | 0 | { |
659 | 0 | MOZ_ASSERT(aItem, "Must have docshell treeitem"); |
660 | 0 | MOZ_ASSERT(mOwnerContent, "Must have owning content"); |
661 | 0 |
|
662 | 0 | nsAutoString value; |
663 | 0 | bool isContent = mOwnerContent->AttrValueIs( |
664 | 0 | kNameSpaceID_None, TypeAttrName(), nsGkAtoms::content, eIgnoreCase); |
665 | 0 |
|
666 | 0 | // Force mozbrowser frames to always be typeContent, even if the |
667 | 0 | // mozbrowser interfaces are disabled. |
668 | 0 | nsCOMPtr<nsIDOMMozBrowserFrame> mozbrowser = |
669 | 0 | do_QueryInterface(mOwnerContent); |
670 | 0 | if (mozbrowser) { |
671 | 0 | bool isMozbrowser = false; |
672 | 0 | mozbrowser->GetMozbrowser(&isMozbrowser); |
673 | 0 | isContent |= isMozbrowser; |
674 | 0 | } |
675 | 0 |
|
676 | 0 | if (isContent) { |
677 | 0 | // The web shell's type is content. |
678 | 0 |
|
679 | 0 | aItem->SetItemType(nsIDocShellTreeItem::typeContent); |
680 | 0 | } else { |
681 | 0 | // Inherit our type from our parent docshell. If it is |
682 | 0 | // chrome, we'll be chrome. If it is content, we'll be |
683 | 0 | // content. |
684 | 0 |
|
685 | 0 | aItem->SetItemType(aParentType); |
686 | 0 | } |
687 | 0 |
|
688 | 0 | // Now that we have our type set, add ourselves to the parent, as needed. |
689 | 0 | if (aParentNode) { |
690 | 0 | aParentNode->AddChild(aItem); |
691 | 0 | } else if (nsCOMPtr<nsIDocShell> childAsDocShell = do_QueryInterface(aItem)) { |
692 | 0 | childAsDocShell->AttachBrowsingContext(aParentNode); |
693 | 0 | } |
694 | 0 |
|
695 | 0 | bool retval = false; |
696 | 0 | if (aParentType == nsIDocShellTreeItem::typeChrome && isContent) { |
697 | 0 | retval = true; |
698 | 0 |
|
699 | 0 | bool is_primary = |
700 | 0 | mOwnerContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::primary, |
701 | 0 | nsGkAtoms::_true, eIgnoreCase); |
702 | 0 | if (aOwner) { |
703 | 0 | mOwnerContent->AddMutationObserver(this); |
704 | 0 | mObservingOwnerContent = true; |
705 | 0 | aOwner->ContentShellAdded(aItem, is_primary); |
706 | 0 | } |
707 | 0 | } |
708 | 0 |
|
709 | 0 | return retval; |
710 | 0 | } |
711 | | |
712 | | static bool |
713 | | AllDescendantsOfType(nsIDocShellTreeItem* aParentItem, int32_t aType) |
714 | 0 | { |
715 | 0 | int32_t childCount = 0; |
716 | 0 | aParentItem->GetChildCount(&childCount); |
717 | 0 |
|
718 | 0 | for (int32_t i = 0; i < childCount; ++i) { |
719 | 0 | nsCOMPtr<nsIDocShellTreeItem> kid; |
720 | 0 | aParentItem->GetChildAt(i, getter_AddRefs(kid)); |
721 | 0 |
|
722 | 0 | if (kid->ItemType() != aType || !AllDescendantsOfType(kid, aType)) { |
723 | 0 | return false; |
724 | 0 | } |
725 | 0 | } |
726 | 0 |
|
727 | 0 | return true; |
728 | 0 | } |
729 | | |
730 | | /** |
731 | | * A class that automatically sets mInShow to false when it goes |
732 | | * out of scope. |
733 | | */ |
734 | | class MOZ_RAII AutoResetInShow { |
735 | | private: |
736 | | nsFrameLoader* mFrameLoader; |
737 | | MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER |
738 | | public: |
739 | | explicit AutoResetInShow(nsFrameLoader* aFrameLoader MOZ_GUARD_OBJECT_NOTIFIER_PARAM) |
740 | | : mFrameLoader(aFrameLoader) |
741 | 0 | { |
742 | 0 | MOZ_GUARD_OBJECT_NOTIFIER_INIT; |
743 | 0 | } |
744 | 0 | ~AutoResetInShow() { mFrameLoader->mInShow = false; } |
745 | | }; |
746 | | |
747 | | static bool |
748 | | ParentWindowIsActive(nsIDocument* aDoc) |
749 | 0 | { |
750 | 0 | nsCOMPtr<nsPIWindowRoot> root = nsContentUtils::GetWindowRoot(aDoc); |
751 | 0 | if (root) { |
752 | 0 | nsPIDOMWindowOuter* rootWin = root->GetWindow(); |
753 | 0 | return rootWin && rootWin->IsActive(); |
754 | 0 | } |
755 | 0 | return false; |
756 | 0 | } |
757 | | |
758 | | void |
759 | | nsFrameLoader::MaybeShowFrame() |
760 | 0 | { |
761 | 0 | nsIFrame* frame = GetPrimaryFrameOfOwningContent(); |
762 | 0 | if (frame) { |
763 | 0 | nsSubDocumentFrame* subDocFrame = do_QueryFrame(frame); |
764 | 0 | if (subDocFrame) { |
765 | 0 | subDocFrame->MaybeShowViewer(); |
766 | 0 | } |
767 | 0 | } |
768 | 0 | } |
769 | | |
770 | | bool |
771 | | nsFrameLoader::Show(int32_t marginWidth, int32_t marginHeight, |
772 | | int32_t scrollbarPrefX, int32_t scrollbarPrefY, |
773 | | nsSubDocumentFrame* frame) |
774 | 0 | { |
775 | 0 | if (mInShow) { |
776 | 0 | return false; |
777 | 0 | } |
778 | 0 | // Reset mInShow if we exit early. |
779 | 0 | AutoResetInShow resetInShow(this); |
780 | 0 | mInShow = true; |
781 | 0 |
|
782 | 0 | ScreenIntSize size = frame->GetSubdocumentSize(); |
783 | 0 | if (IsRemoteFrame()) { |
784 | 0 | return ShowRemoteFrame(size, frame); |
785 | 0 | } |
786 | 0 | |
787 | 0 | nsresult rv = MaybeCreateDocShell(); |
788 | 0 | if (NS_FAILED(rv)) { |
789 | 0 | return false; |
790 | 0 | } |
791 | 0 | NS_ASSERTION(mDocShell, |
792 | 0 | "MaybeCreateDocShell succeeded, but null mDocShell"); |
793 | 0 | if (!mDocShell) { |
794 | 0 | return false; |
795 | 0 | } |
796 | 0 | |
797 | 0 | mDocShell->SetMarginWidth(marginWidth); |
798 | 0 | mDocShell->SetMarginHeight(marginHeight); |
799 | 0 |
|
800 | 0 | nsCOMPtr<nsIScrollable> sc = do_QueryInterface(mDocShell); |
801 | 0 | if (sc) { |
802 | 0 | sc->SetDefaultScrollbarPreferences(nsIScrollable::ScrollOrientation_X, |
803 | 0 | scrollbarPrefX); |
804 | 0 | sc->SetDefaultScrollbarPreferences(nsIScrollable::ScrollOrientation_Y, |
805 | 0 | scrollbarPrefY); |
806 | 0 | } |
807 | 0 |
|
808 | 0 | nsCOMPtr<nsIPresShell> presShell = mDocShell->GetPresShell(); |
809 | 0 | if (presShell) { |
810 | 0 | // Ensure root scroll frame is reflowed in case scroll preferences or |
811 | 0 | // margins have changed |
812 | 0 | nsIFrame* rootScrollFrame = presShell->GetRootScrollFrame(); |
813 | 0 | if (rootScrollFrame) { |
814 | 0 | presShell->FrameNeedsReflow(rootScrollFrame, nsIPresShell::eResize, |
815 | 0 | NS_FRAME_IS_DIRTY); |
816 | 0 | } |
817 | 0 | return true; |
818 | 0 | } |
819 | 0 |
|
820 | 0 | nsView* view = frame->EnsureInnerView(); |
821 | 0 | if (!view) |
822 | 0 | return false; |
823 | 0 | |
824 | 0 | nsCOMPtr<nsIBaseWindow> baseWindow = do_QueryInterface(mDocShell); |
825 | 0 | NS_ASSERTION(baseWindow, "Found a nsIDocShell that isn't a nsIBaseWindow."); |
826 | 0 | baseWindow->InitWindow(nullptr, view->GetWidget(), 0, 0, |
827 | 0 | size.width, size.height); |
828 | 0 | // This is kinda whacky, this "Create()" call doesn't really |
829 | 0 | // create anything, one starts to wonder why this was named |
830 | 0 | // "Create"... |
831 | 0 | baseWindow->Create(); |
832 | 0 | baseWindow->SetVisibility(true); |
833 | 0 | NS_ENSURE_TRUE(mDocShell, false); |
834 | 0 |
|
835 | 0 | // Trigger editor re-initialization if midas is turned on in the |
836 | 0 | // sub-document. This shouldn't be necessary, but given the way our |
837 | 0 | // editor works, it is. See |
838 | 0 | // https://bugzilla.mozilla.org/show_bug.cgi?id=284245 |
839 | 0 | presShell = mDocShell->GetPresShell(); |
840 | 0 | if (presShell) { |
841 | 0 | nsIDocument* doc = presShell->GetDocument(); |
842 | 0 | nsHTMLDocument* htmlDoc = |
843 | 0 | doc && doc->IsHTMLOrXHTML() ? doc->AsHTMLDocument() : nullptr; |
844 | 0 |
|
845 | 0 | if (htmlDoc) { |
846 | 0 | nsAutoString designMode; |
847 | 0 | htmlDoc->GetDesignMode(designMode); |
848 | 0 |
|
849 | 0 | if (designMode.EqualsLiteral("on")) { |
850 | 0 | // Hold on to the editor object to let the document reattach to the |
851 | 0 | // same editor object, instead of creating a new one. |
852 | 0 | RefPtr<HTMLEditor> htmlEditor = mDocShell->GetHTMLEditor(); |
853 | 0 | Unused << htmlEditor; |
854 | 0 | htmlDoc->SetDesignMode(NS_LITERAL_STRING("off"), Nothing(), |
855 | 0 | IgnoreErrors()); |
856 | 0 |
|
857 | 0 | htmlDoc->SetDesignMode(NS_LITERAL_STRING("on"), Nothing(), |
858 | 0 | IgnoreErrors()); |
859 | 0 | } else { |
860 | 0 | // Re-initialize the presentation for contenteditable documents |
861 | 0 | bool editable = false, |
862 | 0 | hasEditingSession = false; |
863 | 0 | mDocShell->GetEditable(&editable); |
864 | 0 | mDocShell->GetHasEditingSession(&hasEditingSession); |
865 | 0 | RefPtr<HTMLEditor> htmlEditor = mDocShell->GetHTMLEditor(); |
866 | 0 | if (editable && hasEditingSession && htmlEditor) { |
867 | 0 | htmlEditor->PostCreate(); |
868 | 0 | } |
869 | 0 | } |
870 | 0 | } |
871 | 0 | } |
872 | 0 |
|
873 | 0 | mInShow = false; |
874 | 0 | if (mHideCalled) { |
875 | 0 | mHideCalled = false; |
876 | 0 | Hide(); |
877 | 0 | return false; |
878 | 0 | } |
879 | 0 | return true; |
880 | 0 | } |
881 | | |
882 | | void |
883 | | nsFrameLoader::MarginsChanged(uint32_t aMarginWidth, |
884 | | uint32_t aMarginHeight) |
885 | 0 | { |
886 | 0 | // We assume that the margins are always zero for remote frames. |
887 | 0 | if (IsRemoteFrame()) |
888 | 0 | return; |
889 | 0 | |
890 | 0 | // If there's no docshell, we're probably not up and running yet. |
891 | 0 | // nsFrameLoader::Show() will take care of setting the right |
892 | 0 | // margins. |
893 | 0 | if (!mDocShell) |
894 | 0 | return; |
895 | 0 | |
896 | 0 | // Set the margins |
897 | 0 | mDocShell->SetMarginWidth(aMarginWidth); |
898 | 0 | mDocShell->SetMarginHeight(aMarginHeight); |
899 | 0 |
|
900 | 0 | // There's a cached property declaration block |
901 | 0 | // that needs to be updated |
902 | 0 | if (nsIDocument* doc = mDocShell->GetDocument()) { |
903 | 0 | for (nsINode* cur = doc; cur; cur = cur->GetNextNode()) { |
904 | 0 | if (cur->IsHTMLElement(nsGkAtoms::body)) { |
905 | 0 | static_cast<HTMLBodyElement*>(cur)->ClearMappedServoStyle(); |
906 | 0 | } |
907 | 0 | } |
908 | 0 | } |
909 | 0 |
|
910 | 0 | // Trigger a restyle if there's a prescontext |
911 | 0 | // FIXME: This could do something much less expensive. |
912 | 0 | RefPtr<nsPresContext> presContext; |
913 | 0 | mDocShell->GetPresContext(getter_AddRefs(presContext)); |
914 | 0 | if (presContext) |
915 | 0 | // rebuild, because now the same nsMappedAttributes* will produce |
916 | 0 | // a different style |
917 | 0 | presContext->RebuildAllStyleData(nsChangeHint(0), eRestyle_Subtree); |
918 | 0 | } |
919 | | |
920 | | bool |
921 | | nsFrameLoader::ShowRemoteFrame(const ScreenIntSize& size, |
922 | | nsSubDocumentFrame *aFrame) |
923 | 0 | { |
924 | 0 | AUTO_PROFILER_LABEL("nsFrameLoader::ShowRemoteFrame", OTHER); |
925 | 0 | NS_ASSERTION(IsRemoteFrame(), "ShowRemote only makes sense on remote frames."); |
926 | 0 |
|
927 | 0 | if (!mRemoteBrowser && !TryRemoteBrowser()) { |
928 | 0 | NS_ERROR("Couldn't create child process."); |
929 | 0 | return false; |
930 | 0 | } |
931 | 0 |
|
932 | 0 | // FIXME/bug 589337: Show()/Hide() is pretty expensive for |
933 | 0 | // cross-process layers; need to figure out what behavior we really |
934 | 0 | // want here. For now, hack. |
935 | 0 | if (!mRemoteBrowserShown) { |
936 | 0 | if (!mOwnerContent || |
937 | 0 | !mOwnerContent->GetComposedDoc()) { |
938 | 0 | return false; |
939 | 0 | } |
940 | 0 | |
941 | 0 | // We never want to host remote frameloaders in simple popups, like menus. |
942 | 0 | nsIWidget* widget = nsContentUtils::WidgetForContent(mOwnerContent); |
943 | 0 | if (!widget || static_cast<nsBaseWidget*>(widget)->IsSmallPopup()) { |
944 | 0 | return false; |
945 | 0 | } |
946 | 0 | |
947 | 0 | RenderFrameParent* rfp = GetCurrentRenderFrame(); |
948 | 0 | if (!rfp) { |
949 | 0 | return false; |
950 | 0 | } |
951 | 0 | |
952 | 0 | if (!rfp->AttachLayerManager()) { |
953 | 0 | // This is just not going to work. |
954 | 0 | return false; |
955 | 0 | } |
956 | 0 | |
957 | 0 | mRemoteBrowser->Show(size, ParentWindowIsActive(mOwnerContent->OwnerDoc())); |
958 | 0 | mRemoteBrowserShown = true; |
959 | 0 |
|
960 | 0 | nsCOMPtr<nsIObserverService> os = services::GetObserverService(); |
961 | 0 | if (os) { |
962 | 0 | os->NotifyObservers(ToSupports(this), |
963 | 0 | "remote-browser-shown", nullptr); |
964 | 0 | } |
965 | 0 | } else { |
966 | 0 | nsIntRect dimensions; |
967 | 0 | NS_ENSURE_SUCCESS(GetWindowDimensions(dimensions), false); |
968 | 0 |
|
969 | 0 | // Don't show remote iframe if we are waiting for the completion of reflow. |
970 | 0 | if (!aFrame || !(aFrame->GetStateBits() & NS_FRAME_FIRST_REFLOW)) { |
971 | 0 | mRemoteBrowser->UpdateDimensions(dimensions, size); |
972 | 0 | } |
973 | 0 | } |
974 | 0 |
|
975 | 0 | return true; |
976 | 0 | } |
977 | | |
978 | | void |
979 | | nsFrameLoader::Hide() |
980 | 0 | { |
981 | 0 | if (mHideCalled) { |
982 | 0 | return; |
983 | 0 | } |
984 | 0 | if (mInShow) { |
985 | 0 | mHideCalled = true; |
986 | 0 | return; |
987 | 0 | } |
988 | 0 | |
989 | 0 | if (!mDocShell) |
990 | 0 | return; |
991 | 0 | |
992 | 0 | nsCOMPtr<nsIContentViewer> contentViewer; |
993 | 0 | mDocShell->GetContentViewer(getter_AddRefs(contentViewer)); |
994 | 0 | if (contentViewer) |
995 | 0 | contentViewer->SetSticky(false); |
996 | 0 |
|
997 | 0 | nsCOMPtr<nsIBaseWindow> baseWin = do_QueryInterface(mDocShell); |
998 | 0 | NS_ASSERTION(baseWin, |
999 | 0 | "Found an nsIDocShell which doesn't implement nsIBaseWindow."); |
1000 | 0 | baseWin->SetVisibility(false); |
1001 | 0 | baseWin->SetParentWidget(nullptr); |
1002 | 0 | } |
1003 | | |
1004 | | void |
1005 | | nsFrameLoader::ForceLayoutIfNecessary() |
1006 | 0 | { |
1007 | 0 | nsIFrame* frame = GetPrimaryFrameOfOwningContent(); |
1008 | 0 | if (!frame) { |
1009 | 0 | return; |
1010 | 0 | } |
1011 | 0 | |
1012 | 0 | nsPresContext* presContext = frame->PresContext(); |
1013 | 0 | if (!presContext) { |
1014 | 0 | return; |
1015 | 0 | } |
1016 | 0 | |
1017 | 0 | // Only force the layout flush if the frameloader hasn't ever been |
1018 | 0 | // run through layout. |
1019 | 0 | if (frame->GetStateBits() & NS_FRAME_FIRST_REFLOW) { |
1020 | 0 | if (nsCOMPtr<nsIPresShell> shell = presContext->GetPresShell()) { |
1021 | 0 | shell->FlushPendingNotifications(FlushType::Layout); |
1022 | 0 | } |
1023 | 0 | } |
1024 | 0 | } |
1025 | | |
1026 | | nsresult |
1027 | | nsFrameLoader::SwapWithOtherRemoteLoader(nsFrameLoader* aOther, |
1028 | | nsIFrameLoaderOwner* aThisOwner, |
1029 | | nsIFrameLoaderOwner* aOtherOwner) |
1030 | 0 | { |
1031 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
1032 | 0 |
|
1033 | | #ifdef DEBUG |
1034 | | RefPtr<nsFrameLoader> first = aThisOwner->GetFrameLoader(); |
1035 | | RefPtr<nsFrameLoader> second = aOtherOwner->GetFrameLoader(); |
1036 | | MOZ_ASSERT(first == this, "aThisOwner must own this"); |
1037 | | MOZ_ASSERT(second == aOther, "aOtherOwner must own aOther"); |
1038 | | #endif |
1039 | |
|
1040 | 0 | Element* ourContent = mOwnerContent; |
1041 | 0 | Element* otherContent = aOther->mOwnerContent; |
1042 | 0 |
|
1043 | 0 | if (!ourContent || !otherContent) { |
1044 | 0 | // Can't handle this |
1045 | 0 | return NS_ERROR_NOT_IMPLEMENTED; |
1046 | 0 | } |
1047 | 0 | |
1048 | 0 | // Make sure there are no same-origin issues |
1049 | 0 | bool equal; |
1050 | 0 | nsresult rv = |
1051 | 0 | ourContent->NodePrincipal()->Equals(otherContent->NodePrincipal(), &equal); |
1052 | 0 | if (NS_FAILED(rv) || !equal) { |
1053 | 0 | // Security problems loom. Just bail on it all |
1054 | 0 | return NS_ERROR_DOM_SECURITY_ERR; |
1055 | 0 | } |
1056 | 0 | |
1057 | 0 | nsIDocument* ourDoc = ourContent->GetComposedDoc(); |
1058 | 0 | nsIDocument* otherDoc = otherContent->GetComposedDoc(); |
1059 | 0 | if (!ourDoc || !otherDoc) { |
1060 | 0 | // Again, how odd, given that we had docshells |
1061 | 0 | return NS_ERROR_NOT_IMPLEMENTED; |
1062 | 0 | } |
1063 | 0 | |
1064 | 0 | nsIPresShell* ourShell = ourDoc->GetShell(); |
1065 | 0 | nsIPresShell* otherShell = otherDoc->GetShell(); |
1066 | 0 | if (!ourShell || !otherShell) { |
1067 | 0 | return NS_ERROR_NOT_IMPLEMENTED; |
1068 | 0 | } |
1069 | 0 | |
1070 | 0 | if (!mRemoteBrowser || !aOther->mRemoteBrowser) { |
1071 | 0 | return NS_ERROR_NOT_IMPLEMENTED; |
1072 | 0 | } |
1073 | 0 | |
1074 | 0 | if (mRemoteBrowser->IsIsolatedMozBrowserElement() != |
1075 | 0 | aOther->mRemoteBrowser->IsIsolatedMozBrowserElement()) { |
1076 | 0 | return NS_ERROR_NOT_IMPLEMENTED; |
1077 | 0 | } |
1078 | 0 | |
1079 | 0 | // When we swap docShells, maybe we have to deal with a new page created just |
1080 | 0 | // for this operation. In this case, the browser code should already have set |
1081 | 0 | // the correct userContextId attribute value in the owning element, but our |
1082 | 0 | // docShell, that has been created way before) doesn't know that that |
1083 | 0 | // happened. |
1084 | 0 | // This is the reason why now we must retrieve the correct value from the |
1085 | 0 | // usercontextid attribute before comparing our originAttributes with the |
1086 | 0 | // other one. |
1087 | 0 | OriginAttributes ourOriginAttributes = |
1088 | 0 | mRemoteBrowser->OriginAttributesRef(); |
1089 | 0 | rv = PopulateUserContextIdFromAttribute(ourOriginAttributes); |
1090 | 0 | NS_ENSURE_SUCCESS(rv,rv); |
1091 | 0 |
|
1092 | 0 | OriginAttributes otherOriginAttributes = |
1093 | 0 | aOther->mRemoteBrowser->OriginAttributesRef(); |
1094 | 0 | rv = aOther->PopulateUserContextIdFromAttribute(otherOriginAttributes); |
1095 | 0 | NS_ENSURE_SUCCESS(rv,rv); |
1096 | 0 |
|
1097 | 0 | if (ourOriginAttributes != otherOriginAttributes) { |
1098 | 0 | return NS_ERROR_NOT_IMPLEMENTED; |
1099 | 0 | } |
1100 | 0 | |
1101 | 0 | bool ourHasHistory = |
1102 | 0 | mIsTopLevelContent && |
1103 | 0 | ourContent->IsXULElement(nsGkAtoms::browser) && |
1104 | 0 | !ourContent->HasAttr(kNameSpaceID_None, nsGkAtoms::disablehistory); |
1105 | 0 | bool otherHasHistory = |
1106 | 0 | aOther->mIsTopLevelContent && |
1107 | 0 | otherContent->IsXULElement(nsGkAtoms::browser) && |
1108 | 0 | !otherContent->HasAttr(kNameSpaceID_None, nsGkAtoms::disablehistory); |
1109 | 0 | if (ourHasHistory != otherHasHistory) { |
1110 | 0 | return NS_ERROR_NOT_IMPLEMENTED; |
1111 | 0 | } |
1112 | 0 | |
1113 | 0 | if (mInSwap || aOther->mInSwap) { |
1114 | 0 | return NS_ERROR_NOT_IMPLEMENTED; |
1115 | 0 | } |
1116 | 0 | mInSwap = aOther->mInSwap = true; |
1117 | 0 |
|
1118 | 0 | nsIFrame* ourFrame = ourContent->GetPrimaryFrame(); |
1119 | 0 | nsIFrame* otherFrame = otherContent->GetPrimaryFrame(); |
1120 | 0 | if (!ourFrame || !otherFrame) { |
1121 | 0 | mInSwap = aOther->mInSwap = false; |
1122 | 0 | return NS_ERROR_NOT_IMPLEMENTED; |
1123 | 0 | } |
1124 | 0 | |
1125 | 0 | nsSubDocumentFrame* ourFrameFrame = do_QueryFrame(ourFrame); |
1126 | 0 | if (!ourFrameFrame) { |
1127 | 0 | mInSwap = aOther->mInSwap = false; |
1128 | 0 | return NS_ERROR_NOT_IMPLEMENTED; |
1129 | 0 | } |
1130 | 0 | |
1131 | 0 | rv = ourFrameFrame->BeginSwapDocShells(otherFrame); |
1132 | 0 | if (NS_FAILED(rv)) { |
1133 | 0 | mInSwap = aOther->mInSwap = false; |
1134 | 0 | return rv; |
1135 | 0 | } |
1136 | 0 | |
1137 | 0 | nsCOMPtr<nsIBrowserDOMWindow> otherBrowserDOMWindow = |
1138 | 0 | aOther->mRemoteBrowser->GetBrowserDOMWindow(); |
1139 | 0 | nsCOMPtr<nsIBrowserDOMWindow> browserDOMWindow = |
1140 | 0 | mRemoteBrowser->GetBrowserDOMWindow(); |
1141 | 0 |
|
1142 | 0 | if (!!otherBrowserDOMWindow != !!browserDOMWindow) { |
1143 | 0 | return NS_ERROR_NOT_IMPLEMENTED; |
1144 | 0 | } |
1145 | 0 | |
1146 | 0 | // Destroy browser frame scripts for content leaving a frame with browser API |
1147 | 0 | if (OwnerIsMozBrowserFrame() && !aOther->OwnerIsMozBrowserFrame()) { |
1148 | 0 | DestroyBrowserFrameScripts(); |
1149 | 0 | } |
1150 | 0 | if (!OwnerIsMozBrowserFrame() && aOther->OwnerIsMozBrowserFrame()) { |
1151 | 0 | aOther->DestroyBrowserFrameScripts(); |
1152 | 0 | } |
1153 | 0 |
|
1154 | 0 | aOther->mRemoteBrowser->SetBrowserDOMWindow(browserDOMWindow); |
1155 | 0 | mRemoteBrowser->SetBrowserDOMWindow(otherBrowserDOMWindow); |
1156 | 0 |
|
1157 | | #ifdef XP_WIN |
1158 | | // Native plugin windows used by this remote content need to be reparented. |
1159 | | if (nsPIDOMWindowOuter* newWin = ourDoc->GetWindow()) { |
1160 | | RefPtr<nsIWidget> newParent = nsGlobalWindowOuter::Cast(newWin)->GetMainWidget(); |
1161 | | const ManagedContainer<mozilla::plugins::PPluginWidgetParent>& plugins = |
1162 | | aOther->mRemoteBrowser->ManagedPPluginWidgetParent(); |
1163 | | for (auto iter = plugins.ConstIter(); !iter.Done(); iter.Next()) { |
1164 | | static_cast<mozilla::plugins::PluginWidgetParent*>(iter.Get()->GetKey())->SetParent(newParent); |
1165 | | } |
1166 | | } |
1167 | | #endif // XP_WIN |
1168 | |
|
1169 | 0 | MaybeUpdatePrimaryTabParent(eTabParentRemoved); |
1170 | 0 | aOther->MaybeUpdatePrimaryTabParent(eTabParentRemoved); |
1171 | 0 |
|
1172 | 0 | SetOwnerContent(otherContent); |
1173 | 0 | aOther->SetOwnerContent(ourContent); |
1174 | 0 |
|
1175 | 0 | mRemoteBrowser->SetOwnerElement(otherContent); |
1176 | 0 | aOther->mRemoteBrowser->SetOwnerElement(ourContent); |
1177 | 0 |
|
1178 | 0 | // Update window activation state for the swapped owner content. |
1179 | 0 | Unused << mRemoteBrowser->SendParentActivated( |
1180 | 0 | ParentWindowIsActive(otherContent->OwnerDoc())); |
1181 | 0 | Unused << aOther->mRemoteBrowser->SendParentActivated( |
1182 | 0 | ParentWindowIsActive(ourContent->OwnerDoc())); |
1183 | 0 |
|
1184 | 0 | MaybeUpdatePrimaryTabParent(eTabParentChanged); |
1185 | 0 | aOther->MaybeUpdatePrimaryTabParent(eTabParentChanged); |
1186 | 0 |
|
1187 | 0 | RefPtr<nsFrameMessageManager> ourMessageManager = mMessageManager; |
1188 | 0 | RefPtr<nsFrameMessageManager> otherMessageManager = aOther->mMessageManager; |
1189 | 0 | // Swap and setup things in parent message managers. |
1190 | 0 | if (ourMessageManager) { |
1191 | 0 | ourMessageManager->SetCallback(aOther); |
1192 | 0 | } |
1193 | 0 | if (otherMessageManager) { |
1194 | 0 | otherMessageManager->SetCallback(this); |
1195 | 0 | } |
1196 | 0 | mMessageManager.swap(aOther->mMessageManager); |
1197 | 0 |
|
1198 | 0 | // Perform the actual swap of the internal refptrs. We keep a strong reference |
1199 | 0 | // to ourselves to make sure we don't die while we overwrite our reference to |
1200 | 0 | // ourself. |
1201 | 0 | RefPtr<nsFrameLoader> kungFuDeathGrip(this); |
1202 | 0 | aThisOwner->InternalSetFrameLoader(aOther); |
1203 | 0 | aOtherOwner->InternalSetFrameLoader(kungFuDeathGrip); |
1204 | 0 |
|
1205 | 0 | ourFrameFrame->EndSwapDocShells(otherFrame); |
1206 | 0 |
|
1207 | 0 | ourShell->BackingScaleFactorChanged(); |
1208 | 0 | otherShell->BackingScaleFactorChanged(); |
1209 | 0 |
|
1210 | 0 | // Initialize browser API if needed now that owner content has changed. |
1211 | 0 | InitializeBrowserAPI(); |
1212 | 0 | aOther->InitializeBrowserAPI(); |
1213 | 0 |
|
1214 | 0 | mInSwap = aOther->mInSwap = false; |
1215 | 0 |
|
1216 | 0 | // Send an updated tab context since owner content type may have changed. |
1217 | 0 | MutableTabContext ourContext; |
1218 | 0 | rv = GetNewTabContext(&ourContext); |
1219 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
1220 | 0 | return rv; |
1221 | 0 | } |
1222 | 0 | MutableTabContext otherContext; |
1223 | 0 | rv = aOther->GetNewTabContext(&otherContext); |
1224 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
1225 | 0 | return rv; |
1226 | 0 | } |
1227 | 0 | |
1228 | 0 | // Swap the remoteType property as the frameloaders are being swapped |
1229 | 0 | nsAutoString ourRemoteType; |
1230 | 0 | if (!ourContent->GetAttr(kNameSpaceID_None, nsGkAtoms::RemoteType, |
1231 | 0 | ourRemoteType)) { |
1232 | 0 | ourRemoteType.AssignLiteral(DEFAULT_REMOTE_TYPE); |
1233 | 0 | } |
1234 | 0 | nsAutoString otherRemoteType; |
1235 | 0 | if (!otherContent->GetAttr(kNameSpaceID_None, nsGkAtoms::RemoteType, |
1236 | 0 | otherRemoteType)) { |
1237 | 0 | otherRemoteType.AssignLiteral(DEFAULT_REMOTE_TYPE); |
1238 | 0 | } |
1239 | 0 | ourContent->SetAttr(kNameSpaceID_None, nsGkAtoms::RemoteType, otherRemoteType, false); |
1240 | 0 | otherContent->SetAttr(kNameSpaceID_None, nsGkAtoms::RemoteType, ourRemoteType, false); |
1241 | 0 |
|
1242 | 0 | Unused << mRemoteBrowser->SendSwappedWithOtherRemoteLoader( |
1243 | 0 | ourContext.AsIPCTabContext()); |
1244 | 0 | Unused << aOther->mRemoteBrowser->SendSwappedWithOtherRemoteLoader( |
1245 | 0 | otherContext.AsIPCTabContext()); |
1246 | 0 | return NS_OK; |
1247 | 0 | } |
1248 | | |
1249 | | class MOZ_RAII AutoResetInFrameSwap final |
1250 | | { |
1251 | | public: |
1252 | | AutoResetInFrameSwap(nsFrameLoader* aThisFrameLoader, |
1253 | | nsFrameLoader* aOtherFrameLoader, |
1254 | | nsDocShell* aThisDocShell, |
1255 | | nsDocShell* aOtherDocShell, |
1256 | | EventTarget* aThisEventTarget, |
1257 | | EventTarget* aOtherEventTarget |
1258 | | MOZ_GUARD_OBJECT_NOTIFIER_PARAM) |
1259 | | : mThisFrameLoader(aThisFrameLoader) |
1260 | | , mOtherFrameLoader(aOtherFrameLoader) |
1261 | | , mThisDocShell(aThisDocShell) |
1262 | | , mOtherDocShell(aOtherDocShell) |
1263 | | , mThisEventTarget(aThisEventTarget) |
1264 | | , mOtherEventTarget(aOtherEventTarget) |
1265 | 0 | { |
1266 | 0 | MOZ_GUARD_OBJECT_NOTIFIER_INIT; |
1267 | 0 |
|
1268 | 0 | mThisFrameLoader->mInSwap = true; |
1269 | 0 | mOtherFrameLoader->mInSwap = true; |
1270 | 0 | mThisDocShell->SetInFrameSwap(true); |
1271 | 0 | mOtherDocShell->SetInFrameSwap(true); |
1272 | 0 |
|
1273 | 0 | // Fire pageshow events on still-loading pages, and then fire pagehide |
1274 | 0 | // events. Note that we do NOT fire these in the normal way, but just fire |
1275 | 0 | // them on the chrome event handlers. |
1276 | 0 | nsContentUtils::FirePageShowEvent(mThisDocShell, mThisEventTarget, false); |
1277 | 0 | nsContentUtils::FirePageShowEvent(mOtherDocShell, mOtherEventTarget, false); |
1278 | 0 | nsContentUtils::FirePageHideEvent(mThisDocShell, mThisEventTarget); |
1279 | 0 | nsContentUtils::FirePageHideEvent(mOtherDocShell, mOtherEventTarget); |
1280 | 0 | } |
1281 | | |
1282 | | ~AutoResetInFrameSwap() |
1283 | 0 | { |
1284 | 0 | nsContentUtils::FirePageShowEvent(mThisDocShell, mThisEventTarget, true); |
1285 | 0 | nsContentUtils::FirePageShowEvent(mOtherDocShell, mOtherEventTarget, true); |
1286 | 0 |
|
1287 | 0 | mThisFrameLoader->mInSwap = false; |
1288 | 0 | mOtherFrameLoader->mInSwap = false; |
1289 | 0 | mThisDocShell->SetInFrameSwap(false); |
1290 | 0 | mOtherDocShell->SetInFrameSwap(false); |
1291 | 0 | } |
1292 | | |
1293 | | private: |
1294 | | RefPtr<nsFrameLoader> mThisFrameLoader; |
1295 | | RefPtr<nsFrameLoader> mOtherFrameLoader; |
1296 | | RefPtr<nsDocShell> mThisDocShell; |
1297 | | RefPtr<nsDocShell> mOtherDocShell; |
1298 | | nsCOMPtr<EventTarget> mThisEventTarget; |
1299 | | nsCOMPtr<EventTarget> mOtherEventTarget; |
1300 | | MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER |
1301 | | }; |
1302 | | |
1303 | | nsresult |
1304 | | nsFrameLoader::SwapWithOtherLoader(nsFrameLoader* aOther, |
1305 | | nsIFrameLoaderOwner* aThisOwner, |
1306 | | nsIFrameLoaderOwner* aOtherOwner) |
1307 | 0 | { |
1308 | | #ifdef DEBUG |
1309 | | RefPtr<nsFrameLoader> first = aThisOwner->GetFrameLoader(); |
1310 | | RefPtr<nsFrameLoader> second = aOtherOwner->GetFrameLoader(); |
1311 | | MOZ_ASSERT(first == this, "aThisOwner must own this"); |
1312 | | MOZ_ASSERT(second == aOther, "aOtherOwner must own aOther"); |
1313 | | #endif |
1314 | |
|
1315 | 0 | NS_ENSURE_STATE(!mInShow && !aOther->mInShow); |
1316 | 0 |
|
1317 | 0 | if (IsRemoteFrame() != aOther->IsRemoteFrame()) { |
1318 | 0 | NS_WARNING("Swapping remote and non-remote frames is not currently supported"); |
1319 | 0 | return NS_ERROR_NOT_IMPLEMENTED; |
1320 | 0 | } |
1321 | 0 |
|
1322 | 0 | Element* ourContent = mOwnerContent; |
1323 | 0 | Element* otherContent = aOther->mOwnerContent; |
1324 | 0 |
|
1325 | 0 | if (!ourContent || !otherContent) { |
1326 | 0 | // Can't handle this |
1327 | 0 | return NS_ERROR_NOT_IMPLEMENTED; |
1328 | 0 | } |
1329 | 0 | |
1330 | 0 | bool ourHasSrcdoc = ourContent->IsHTMLElement(nsGkAtoms::iframe) && |
1331 | 0 | ourContent->HasAttr(kNameSpaceID_None, nsGkAtoms::srcdoc); |
1332 | 0 | bool otherHasSrcdoc = otherContent->IsHTMLElement(nsGkAtoms::iframe) && |
1333 | 0 | otherContent->HasAttr(kNameSpaceID_None, nsGkAtoms::srcdoc); |
1334 | 0 | if (ourHasSrcdoc || otherHasSrcdoc) { |
1335 | 0 | // Ignore this case entirely for now, since we support XUL <-> HTML swapping |
1336 | 0 | return NS_ERROR_NOT_IMPLEMENTED; |
1337 | 0 | } |
1338 | 0 | |
1339 | 0 | bool ourFullscreenAllowed = |
1340 | 0 | ourContent->IsXULElement() || |
1341 | 0 | (OwnerIsMozBrowserFrame() && |
1342 | 0 | (ourContent->HasAttr(kNameSpaceID_None, nsGkAtoms::allowfullscreen) || |
1343 | 0 | ourContent->HasAttr(kNameSpaceID_None, nsGkAtoms::mozallowfullscreen))); |
1344 | 0 | bool otherFullscreenAllowed = |
1345 | 0 | otherContent->IsXULElement() || |
1346 | 0 | (aOther->OwnerIsMozBrowserFrame() && |
1347 | 0 | (otherContent->HasAttr(kNameSpaceID_None, nsGkAtoms::allowfullscreen) || |
1348 | 0 | otherContent->HasAttr(kNameSpaceID_None, nsGkAtoms::mozallowfullscreen))); |
1349 | 0 | if (ourFullscreenAllowed != otherFullscreenAllowed) { |
1350 | 0 | return NS_ERROR_NOT_IMPLEMENTED; |
1351 | 0 | } |
1352 | 0 | |
1353 | 0 | bool ourPaymentRequestAllowed = |
1354 | 0 | ourContent->HasAttr(kNameSpaceID_None, nsGkAtoms::allowpaymentrequest); |
1355 | 0 | bool otherPaymentRequestAllowed = |
1356 | 0 | otherContent->HasAttr(kNameSpaceID_None, nsGkAtoms::allowpaymentrequest); |
1357 | 0 | if (ourPaymentRequestAllowed != otherPaymentRequestAllowed) { |
1358 | 0 | return NS_ERROR_NOT_IMPLEMENTED; |
1359 | 0 | } |
1360 | 0 | |
1361 | 0 | // Divert to a separate path for the remaining steps in the remote case |
1362 | 0 | if (IsRemoteFrame()) { |
1363 | 0 | MOZ_ASSERT(aOther->IsRemoteFrame()); |
1364 | 0 | return SwapWithOtherRemoteLoader(aOther, aThisOwner, aOtherOwner); |
1365 | 0 | } |
1366 | 0 |
|
1367 | 0 | // Make sure there are no same-origin issues |
1368 | 0 | bool equal; |
1369 | 0 | nsresult rv = |
1370 | 0 | ourContent->NodePrincipal()->Equals(otherContent->NodePrincipal(), &equal); |
1371 | 0 | if (NS_FAILED(rv) || !equal) { |
1372 | 0 | // Security problems loom. Just bail on it all |
1373 | 0 | return NS_ERROR_DOM_SECURITY_ERR; |
1374 | 0 | } |
1375 | 0 | |
1376 | 0 | RefPtr<nsDocShell> ourDocshell = static_cast<nsDocShell*>(GetExistingDocShell()); |
1377 | 0 | RefPtr<nsDocShell> otherDocshell = static_cast<nsDocShell*>(aOther->GetExistingDocShell()); |
1378 | 0 | if (!ourDocshell || !otherDocshell) { |
1379 | 0 | // How odd |
1380 | 0 | return NS_ERROR_NOT_IMPLEMENTED; |
1381 | 0 | } |
1382 | 0 | |
1383 | 0 | // To avoid having to mess with session history, avoid swapping |
1384 | 0 | // frameloaders that don't correspond to root same-type docshells, |
1385 | 0 | // unless both roots have session history disabled. |
1386 | 0 | nsCOMPtr<nsIDocShellTreeItem> ourRootTreeItem, otherRootTreeItem; |
1387 | 0 | ourDocshell->GetSameTypeRootTreeItem(getter_AddRefs(ourRootTreeItem)); |
1388 | 0 | otherDocshell->GetSameTypeRootTreeItem(getter_AddRefs(otherRootTreeItem)); |
1389 | 0 | nsCOMPtr<nsIWebNavigation> ourRootWebnav = |
1390 | 0 | do_QueryInterface(ourRootTreeItem); |
1391 | 0 | nsCOMPtr<nsIWebNavigation> otherRootWebnav = |
1392 | 0 | do_QueryInterface(otherRootTreeItem); |
1393 | 0 |
|
1394 | 0 | if (!ourRootWebnav || !otherRootWebnav) { |
1395 | 0 | return NS_ERROR_NOT_IMPLEMENTED; |
1396 | 0 | } |
1397 | 0 | |
1398 | 0 | RefPtr<ChildSHistory> ourHistory = ourRootWebnav->GetSessionHistory(); |
1399 | 0 | RefPtr<ChildSHistory> otherHistory = otherRootWebnav->GetSessionHistory(); |
1400 | 0 |
|
1401 | 0 | if ((ourRootTreeItem != ourDocshell || otherRootTreeItem != otherDocshell) && |
1402 | 0 | (ourHistory || otherHistory)) { |
1403 | 0 | return NS_ERROR_NOT_IMPLEMENTED; |
1404 | 0 | } |
1405 | 0 | |
1406 | 0 | // Also make sure that the two docshells are the same type. Otherwise |
1407 | 0 | // swapping is certainly not safe. If this needs to be changed then |
1408 | 0 | // the code below needs to be audited as it assumes identical types. |
1409 | 0 | int32_t ourType = ourDocshell->ItemType(); |
1410 | 0 | int32_t otherType = otherDocshell->ItemType(); |
1411 | 0 | if (ourType != otherType) { |
1412 | 0 | return NS_ERROR_NOT_IMPLEMENTED; |
1413 | 0 | } |
1414 | 0 | |
1415 | 0 | // One more twist here. Setting up the right treeowners in a heterogeneous |
1416 | 0 | // tree is a bit of a pain. So make sure that if ourType is not |
1417 | 0 | // nsIDocShellTreeItem::typeContent then all of our descendants are the same |
1418 | 0 | // type as us. |
1419 | 0 | if (ourType != nsIDocShellTreeItem::typeContent && |
1420 | 0 | (!AllDescendantsOfType(ourDocshell, ourType) || |
1421 | 0 | !AllDescendantsOfType(otherDocshell, otherType))) { |
1422 | 0 | return NS_ERROR_NOT_IMPLEMENTED; |
1423 | 0 | } |
1424 | 0 | |
1425 | 0 | // Save off the tree owners, frame elements, chrome event handlers, and |
1426 | 0 | // docshell and document parents before doing anything else. |
1427 | 0 | nsCOMPtr<nsIDocShellTreeOwner> ourOwner, otherOwner; |
1428 | 0 | ourDocshell->GetTreeOwner(getter_AddRefs(ourOwner)); |
1429 | 0 | otherDocshell->GetTreeOwner(getter_AddRefs(otherOwner)); |
1430 | 0 | // Note: it's OK to have null treeowners. |
1431 | 0 |
|
1432 | 0 | nsCOMPtr<nsIDocShellTreeItem> ourParentItem, otherParentItem; |
1433 | 0 | ourDocshell->GetParent(getter_AddRefs(ourParentItem)); |
1434 | 0 | otherDocshell->GetParent(getter_AddRefs(otherParentItem)); |
1435 | 0 | if (!ourParentItem || !otherParentItem) { |
1436 | 0 | return NS_ERROR_NOT_IMPLEMENTED; |
1437 | 0 | } |
1438 | 0 | |
1439 | 0 | // Make sure our parents are the same type too |
1440 | 0 | int32_t ourParentType = ourParentItem->ItemType(); |
1441 | 0 | int32_t otherParentType = otherParentItem->ItemType(); |
1442 | 0 | if (ourParentType != otherParentType) { |
1443 | 0 | return NS_ERROR_NOT_IMPLEMENTED; |
1444 | 0 | } |
1445 | 0 | |
1446 | 0 | nsCOMPtr<nsPIDOMWindowOuter> ourWindow = ourDocshell->GetWindow(); |
1447 | 0 | nsCOMPtr<nsPIDOMWindowOuter> otherWindow = otherDocshell->GetWindow(); |
1448 | 0 |
|
1449 | 0 | nsCOMPtr<Element> ourFrameElement = |
1450 | 0 | ourWindow->GetFrameElementInternal(); |
1451 | 0 | nsCOMPtr<Element> otherFrameElement = |
1452 | 0 | otherWindow->GetFrameElementInternal(); |
1453 | 0 |
|
1454 | 0 | nsCOMPtr<EventTarget> ourChromeEventHandler = ourWindow->GetChromeEventHandler(); |
1455 | 0 | nsCOMPtr<EventTarget> otherChromeEventHandler = otherWindow->GetChromeEventHandler(); |
1456 | 0 |
|
1457 | 0 | nsCOMPtr<EventTarget> ourEventTarget = ourWindow->GetParentTarget(); |
1458 | 0 | nsCOMPtr<EventTarget> otherEventTarget = otherWindow->GetParentTarget(); |
1459 | 0 |
|
1460 | 0 | NS_ASSERTION(SameCOMIdentity(ourFrameElement, ourContent) && |
1461 | 0 | SameCOMIdentity(otherFrameElement, otherContent) && |
1462 | 0 | SameCOMIdentity(ourChromeEventHandler, ourContent) && |
1463 | 0 | SameCOMIdentity(otherChromeEventHandler, otherContent), |
1464 | 0 | "How did that happen, exactly?"); |
1465 | 0 |
|
1466 | 0 | nsCOMPtr<nsIDocument> ourChildDocument = ourWindow->GetExtantDoc(); |
1467 | 0 | nsCOMPtr<nsIDocument> otherChildDocument = otherWindow ->GetExtantDoc(); |
1468 | 0 | if (!ourChildDocument || !otherChildDocument) { |
1469 | 0 | // This shouldn't be happening |
1470 | 0 | return NS_ERROR_NOT_IMPLEMENTED; |
1471 | 0 | } |
1472 | 0 | |
1473 | 0 | nsCOMPtr<nsIDocument> ourParentDocument = |
1474 | 0 | ourChildDocument->GetParentDocument(); |
1475 | 0 | nsCOMPtr<nsIDocument> otherParentDocument = |
1476 | 0 | otherChildDocument->GetParentDocument(); |
1477 | 0 |
|
1478 | 0 | // Make sure to swap docshells between the two frames. |
1479 | 0 | nsIDocument* ourDoc = ourContent->GetComposedDoc(); |
1480 | 0 | nsIDocument* otherDoc = otherContent->GetComposedDoc(); |
1481 | 0 | if (!ourDoc || !otherDoc) { |
1482 | 0 | // Again, how odd, given that we had docshells |
1483 | 0 | return NS_ERROR_NOT_IMPLEMENTED; |
1484 | 0 | } |
1485 | 0 | |
1486 | 0 | NS_ASSERTION(ourDoc == ourParentDocument, "Unexpected parent document"); |
1487 | 0 | NS_ASSERTION(otherDoc == otherParentDocument, "Unexpected parent document"); |
1488 | 0 |
|
1489 | 0 | nsIPresShell* ourShell = ourDoc->GetShell(); |
1490 | 0 | nsIPresShell* otherShell = otherDoc->GetShell(); |
1491 | 0 | if (!ourShell || !otherShell) { |
1492 | 0 | return NS_ERROR_NOT_IMPLEMENTED; |
1493 | 0 | } |
1494 | 0 | |
1495 | 0 | if (ourDocshell->GetIsIsolatedMozBrowserElement() != |
1496 | 0 | otherDocshell->GetIsIsolatedMozBrowserElement()) { |
1497 | 0 | return NS_ERROR_NOT_IMPLEMENTED; |
1498 | 0 | } |
1499 | 0 | |
1500 | 0 | // When we swap docShells, maybe we have to deal with a new page created just |
1501 | 0 | // for this operation. In this case, the browser code should already have set |
1502 | 0 | // the correct userContextId attribute value in the owning element, but our |
1503 | 0 | // docShell, that has been created way before) doesn't know that that |
1504 | 0 | // happened. |
1505 | 0 | // This is the reason why now we must retrieve the correct value from the |
1506 | 0 | // usercontextid attribute before comparing our originAttributes with the |
1507 | 0 | // other one. |
1508 | 0 | OriginAttributes ourOriginAttributes = |
1509 | 0 | ourDocshell->GetOriginAttributes(); |
1510 | 0 | rv = PopulateUserContextIdFromAttribute(ourOriginAttributes); |
1511 | 0 | NS_ENSURE_SUCCESS(rv,rv); |
1512 | 0 |
|
1513 | 0 | OriginAttributes otherOriginAttributes = |
1514 | 0 | otherDocshell->GetOriginAttributes(); |
1515 | 0 | rv = aOther->PopulateUserContextIdFromAttribute(otherOriginAttributes); |
1516 | 0 | NS_ENSURE_SUCCESS(rv,rv); |
1517 | 0 |
|
1518 | 0 | if (ourOriginAttributes != otherOriginAttributes) { |
1519 | 0 | return NS_ERROR_NOT_IMPLEMENTED; |
1520 | 0 | } |
1521 | 0 | |
1522 | 0 | if (mInSwap || aOther->mInSwap) { |
1523 | 0 | return NS_ERROR_NOT_IMPLEMENTED; |
1524 | 0 | } |
1525 | 0 | AutoResetInFrameSwap autoFrameSwap(this, aOther, ourDocshell, otherDocshell, |
1526 | 0 | ourEventTarget, otherEventTarget); |
1527 | 0 |
|
1528 | 0 | nsIFrame* ourFrame = ourContent->GetPrimaryFrame(); |
1529 | 0 | nsIFrame* otherFrame = otherContent->GetPrimaryFrame(); |
1530 | 0 | if (!ourFrame || !otherFrame) { |
1531 | 0 | return NS_ERROR_NOT_IMPLEMENTED; |
1532 | 0 | } |
1533 | 0 | |
1534 | 0 | nsSubDocumentFrame* ourFrameFrame = do_QueryFrame(ourFrame); |
1535 | 0 | if (!ourFrameFrame) { |
1536 | 0 | return NS_ERROR_NOT_IMPLEMENTED; |
1537 | 0 | } |
1538 | 0 | |
1539 | 0 | // OK. First begin to swap the docshells in the two nsIFrames |
1540 | 0 | rv = ourFrameFrame->BeginSwapDocShells(otherFrame); |
1541 | 0 | if (NS_FAILED(rv)) { |
1542 | 0 | return rv; |
1543 | 0 | } |
1544 | 0 | |
1545 | 0 | // Destroy browser frame scripts for content leaving a frame with browser API |
1546 | 0 | if (OwnerIsMozBrowserFrame() && !aOther->OwnerIsMozBrowserFrame()) { |
1547 | 0 | DestroyBrowserFrameScripts(); |
1548 | 0 | } |
1549 | 0 | if (!OwnerIsMozBrowserFrame() && aOther->OwnerIsMozBrowserFrame()) { |
1550 | 0 | aOther->DestroyBrowserFrameScripts(); |
1551 | 0 | } |
1552 | 0 |
|
1553 | 0 | // Now move the docshells to the right docshell trees. Note that this |
1554 | 0 | // resets their treeowners to null. |
1555 | 0 | ourParentItem->RemoveChild(ourDocshell); |
1556 | 0 | otherParentItem->RemoveChild(otherDocshell); |
1557 | 0 | if (ourType == nsIDocShellTreeItem::typeContent) { |
1558 | 0 | ourOwner->ContentShellRemoved(ourDocshell); |
1559 | 0 | otherOwner->ContentShellRemoved(otherDocshell); |
1560 | 0 | } |
1561 | 0 |
|
1562 | 0 | ourParentItem->AddChild(otherDocshell); |
1563 | 0 | otherParentItem->AddChild(ourDocshell); |
1564 | 0 |
|
1565 | 0 | // Restore the correct chrome event handlers. |
1566 | 0 | ourDocshell->SetChromeEventHandler(otherChromeEventHandler); |
1567 | 0 | otherDocshell->SetChromeEventHandler(ourChromeEventHandler); |
1568 | 0 | // Restore the correct treeowners |
1569 | 0 | // (and also chrome event handlers for content frames only). |
1570 | 0 | SetTreeOwnerAndChromeEventHandlerOnDocshellTree(ourDocshell, otherOwner, |
1571 | 0 | ourType == nsIDocShellTreeItem::typeContent ? otherChromeEventHandler.get() : nullptr); |
1572 | 0 | SetTreeOwnerAndChromeEventHandlerOnDocshellTree(otherDocshell, ourOwner, |
1573 | 0 | ourType == nsIDocShellTreeItem::typeContent ? ourChromeEventHandler.get() : nullptr); |
1574 | 0 |
|
1575 | 0 | // Switch the owner content before we start calling AddTreeItemToTreeOwner. |
1576 | 0 | // Note that we rely on this to deal with setting mObservingOwnerContent to |
1577 | 0 | // false and calling RemoveMutationObserver as needed. |
1578 | 0 | SetOwnerContent(otherContent); |
1579 | 0 | aOther->SetOwnerContent(ourContent); |
1580 | 0 |
|
1581 | 0 | AddTreeItemToTreeOwner(ourDocshell, otherOwner, otherParentType, nullptr); |
1582 | 0 | aOther->AddTreeItemToTreeOwner(otherDocshell, ourOwner, ourParentType, |
1583 | 0 | nullptr); |
1584 | 0 |
|
1585 | 0 | // SetSubDocumentFor nulls out parent documents on the old child doc if a |
1586 | 0 | // new non-null document is passed in, so just go ahead and remove both |
1587 | 0 | // kids before reinserting in the parent subdoc maps, to avoid |
1588 | 0 | // complications. |
1589 | 0 | ourParentDocument->SetSubDocumentFor(ourContent, nullptr); |
1590 | 0 | otherParentDocument->SetSubDocumentFor(otherContent, nullptr); |
1591 | 0 | ourParentDocument->SetSubDocumentFor(ourContent, otherChildDocument); |
1592 | 0 | otherParentDocument->SetSubDocumentFor(otherContent, ourChildDocument); |
1593 | 0 |
|
1594 | 0 | ourWindow->SetFrameElementInternal(otherFrameElement); |
1595 | 0 | otherWindow->SetFrameElementInternal(ourFrameElement); |
1596 | 0 |
|
1597 | 0 | RefPtr<nsFrameMessageManager> ourMessageManager = mMessageManager; |
1598 | 0 | RefPtr<nsFrameMessageManager> otherMessageManager = aOther->mMessageManager; |
1599 | 0 | // Swap pointers in child message managers. |
1600 | 0 | if (mChildMessageManager) { |
1601 | 0 | InProcessTabChildMessageManager* tabChild = mChildMessageManager; |
1602 | 0 | tabChild->SetOwner(otherContent); |
1603 | 0 | tabChild->SetChromeMessageManager(otherMessageManager); |
1604 | 0 | } |
1605 | 0 | if (aOther->mChildMessageManager) { |
1606 | 0 | InProcessTabChildMessageManager* otherTabChild = aOther->mChildMessageManager; |
1607 | 0 | otherTabChild->SetOwner(ourContent); |
1608 | 0 | otherTabChild->SetChromeMessageManager(ourMessageManager); |
1609 | 0 | } |
1610 | 0 | // Swap and setup things in parent message managers. |
1611 | 0 | if (mMessageManager) { |
1612 | 0 | mMessageManager->SetCallback(aOther); |
1613 | 0 | } |
1614 | 0 | if (aOther->mMessageManager) { |
1615 | 0 | aOther->mMessageManager->SetCallback(this); |
1616 | 0 | } |
1617 | 0 | mMessageManager.swap(aOther->mMessageManager); |
1618 | 0 |
|
1619 | 0 | // Perform the actual swap of the internal refptrs. We keep a strong reference |
1620 | 0 | // to ourselves to make sure we don't die while we overwrite our reference to |
1621 | 0 | // ourself. |
1622 | 0 | RefPtr<nsFrameLoader> kungFuDeathGrip(this); |
1623 | 0 | aThisOwner->InternalSetFrameLoader(aOther); |
1624 | 0 | aOtherOwner->InternalSetFrameLoader(kungFuDeathGrip); |
1625 | 0 |
|
1626 | 0 | // Drop any cached content viewers in the two session histories. |
1627 | 0 | if (ourHistory) { |
1628 | 0 | ourHistory->EvictLocalContentViewers(); |
1629 | 0 | } |
1630 | 0 | if (otherHistory) { |
1631 | 0 | otherHistory->EvictLocalContentViewers(); |
1632 | 0 | } |
1633 | 0 |
|
1634 | 0 | NS_ASSERTION(ourFrame == ourContent->GetPrimaryFrame() && |
1635 | 0 | otherFrame == otherContent->GetPrimaryFrame(), |
1636 | 0 | "changed primary frame"); |
1637 | 0 |
|
1638 | 0 | ourFrameFrame->EndSwapDocShells(otherFrame); |
1639 | 0 |
|
1640 | 0 | // If the content being swapped came from windows on two screens with |
1641 | 0 | // incompatible backing resolution (e.g. dragging a tab between windows on |
1642 | 0 | // hi-dpi and low-dpi screens), it will have style data that is based on |
1643 | 0 | // the wrong appUnitsPerDevPixel value. So we tell the PresShells that their |
1644 | 0 | // backing scale factor may have changed. (Bug 822266) |
1645 | 0 | ourShell->BackingScaleFactorChanged(); |
1646 | 0 | otherShell->BackingScaleFactorChanged(); |
1647 | 0 |
|
1648 | 0 | // Initialize browser API if needed now that owner content has changed |
1649 | 0 | InitializeBrowserAPI(); |
1650 | 0 | aOther->InitializeBrowserAPI(); |
1651 | 0 |
|
1652 | 0 | return NS_OK; |
1653 | 0 | } |
1654 | | |
1655 | | void |
1656 | | nsFrameLoader::Destroy() |
1657 | 0 | { |
1658 | 0 | StartDestroy(); |
1659 | 0 | } |
1660 | | |
1661 | | class nsFrameLoaderDestroyRunnable : public Runnable |
1662 | | { |
1663 | | enum DestroyPhase |
1664 | | { |
1665 | | // See the implementation of Run for an explanation of these phases. |
1666 | | eDestroyDocShell, |
1667 | | eWaitForUnloadMessage, |
1668 | | eDestroyComplete |
1669 | | }; |
1670 | | |
1671 | | RefPtr<nsFrameLoader> mFrameLoader; |
1672 | | DestroyPhase mPhase; |
1673 | | |
1674 | | public: |
1675 | | explicit nsFrameLoaderDestroyRunnable(nsFrameLoader* aFrameLoader) |
1676 | | : mozilla::Runnable("nsFrameLoaderDestroyRunnable") |
1677 | | , mFrameLoader(aFrameLoader) |
1678 | | , mPhase(eDestroyDocShell) |
1679 | 0 | { |
1680 | 0 | } |
1681 | | |
1682 | | NS_IMETHOD Run() override; |
1683 | | }; |
1684 | | |
1685 | | void |
1686 | | nsFrameLoader::StartDestroy() |
1687 | 0 | { |
1688 | 0 | // nsFrameLoader::StartDestroy is called just before the frameloader is |
1689 | 0 | // detached from the <browser> element. Destruction continues in phases via |
1690 | 0 | // the nsFrameLoaderDestroyRunnable. |
1691 | 0 |
|
1692 | 0 | if (mDestroyCalled) { |
1693 | 0 | return; |
1694 | 0 | } |
1695 | 0 | mDestroyCalled = true; |
1696 | 0 |
|
1697 | 0 | // After this point, we return an error when trying to send a message using |
1698 | 0 | // the message manager on the frame. |
1699 | 0 | if (mMessageManager) { |
1700 | 0 | mMessageManager->Close(); |
1701 | 0 | } |
1702 | 0 |
|
1703 | 0 | // Retain references to the <browser> element and the frameloader in case we |
1704 | 0 | // receive any messages from the message manager on the frame. These |
1705 | 0 | // references are dropped in DestroyComplete. |
1706 | 0 | if (mChildMessageManager || mRemoteBrowser) { |
1707 | 0 | mOwnerContentStrong = mOwnerContent; |
1708 | 0 | if (mRemoteBrowser) { |
1709 | 0 | mRemoteBrowser->CacheFrameLoader(this); |
1710 | 0 | } |
1711 | 0 | if (mChildMessageManager) { |
1712 | 0 | mChildMessageManager->CacheFrameLoader(this); |
1713 | 0 | } |
1714 | 0 | } |
1715 | 0 |
|
1716 | 0 | // If the TabParent has installed any event listeners on the window, this is |
1717 | 0 | // its last chance to remove them while we're still in the document. |
1718 | 0 | if (mRemoteBrowser) { |
1719 | 0 | mRemoteBrowser->RemoveWindowListeners(); |
1720 | 0 | } |
1721 | 0 |
|
1722 | 0 | nsCOMPtr<nsIDocument> doc; |
1723 | 0 | bool dynamicSubframeRemoval = false; |
1724 | 0 | if (mOwnerContent) { |
1725 | 0 | doc = mOwnerContent->OwnerDoc(); |
1726 | 0 | dynamicSubframeRemoval = !mIsTopLevelContent && !doc->InUnlinkOrDeletion(); |
1727 | 0 | doc->SetSubDocumentFor(mOwnerContent, nullptr); |
1728 | 0 | MaybeUpdatePrimaryTabParent(eTabParentRemoved); |
1729 | 0 | SetOwnerContent(nullptr); |
1730 | 0 | } |
1731 | 0 |
|
1732 | 0 | // Seems like this is a dynamic frame removal. |
1733 | 0 | if (dynamicSubframeRemoval) { |
1734 | 0 | if (mDocShell) { |
1735 | 0 | mDocShell->RemoveFromSessionHistory(); |
1736 | 0 | } |
1737 | 0 | } |
1738 | 0 |
|
1739 | 0 | // Let the tree owner know we're gone. |
1740 | 0 | if (mIsTopLevelContent) { |
1741 | 0 | if (mDocShell) { |
1742 | 0 | nsCOMPtr<nsIDocShellTreeItem> parentItem; |
1743 | 0 | mDocShell->GetParent(getter_AddRefs(parentItem)); |
1744 | 0 | nsCOMPtr<nsIDocShellTreeOwner> owner = do_GetInterface(parentItem); |
1745 | 0 | if (owner) { |
1746 | 0 | owner->ContentShellRemoved(mDocShell); |
1747 | 0 | } |
1748 | 0 | } |
1749 | 0 | } |
1750 | 0 |
|
1751 | 0 | // Let our window know that we are gone |
1752 | 0 | if (mDocShell) { |
1753 | 0 | nsCOMPtr<nsPIDOMWindowOuter> win_private(mDocShell->GetWindow()); |
1754 | 0 | if (win_private) { |
1755 | 0 | win_private->SetFrameElementInternal(nullptr); |
1756 | 0 | } |
1757 | 0 | } |
1758 | 0 |
|
1759 | 0 | nsCOMPtr<nsIRunnable> destroyRunnable = new nsFrameLoaderDestroyRunnable(this); |
1760 | 0 | if (mNeedsAsyncDestroy || !doc || |
1761 | 0 | NS_FAILED(doc->FinalizeFrameLoader(this, destroyRunnable))) { |
1762 | 0 | NS_DispatchToCurrentThread(destroyRunnable); |
1763 | 0 | } |
1764 | 0 | } |
1765 | | |
1766 | | nsresult |
1767 | | nsFrameLoaderDestroyRunnable::Run() |
1768 | 0 | { |
1769 | 0 | switch (mPhase) { |
1770 | 0 | case eDestroyDocShell: |
1771 | 0 | mFrameLoader->DestroyDocShell(); |
1772 | 0 |
|
1773 | 0 | // In the out-of-process case, TabParent will eventually call |
1774 | 0 | // DestroyComplete once it receives a __delete__ message from the child. In |
1775 | 0 | // the in-process case, we dispatch a series of runnables to ensure that |
1776 | 0 | // DestroyComplete gets called at the right time. The frame loader is kept |
1777 | 0 | // alive by mFrameLoader during this time. |
1778 | 0 | if (mFrameLoader->mChildMessageManager) { |
1779 | 0 | // When the docshell is destroyed, NotifyWindowIDDestroyed is called to |
1780 | 0 | // asynchronously notify {outer,inner}-window-destroyed via a runnable. We |
1781 | 0 | // don't want DestroyComplete to run until after those runnables have |
1782 | 0 | // run. Since we're enqueueing ourselves after the window-destroyed |
1783 | 0 | // runnables are enqueued, we're guaranteed to run after. |
1784 | 0 | mPhase = eWaitForUnloadMessage; |
1785 | 0 | NS_DispatchToCurrentThread(this); |
1786 | 0 | } |
1787 | 0 | break; |
1788 | 0 |
|
1789 | 0 | case eWaitForUnloadMessage: |
1790 | 0 | // The *-window-destroyed observers have finished running at this |
1791 | 0 | // point. However, it's possible that a *-window-destroyed observer might |
1792 | 0 | // have sent a message using the message manager. These messages might not |
1793 | 0 | // have been processed yet. So we enqueue ourselves again to ensure that |
1794 | 0 | // DestroyComplete runs after all messages sent by *-window-destroyed |
1795 | 0 | // observers have been processed. |
1796 | 0 | mPhase = eDestroyComplete; |
1797 | 0 | NS_DispatchToCurrentThread(this); |
1798 | 0 | break; |
1799 | 0 |
|
1800 | 0 | case eDestroyComplete: |
1801 | 0 | // Now that all messages sent by unload listeners and window destroyed |
1802 | 0 | // observers have been processed, we disconnect the message manager and |
1803 | 0 | // finish destruction. |
1804 | 0 | mFrameLoader->DestroyComplete(); |
1805 | 0 | break; |
1806 | 0 | } |
1807 | 0 | |
1808 | 0 | return NS_OK; |
1809 | 0 | } |
1810 | | |
1811 | | void |
1812 | | nsFrameLoader::DestroyDocShell() |
1813 | 0 | { |
1814 | 0 | // This code runs after the frameloader has been detached from the <browser> |
1815 | 0 | // element. We postpone this work because we may not be allowed to run |
1816 | 0 | // script at that time. |
1817 | 0 |
|
1818 | 0 | // Ask the TabChild to fire the frame script "unload" event, destroy its |
1819 | 0 | // docshell, and finally destroy the PBrowser actor. This eventually leads to |
1820 | 0 | // nsFrameLoader::DestroyComplete being called. |
1821 | 0 | if (mRemoteBrowser) { |
1822 | 0 | mRemoteBrowser->Destroy(); |
1823 | 0 | } |
1824 | 0 |
|
1825 | 0 | // Fire the "unload" event if we're in-process. |
1826 | 0 | if (mChildMessageManager) { |
1827 | 0 | mChildMessageManager->FireUnloadEvent(); |
1828 | 0 | } |
1829 | 0 |
|
1830 | 0 | // Destroy the docshell. |
1831 | 0 | nsCOMPtr<nsIBaseWindow> base_win(do_QueryInterface(mDocShell)); |
1832 | 0 | if (base_win) { |
1833 | 0 | base_win->Destroy(); |
1834 | 0 | } |
1835 | 0 | mDocShell = nullptr; |
1836 | 0 |
|
1837 | 0 | if (mChildMessageManager) { |
1838 | 0 | // Stop handling events in the in-process frame script. |
1839 | 0 | mChildMessageManager->DisconnectEventListeners(); |
1840 | 0 | } |
1841 | 0 | } |
1842 | | |
1843 | | void |
1844 | | nsFrameLoader::DestroyComplete() |
1845 | 0 | { |
1846 | 0 | // We get here, as part of StartDestroy, after the docshell has been destroyed |
1847 | 0 | // and all message manager messages sent during docshell destruction have been |
1848 | 0 | // dispatched. We also get here if the child process crashes. In the latter |
1849 | 0 | // case, StartDestroy might not have been called. |
1850 | 0 |
|
1851 | 0 | // Drop the strong references created in StartDestroy. |
1852 | 0 | if (mChildMessageManager || mRemoteBrowser) { |
1853 | 0 | mOwnerContentStrong = nullptr; |
1854 | 0 | if (mRemoteBrowser) { |
1855 | 0 | mRemoteBrowser->CacheFrameLoader(nullptr); |
1856 | 0 | } |
1857 | 0 | if (mChildMessageManager) { |
1858 | 0 | mChildMessageManager->CacheFrameLoader(nullptr); |
1859 | 0 | } |
1860 | 0 | } |
1861 | 0 |
|
1862 | 0 | // Call TabParent::Destroy if we haven't already (in case of a crash). |
1863 | 0 | if (mRemoteBrowser) { |
1864 | 0 | mRemoteBrowser->SetOwnerElement(nullptr); |
1865 | 0 | mRemoteBrowser->Destroy(); |
1866 | 0 | mRemoteBrowser = nullptr; |
1867 | 0 | } |
1868 | 0 |
|
1869 | 0 | if (mMessageManager) { |
1870 | 0 | mMessageManager->Disconnect(); |
1871 | 0 | } |
1872 | 0 |
|
1873 | 0 | if (mChildMessageManager) { |
1874 | 0 | mChildMessageManager->Disconnect(); |
1875 | 0 | } |
1876 | 0 |
|
1877 | 0 | mMessageManager = nullptr; |
1878 | 0 | mChildMessageManager = nullptr; |
1879 | 0 | } |
1880 | | |
1881 | | void |
1882 | | nsFrameLoader::SetOwnerContent(Element* aContent) |
1883 | 0 | { |
1884 | 0 | if (mObservingOwnerContent) { |
1885 | 0 | mObservingOwnerContent = false; |
1886 | 0 | mOwnerContent->RemoveMutationObserver(this); |
1887 | 0 | } |
1888 | 0 | mOwnerContent = aContent; |
1889 | 0 |
|
1890 | 0 | AutoJSAPI jsapi; |
1891 | 0 | jsapi.Init(); |
1892 | 0 |
|
1893 | 0 | JS::RootedObject wrapper(jsapi.cx(), GetWrapper()); |
1894 | 0 | if (wrapper) { |
1895 | 0 | JSAutoRealm ar(jsapi.cx(), wrapper); |
1896 | 0 | IgnoredErrorResult rv; |
1897 | 0 | ReparentWrapper(jsapi.cx(), wrapper, rv); |
1898 | 0 | Unused << NS_WARN_IF(rv.Failed()); |
1899 | 0 | } |
1900 | 0 |
|
1901 | 0 | if (RenderFrameParent* rfp = GetCurrentRenderFrame()) { |
1902 | 0 | rfp->OwnerContentChanged(aContent); |
1903 | 0 | } |
1904 | 0 | } |
1905 | | |
1906 | | bool |
1907 | | nsFrameLoader::OwnerIsMozBrowserFrame() |
1908 | 0 | { |
1909 | 0 | nsCOMPtr<nsIMozBrowserFrame> browserFrame = do_QueryInterface(mOwnerContent); |
1910 | 0 | return browserFrame ? browserFrame->GetReallyIsBrowser() : false; |
1911 | 0 | } |
1912 | | |
1913 | | bool |
1914 | | nsFrameLoader::OwnerIsIsolatedMozBrowserFrame() |
1915 | 0 | { |
1916 | 0 | nsCOMPtr<nsIMozBrowserFrame> browserFrame = do_QueryInterface(mOwnerContent); |
1917 | 0 | if (!browserFrame) { |
1918 | 0 | return false; |
1919 | 0 | } |
1920 | 0 | |
1921 | 0 | if (!OwnerIsMozBrowserFrame()) { |
1922 | 0 | return false; |
1923 | 0 | } |
1924 | 0 | |
1925 | 0 | bool isolated = browserFrame->GetIsolated(); |
1926 | 0 | if (isolated) { |
1927 | 0 | return true; |
1928 | 0 | } |
1929 | 0 | |
1930 | 0 | return false; |
1931 | 0 | } |
1932 | | |
1933 | | bool |
1934 | | nsFrameLoader::ShouldUseRemoteProcess() |
1935 | 0 | { |
1936 | 0 | if (IsForJSPlugin()) { |
1937 | 0 | return true; |
1938 | 0 | } |
1939 | 0 | |
1940 | 0 | if (PR_GetEnv("MOZ_DISABLE_OOP_TABS") || |
1941 | 0 | Preferences::GetBool("dom.ipc.tabs.disabled", false)) { |
1942 | 0 | return false; |
1943 | 0 | } |
1944 | 0 | |
1945 | 0 | // Don't try to launch nested children if we don't have OMTC. |
1946 | 0 | // They won't render! |
1947 | 0 | if (XRE_IsContentProcess() && |
1948 | 0 | !CompositorBridgeChild::ChildProcessHasCompositorBridge()) { |
1949 | 0 | return false; |
1950 | 0 | } |
1951 | 0 | |
1952 | 0 | if (XRE_IsContentProcess() && |
1953 | 0 | !(PR_GetEnv("MOZ_NESTED_OOP_TABS") || |
1954 | 0 | Preferences::GetBool("dom.ipc.tabs.nested.enabled", false))) { |
1955 | 0 | return false; |
1956 | 0 | } |
1957 | 0 | |
1958 | 0 | // If we're an <iframe mozbrowser> and we don't have a "remote" attribute, |
1959 | 0 | // fall back to the default. |
1960 | 0 | if (OwnerIsMozBrowserFrame() && |
1961 | 0 | !mOwnerContent->HasAttr(kNameSpaceID_None, nsGkAtoms::remote)) { |
1962 | 0 |
|
1963 | 0 | return Preferences::GetBool("dom.ipc.browser_frames.oop_by_default", false); |
1964 | 0 | } |
1965 | 0 | |
1966 | 0 | // Otherwise, we're remote if we have "remote=true" and we're either a |
1967 | 0 | // browser frame or a XUL element. |
1968 | 0 | return (OwnerIsMozBrowserFrame() || |
1969 | 0 | mOwnerContent->GetNameSpaceID() == kNameSpaceID_XUL) && |
1970 | 0 | mOwnerContent->AttrValueIs(kNameSpaceID_None, |
1971 | 0 | nsGkAtoms::remote, |
1972 | 0 | nsGkAtoms::_true, |
1973 | 0 | eCaseMatters); |
1974 | 0 | } |
1975 | | |
1976 | | bool |
1977 | | nsFrameLoader::IsRemoteFrame() |
1978 | 0 | { |
1979 | 0 | if (mRemoteFrame) { |
1980 | 0 | MOZ_ASSERT(!mDocShell, "Found a remote frame with a DocShell"); |
1981 | 0 | return true; |
1982 | 0 | } |
1983 | 0 | return false; |
1984 | 0 | } |
1985 | | |
1986 | | nsresult |
1987 | | nsFrameLoader::MaybeCreateDocShell() |
1988 | 0 | { |
1989 | 0 | if (mDocShell) { |
1990 | 0 | return NS_OK; |
1991 | 0 | } |
1992 | 0 | if (IsRemoteFrame()) { |
1993 | 0 | return NS_OK; |
1994 | 0 | } |
1995 | 0 | NS_ENSURE_STATE(!mDestroyCalled); |
1996 | 0 |
|
1997 | 0 | // Get our parent docshell off the document of mOwnerContent |
1998 | 0 | // XXXbz this is such a total hack.... We really need to have a |
1999 | 0 | // better setup for doing this. |
2000 | 0 | nsIDocument* doc = mOwnerContent->OwnerDoc(); |
2001 | 0 |
|
2002 | 0 | MOZ_RELEASE_ASSERT(!doc->IsResourceDoc(), "We shouldn't even exist"); |
2003 | 0 |
|
2004 | 0 | // Check if the document still has a window since it is possible for an |
2005 | 0 | // iframe to be inserted and cause the creation of the docshell in a |
2006 | 0 | // partially unloaded document (see Bug 1305237 comment 127). |
2007 | 0 | if (!doc->IsStaticDocument() && |
2008 | 0 | (!doc->GetWindow() || !mOwnerContent->IsInComposedDoc())) { |
2009 | 0 | return NS_ERROR_UNEXPECTED; |
2010 | 0 | } |
2011 | 0 | |
2012 | 0 | if (!doc->IsActive()) { |
2013 | 0 | // Don't allow subframe loads in non-active documents. |
2014 | 0 | // (See bug 610571 comment 5.) |
2015 | 0 | return NS_ERROR_NOT_AVAILABLE; |
2016 | 0 | } |
2017 | 0 | |
2018 | 0 | nsCOMPtr<nsIDocShell> parentDocShell = doc->GetDocShell(); |
2019 | 0 | nsCOMPtr<nsIWebNavigation> parentAsWebNav = do_QueryInterface(parentDocShell); |
2020 | 0 | NS_ENSURE_STATE(parentAsWebNav); |
2021 | 0 |
|
2022 | 0 | // Create the docshell... |
2023 | 0 | mDocShell = do_CreateInstance("@mozilla.org/docshell;1"); |
2024 | 0 | NS_ENSURE_TRUE(mDocShell, NS_ERROR_FAILURE); |
2025 | 0 |
|
2026 | 0 | if (!mNetworkCreated) { |
2027 | 0 | if (mDocShell) { |
2028 | 0 | mDocShell->SetCreatedDynamically(true); |
2029 | 0 | } |
2030 | 0 | } |
2031 | 0 |
|
2032 | 0 | // Get the frame name and tell the docshell about it. |
2033 | 0 | NS_ENSURE_TRUE(mDocShell, NS_ERROR_FAILURE); |
2034 | 0 | nsAutoString frameName; |
2035 | 0 |
|
2036 | 0 | int32_t namespaceID = mOwnerContent->GetNameSpaceID(); |
2037 | 0 | if (namespaceID == kNameSpaceID_XHTML && !mOwnerContent->IsInHTMLDocument()) { |
2038 | 0 | mOwnerContent->GetAttr(kNameSpaceID_None, nsGkAtoms::id, frameName); |
2039 | 0 | } else { |
2040 | 0 | mOwnerContent->GetAttr(kNameSpaceID_None, nsGkAtoms::name, frameName); |
2041 | 0 | // XXX if no NAME then use ID, after a transition period this will be |
2042 | 0 | // changed so that XUL only uses ID too (bug 254284). |
2043 | 0 | if (frameName.IsEmpty() && namespaceID == kNameSpaceID_XUL) { |
2044 | 0 | mOwnerContent->GetAttr(kNameSpaceID_None, nsGkAtoms::id, frameName); |
2045 | 0 | } |
2046 | 0 | } |
2047 | 0 |
|
2048 | 0 | if (!frameName.IsEmpty()) { |
2049 | 0 | mDocShell->SetName(frameName); |
2050 | 0 | } |
2051 | 0 |
|
2052 | 0 | // Inform our docShell that it has a new child. |
2053 | 0 | // Note: This logic duplicates a lot of logic in |
2054 | 0 | // nsSubDocumentFrame::AttributeChanged. We should fix that. |
2055 | 0 |
|
2056 | 0 | const int32_t parentType = parentDocShell->ItemType(); |
2057 | 0 |
|
2058 | 0 | // XXXbz why is this in content code, exactly? We should handle |
2059 | 0 | // this some other way..... Not sure how yet. |
2060 | 0 | nsCOMPtr<nsIDocShellTreeOwner> parentTreeOwner; |
2061 | 0 | parentDocShell->GetTreeOwner(getter_AddRefs(parentTreeOwner)); |
2062 | 0 | NS_ENSURE_STATE(parentTreeOwner); |
2063 | 0 | mIsTopLevelContent = |
2064 | 0 | AddTreeItemToTreeOwner(mDocShell, parentTreeOwner, parentType, |
2065 | 0 | parentDocShell); |
2066 | 0 |
|
2067 | 0 | if (mIsTopLevelContent) { |
2068 | 0 | mDocShell->SetCreatedDynamically(false); |
2069 | 0 | } |
2070 | 0 |
|
2071 | 0 | // Make sure all shells have links back to the content element |
2072 | 0 | // in the nearest enclosing chrome shell. |
2073 | 0 | RefPtr<EventTarget> chromeEventHandler; |
2074 | 0 |
|
2075 | 0 | if (parentType == nsIDocShellTreeItem::typeChrome) { |
2076 | 0 | // Our parent shell is a chrome shell. It is therefore our nearest |
2077 | 0 | // enclosing chrome shell. |
2078 | 0 |
|
2079 | 0 | chromeEventHandler = mOwnerContent; |
2080 | 0 | NS_ASSERTION(chromeEventHandler, |
2081 | 0 | "This mContent should implement this."); |
2082 | 0 | } else { |
2083 | 0 | // Our parent shell is a content shell. Get the chrome event |
2084 | 0 | // handler from it and use that for our shell as well. |
2085 | 0 |
|
2086 | 0 | parentDocShell->GetChromeEventHandler(getter_AddRefs(chromeEventHandler)); |
2087 | 0 | } |
2088 | 0 |
|
2089 | 0 | mDocShell->SetChromeEventHandler(chromeEventHandler); |
2090 | 0 |
|
2091 | 0 | // This is nasty, this code (the mDocShell->GetWindow() below) |
2092 | 0 | // *must* come *after* the above call to |
2093 | 0 | // mDocShell->SetChromeEventHandler() for the global window to get |
2094 | 0 | // the right chrome event handler. |
2095 | 0 |
|
2096 | 0 | // Tell the window about the frame that hosts it. |
2097 | 0 | nsCOMPtr<Element> frame_element = mOwnerContent; |
2098 | 0 | NS_ASSERTION(frame_element, "frame loader owner element not a DOM element!"); |
2099 | 0 |
|
2100 | 0 | nsCOMPtr<nsPIDOMWindowOuter> win_private(mDocShell->GetWindow()); |
2101 | 0 | nsCOMPtr<nsIBaseWindow> base_win(do_QueryInterface(mDocShell)); |
2102 | 0 | if (win_private) { |
2103 | 0 | win_private->SetFrameElementInternal(frame_element); |
2104 | 0 |
|
2105 | 0 | // Set the opener window if we have one provided here |
2106 | 0 | if (mOpener) { |
2107 | 0 | win_private->SetOpenerWindow(mOpener, true); |
2108 | 0 | mOpener = nullptr; |
2109 | 0 | } |
2110 | 0 | } |
2111 | 0 |
|
2112 | 0 | // Allow scripts to close the docshell if specified. |
2113 | 0 | if (win_private && mOwnerContent->IsXULElement(nsGkAtoms::browser) && |
2114 | 0 | mOwnerContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::allowscriptstoclose, |
2115 | 0 | nsGkAtoms::_true, eCaseMatters)) { |
2116 | 0 | nsGlobalWindowOuter::Cast(win_private)->AllowScriptsToClose(); |
2117 | 0 | } |
2118 | 0 |
|
2119 | 0 | // This is kinda whacky, this call doesn't really create anything, |
2120 | 0 | // but it must be called to make sure things are properly |
2121 | 0 | // initialized. |
2122 | 0 | if (NS_FAILED(base_win->Create()) || !win_private) { |
2123 | 0 | // Do not call Destroy() here. See bug 472312. |
2124 | 0 | NS_WARNING("Something wrong when creating the docshell for a frameloader!"); |
2125 | 0 | return NS_ERROR_FAILURE; |
2126 | 0 | } |
2127 | 0 |
|
2128 | 0 | // If we are an in-process browser, we want to set up our session history. We |
2129 | 0 | // do this by creating both the child SHistory (which is in the nsDocShell), |
2130 | 0 | // and creating the corresponding in-process ParentSHistory. |
2131 | 0 | if (mIsTopLevelContent && |
2132 | 0 | mOwnerContent->IsXULElement(nsGkAtoms::browser) && |
2133 | 0 | !mOwnerContent->HasAttr(kNameSpaceID_None, nsGkAtoms::disablehistory)) { |
2134 | 0 | // XXX(nika): Set this up more explicitly? |
2135 | 0 | nsresult rv = mDocShell->InitSessionHistory(); |
2136 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
2137 | 0 | mParentSHistory = new ParentSHistory(this); |
2138 | 0 | } |
2139 | 0 |
|
2140 | 0 | OriginAttributes attrs; |
2141 | 0 | if (parentDocShell->ItemType() == mDocShell->ItemType()) { |
2142 | 0 | attrs = nsDocShell::Cast(parentDocShell)->GetOriginAttributes(); |
2143 | 0 | } |
2144 | 0 |
|
2145 | 0 | // Inherit origin attributes from parent document if |
2146 | 0 | // 1. It's in a content docshell. |
2147 | 0 | // 2. its nodePrincipal is not a SystemPrincipal. |
2148 | 0 | // 3. It's not a mozbrowser frame. |
2149 | 0 | // |
2150 | 0 | // For example, firstPartyDomain is computed from top-level document, it |
2151 | 0 | // doesn't exist in the top-level docshell. |
2152 | 0 | if (parentType == nsIDocShellTreeItem::typeContent && |
2153 | 0 | !nsContentUtils::IsSystemPrincipal(doc->NodePrincipal()) && |
2154 | 0 | !OwnerIsMozBrowserFrame()) { |
2155 | 0 | OriginAttributes oa = doc->NodePrincipal()->OriginAttributesRef(); |
2156 | 0 |
|
2157 | 0 | // Assert on the firstPartyDomain from top-level docshell should be empty |
2158 | 0 | MOZ_ASSERT_IF(mIsTopLevelContent, attrs.mFirstPartyDomain.IsEmpty()); |
2159 | 0 |
|
2160 | 0 | // So far we want to make sure Inherit doesn't override any other origin |
2161 | 0 | // attribute than firstPartyDomain. |
2162 | 0 | MOZ_ASSERT(attrs.mAppId == oa.mAppId, |
2163 | 0 | "docshell and document should have the same appId attribute."); |
2164 | 0 | MOZ_ASSERT(attrs.mUserContextId == oa.mUserContextId, |
2165 | 0 | "docshell and document should have the same userContextId attribute."); |
2166 | 0 | MOZ_ASSERT(attrs.mInIsolatedMozBrowser == oa.mInIsolatedMozBrowser, |
2167 | 0 | "docshell and document should have the same inIsolatedMozBrowser attribute."); |
2168 | 0 | MOZ_ASSERT(attrs.mPrivateBrowsingId == oa.mPrivateBrowsingId, |
2169 | 0 | "docshell and document should have the same privateBrowsingId attribute."); |
2170 | 0 |
|
2171 | 0 | attrs = oa; |
2172 | 0 | } |
2173 | 0 |
|
2174 | 0 | if (OwnerIsMozBrowserFrame()) { |
2175 | 0 | attrs.mAppId = nsIScriptSecurityManager::NO_APP_ID; |
2176 | 0 | attrs.mInIsolatedMozBrowser = OwnerIsIsolatedMozBrowserFrame(); |
2177 | 0 | mDocShell->SetFrameType(nsIDocShell::FRAME_TYPE_BROWSER); |
2178 | 0 | } |
2179 | 0 |
|
2180 | 0 | // Apply sandbox flags even if our owner is not an iframe, as this copies |
2181 | 0 | // flags from our owning content's owning document. |
2182 | 0 | // Note: ApplySandboxFlags should be called after mDocShell->SetFrameType |
2183 | 0 | // because we need to get the correct presentation URL in ApplySandboxFlags. |
2184 | 0 | uint32_t sandboxFlags = 0; |
2185 | 0 | HTMLIFrameElement* iframe = HTMLIFrameElement::FromNode(mOwnerContent); |
2186 | 0 | if (iframe) { |
2187 | 0 | sandboxFlags = iframe->GetSandboxFlags(); |
2188 | 0 | } |
2189 | 0 | ApplySandboxFlags(sandboxFlags); |
2190 | 0 |
|
2191 | 0 | // Grab the userContextId from owner |
2192 | 0 | nsresult rv = PopulateUserContextIdFromAttribute(attrs); |
2193 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
2194 | 0 | return rv; |
2195 | 0 | } |
2196 | 0 | |
2197 | 0 | bool isPrivate = false; |
2198 | 0 | nsCOMPtr<nsILoadContext> parentContext = do_QueryInterface(parentDocShell); |
2199 | 0 | NS_ENSURE_STATE(parentContext); |
2200 | 0 |
|
2201 | 0 | rv = parentContext->GetUsePrivateBrowsing(&isPrivate); |
2202 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
2203 | 0 | return rv; |
2204 | 0 | } |
2205 | 0 | attrs.SyncAttributesWithPrivateBrowsing(isPrivate); |
2206 | 0 |
|
2207 | 0 | if (OwnerIsMozBrowserFrame()) { |
2208 | 0 | // For inproc frames, set the docshell properties. |
2209 | 0 | nsAutoString name; |
2210 | 0 | if (mOwnerContent->GetAttr(kNameSpaceID_None, nsGkAtoms::name, name)) { |
2211 | 0 | mDocShell->SetName(name); |
2212 | 0 | } |
2213 | 0 | mDocShell->SetFullscreenAllowed( |
2214 | 0 | mOwnerContent->HasAttr(kNameSpaceID_None, nsGkAtoms::allowfullscreen) || |
2215 | 0 | mOwnerContent->HasAttr(kNameSpaceID_None, nsGkAtoms::mozallowfullscreen)); |
2216 | 0 | bool isPrivate = mOwnerContent->HasAttr(kNameSpaceID_None, nsGkAtoms::mozprivatebrowsing); |
2217 | 0 | if (isPrivate) { |
2218 | 0 | if (mDocShell->GetHasLoadedNonBlankURI()) { |
2219 | 0 | nsContentUtils::ReportToConsoleNonLocalized( |
2220 | 0 | NS_LITERAL_STRING("We should not switch to Private Browsing after loading a document."), |
2221 | 0 | nsIScriptError::warningFlag, |
2222 | 0 | NS_LITERAL_CSTRING("mozprivatebrowsing"), |
2223 | 0 | nullptr); |
2224 | 0 | } else { |
2225 | 0 | // This handles the case where a frames private browsing is set by chrome flags |
2226 | 0 | // and not inherited by its parent. |
2227 | 0 | attrs.SyncAttributesWithPrivateBrowsing(isPrivate); |
2228 | 0 | } |
2229 | 0 | } |
2230 | 0 | } |
2231 | 0 |
|
2232 | 0 | nsDocShell::Cast(mDocShell)->SetOriginAttributes(attrs); |
2233 | 0 |
|
2234 | 0 | // Typically there will be a window, however for some cases such as printing |
2235 | 0 | // the document is cloned with a docshell that has no window. We check |
2236 | 0 | // that the window exists to ensure we don't try to gather ancestors for |
2237 | 0 | // those cases. |
2238 | 0 | nsCOMPtr<nsPIDOMWindowOuter> win = doc->GetWindow(); |
2239 | 0 | if (!mDocShell->GetIsMozBrowser() && |
2240 | 0 | parentType == mDocShell->ItemType() && |
2241 | 0 | !doc->IsStaticDocument() && win) { |
2242 | 0 | // Propagate through the ancestor principals. |
2243 | 0 | nsTArray<nsCOMPtr<nsIPrincipal>> ancestorPrincipals; |
2244 | 0 | // Make a copy, so we can modify it. |
2245 | 0 | ancestorPrincipals = doc->AncestorPrincipals(); |
2246 | 0 | ancestorPrincipals.InsertElementAt(0, doc->NodePrincipal()); |
2247 | 0 | nsDocShell::Cast(mDocShell)->SetAncestorPrincipals(std::move(ancestorPrincipals)); |
2248 | 0 |
|
2249 | 0 | // Repeat for outer window IDs. |
2250 | 0 | nsTArray<uint64_t> ancestorOuterWindowIDs; |
2251 | 0 | ancestorOuterWindowIDs = doc->AncestorOuterWindowIDs(); |
2252 | 0 | ancestorOuterWindowIDs.InsertElementAt(0, win->WindowID()); |
2253 | 0 | nsDocShell::Cast(mDocShell)->SetAncestorOuterWindowIDs(std::move(ancestorOuterWindowIDs)); |
2254 | 0 | } |
2255 | 0 |
|
2256 | 0 | ReallyLoadFrameScripts(); |
2257 | 0 | InitializeBrowserAPI(); |
2258 | 0 |
|
2259 | 0 | nsCOMPtr<nsIObserverService> os = services::GetObserverService(); |
2260 | 0 | if (os) { |
2261 | 0 | os->NotifyObservers(ToSupports(this), |
2262 | 0 | "inprocess-browser-shown", nullptr); |
2263 | 0 | } |
2264 | 0 |
|
2265 | 0 | return NS_OK; |
2266 | 0 | } |
2267 | | |
2268 | | void |
2269 | | nsFrameLoader::GetURL(nsString& aURI, nsIPrincipal** aTriggeringPrincipal) |
2270 | 0 | { |
2271 | 0 | aURI.Truncate(); |
2272 | 0 |
|
2273 | 0 | if (mOwnerContent->IsHTMLElement(nsGkAtoms::object)) { |
2274 | 0 | mOwnerContent->GetAttr(kNameSpaceID_None, nsGkAtoms::data, aURI); |
2275 | 0 | } else { |
2276 | 0 | mOwnerContent->GetAttr(kNameSpaceID_None, nsGkAtoms::src, aURI); |
2277 | 0 | if (RefPtr<nsGenericHTMLFrameElement> frame = do_QueryObject(mOwnerContent)) { |
2278 | 0 | nsCOMPtr<nsIPrincipal> prin = frame->GetSrcTriggeringPrincipal(); |
2279 | 0 | prin.forget(aTriggeringPrincipal); |
2280 | 0 | } |
2281 | 0 | } |
2282 | 0 | } |
2283 | | |
2284 | | nsresult |
2285 | | nsFrameLoader::CheckForRecursiveLoad(nsIURI* aURI) |
2286 | 0 | { |
2287 | 0 | nsresult rv; |
2288 | 0 |
|
2289 | 0 | MOZ_ASSERT(!IsRemoteFrame(), |
2290 | 0 | "Shouldn't call CheckForRecursiveLoad on remote frames."); |
2291 | 0 |
|
2292 | 0 | mDepthTooGreat = false; |
2293 | 0 | rv = MaybeCreateDocShell(); |
2294 | 0 | if (NS_FAILED(rv)) { |
2295 | 0 | return rv; |
2296 | 0 | } |
2297 | 0 | NS_ASSERTION(mDocShell, |
2298 | 0 | "MaybeCreateDocShell succeeded, but null mDocShell"); |
2299 | 0 | if (!mDocShell) { |
2300 | 0 | return NS_ERROR_FAILURE; |
2301 | 0 | } |
2302 | 0 | |
2303 | 0 | // Check that we're still in the docshell tree. |
2304 | 0 | nsCOMPtr<nsIDocShellTreeOwner> treeOwner; |
2305 | 0 | mDocShell->GetTreeOwner(getter_AddRefs(treeOwner)); |
2306 | 0 | NS_WARNING_ASSERTION(treeOwner, |
2307 | 0 | "Trying to load a new url to a docshell without owner!"); |
2308 | 0 | NS_ENSURE_STATE(treeOwner); |
2309 | 0 |
|
2310 | 0 | if (mDocShell->ItemType() != nsIDocShellTreeItem::typeContent) { |
2311 | 0 | // No need to do recursion-protection here XXXbz why not?? Do we really |
2312 | 0 | // trust people not to screw up with non-content docshells? |
2313 | 0 | return NS_OK; |
2314 | 0 | } |
2315 | 0 | |
2316 | 0 | // Bug 8065: Don't exceed some maximum depth in content frames |
2317 | 0 | // (MAX_DEPTH_CONTENT_FRAMES) |
2318 | 0 | nsCOMPtr<nsIDocShellTreeItem> parentAsItem; |
2319 | 0 | mDocShell->GetSameTypeParent(getter_AddRefs(parentAsItem)); |
2320 | 0 | int32_t depth = 0; |
2321 | 0 | while (parentAsItem) { |
2322 | 0 | ++depth; |
2323 | 0 |
|
2324 | 0 | if (depth >= MAX_DEPTH_CONTENT_FRAMES) { |
2325 | 0 | mDepthTooGreat = true; |
2326 | 0 | NS_WARNING("Too many nested content frames so giving up"); |
2327 | 0 |
|
2328 | 0 | return NS_ERROR_UNEXPECTED; // Too deep, give up! (silently?) |
2329 | 0 | } |
2330 | 0 |
|
2331 | 0 | nsCOMPtr<nsIDocShellTreeItem> temp; |
2332 | 0 | temp.swap(parentAsItem); |
2333 | 0 | temp->GetSameTypeParent(getter_AddRefs(parentAsItem)); |
2334 | 0 | } |
2335 | 0 |
|
2336 | 0 | // Bug 136580: Check for recursive frame loading excluding about:srcdoc URIs. |
2337 | 0 | // srcdoc URIs require their contents to be specified inline, so it isn't |
2338 | 0 | // possible for undesirable recursion to occur without the aid of a |
2339 | 0 | // non-srcdoc URI, which this method will block normally. |
2340 | 0 | // Besides, URI is not enough to guarantee uniqueness of srcdoc documents. |
2341 | 0 | nsAutoCString buffer; |
2342 | 0 | rv = aURI->GetScheme(buffer); |
2343 | 0 | if (NS_SUCCEEDED(rv) && buffer.EqualsLiteral("about")) { |
2344 | 0 | rv = aURI->GetPathQueryRef(buffer); |
2345 | 0 | if (NS_SUCCEEDED(rv) && buffer.EqualsLiteral("srcdoc")) { |
2346 | 0 | // Duplicates allowed up to depth limits |
2347 | 0 | return NS_OK; |
2348 | 0 | } |
2349 | 0 | } |
2350 | 0 | int32_t matchCount = 0; |
2351 | 0 | mDocShell->GetSameTypeParent(getter_AddRefs(parentAsItem)); |
2352 | 0 | while (parentAsItem) { |
2353 | 0 | // Check the parent URI with the URI we're loading |
2354 | 0 | nsCOMPtr<nsIWebNavigation> parentAsNav(do_QueryInterface(parentAsItem)); |
2355 | 0 | if (parentAsNav) { |
2356 | 0 | // Does the URI match the one we're about to load? |
2357 | 0 | nsCOMPtr<nsIURI> parentURI; |
2358 | 0 | parentAsNav->GetCurrentURI(getter_AddRefs(parentURI)); |
2359 | 0 | if (parentURI) { |
2360 | 0 | // Bug 98158/193011: We need to ignore data after the # |
2361 | 0 | bool equal; |
2362 | 0 | rv = aURI->EqualsExceptRef(parentURI, &equal); |
2363 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
2364 | 0 |
|
2365 | 0 | if (equal) { |
2366 | 0 | matchCount++; |
2367 | 0 | if (matchCount >= MAX_SAME_URL_CONTENT_FRAMES) { |
2368 | 0 | NS_WARNING("Too many nested content frames have the same url (recursion?) so giving up"); |
2369 | 0 | return NS_ERROR_UNEXPECTED; |
2370 | 0 | } |
2371 | 0 | } |
2372 | 0 | } |
2373 | 0 | } |
2374 | 0 | nsCOMPtr<nsIDocShellTreeItem> temp; |
2375 | 0 | temp.swap(parentAsItem); |
2376 | 0 | temp->GetSameTypeParent(getter_AddRefs(parentAsItem)); |
2377 | 0 | } |
2378 | 0 |
|
2379 | 0 | return NS_OK; |
2380 | 0 | } |
2381 | | |
2382 | | nsresult |
2383 | | nsFrameLoader::GetWindowDimensions(nsIntRect& aRect) |
2384 | 0 | { |
2385 | 0 | // Need to get outer window position here |
2386 | 0 | nsIDocument* doc = mOwnerContent->GetComposedDoc(); |
2387 | 0 | if (!doc) { |
2388 | 0 | return NS_ERROR_FAILURE; |
2389 | 0 | } |
2390 | 0 | |
2391 | 0 | MOZ_RELEASE_ASSERT(!doc->IsResourceDoc(), "We shouldn't even exist"); |
2392 | 0 |
|
2393 | 0 | nsCOMPtr<nsPIDOMWindowOuter> win = doc->GetWindow(); |
2394 | 0 | if (!win) { |
2395 | 0 | return NS_ERROR_FAILURE; |
2396 | 0 | } |
2397 | 0 | |
2398 | 0 | nsCOMPtr<nsIDocShellTreeItem> parentAsItem(win->GetDocShell()); |
2399 | 0 | if (!parentAsItem) { |
2400 | 0 | return NS_ERROR_FAILURE; |
2401 | 0 | } |
2402 | 0 | |
2403 | 0 | nsCOMPtr<nsIDocShellTreeOwner> parentOwner; |
2404 | 0 | if (NS_FAILED(parentAsItem->GetTreeOwner(getter_AddRefs(parentOwner))) || |
2405 | 0 | !parentOwner) { |
2406 | 0 | return NS_ERROR_FAILURE; |
2407 | 0 | } |
2408 | 0 | |
2409 | 0 | nsCOMPtr<nsIBaseWindow> treeOwnerAsWin(do_GetInterface(parentOwner)); |
2410 | 0 | treeOwnerAsWin->GetPosition(&aRect.x, &aRect.y); |
2411 | 0 | treeOwnerAsWin->GetSize(&aRect.width, &aRect.height); |
2412 | 0 | return NS_OK; |
2413 | 0 | } |
2414 | | |
2415 | | nsresult |
2416 | | nsFrameLoader::UpdatePositionAndSize(nsSubDocumentFrame *aIFrame) |
2417 | 0 | { |
2418 | 0 | if (IsRemoteFrame()) { |
2419 | 0 | if (mRemoteBrowser) { |
2420 | 0 | ScreenIntSize size = aIFrame->GetSubdocumentSize(); |
2421 | 0 | // If we were not able to show remote frame before, we should probably |
2422 | 0 | // retry now to send correct showInfo. |
2423 | 0 | if (!mRemoteBrowserShown) { |
2424 | 0 | ShowRemoteFrame(size, aIFrame); |
2425 | 0 | } |
2426 | 0 | nsIntRect dimensions; |
2427 | 0 | NS_ENSURE_SUCCESS(GetWindowDimensions(dimensions), NS_ERROR_FAILURE); |
2428 | 0 | mLazySize = size; |
2429 | 0 | mRemoteBrowser->UpdateDimensions(dimensions, size); |
2430 | 0 | } |
2431 | 0 | return NS_OK; |
2432 | 0 | } |
2433 | 0 | UpdateBaseWindowPositionAndSize(aIFrame); |
2434 | 0 | return NS_OK; |
2435 | 0 | } |
2436 | | |
2437 | | void |
2438 | | nsFrameLoader::UpdateBaseWindowPositionAndSize(nsSubDocumentFrame *aIFrame) |
2439 | 0 | { |
2440 | 0 | nsCOMPtr<nsIBaseWindow> baseWindow = |
2441 | 0 | do_QueryInterface(GetDocShell(IgnoreErrors())); |
2442 | 0 |
|
2443 | 0 | // resize the sub document |
2444 | 0 | if (baseWindow) { |
2445 | 0 | int32_t x = 0; |
2446 | 0 | int32_t y = 0; |
2447 | 0 |
|
2448 | 0 | AutoWeakFrame weakFrame(aIFrame); |
2449 | 0 |
|
2450 | 0 | baseWindow->GetPosition(&x, &y); |
2451 | 0 |
|
2452 | 0 | if (!weakFrame.IsAlive()) { |
2453 | 0 | // GetPosition() killed us |
2454 | 0 | return; |
2455 | 0 | } |
2456 | 0 | |
2457 | 0 | ScreenIntSize size = aIFrame->GetSubdocumentSize(); |
2458 | 0 | mLazySize = size; |
2459 | 0 |
|
2460 | 0 | baseWindow->SetPositionAndSize(x, y, size.width, size.height, |
2461 | 0 | nsIBaseWindow::eDelayResize); |
2462 | 0 | } |
2463 | 0 | } |
2464 | | |
2465 | | uint32_t |
2466 | | nsFrameLoader::LazyWidth() const |
2467 | 0 | { |
2468 | 0 | uint32_t lazyWidth = mLazySize.width; |
2469 | 0 |
|
2470 | 0 | nsIFrame* frame = GetPrimaryFrameOfOwningContent(); |
2471 | 0 | if (frame) { |
2472 | 0 | lazyWidth = frame->PresContext()->DevPixelsToIntCSSPixels(lazyWidth); |
2473 | 0 | } |
2474 | 0 |
|
2475 | 0 | return lazyWidth; |
2476 | 0 | } |
2477 | | |
2478 | | uint32_t |
2479 | | nsFrameLoader::LazyHeight() const |
2480 | 0 | { |
2481 | 0 | uint32_t lazyHeight = mLazySize.height; |
2482 | 0 |
|
2483 | 0 | nsIFrame* frame = GetPrimaryFrameOfOwningContent(); |
2484 | 0 | if (frame) { |
2485 | 0 | lazyHeight = frame->PresContext()->DevPixelsToIntCSSPixels(lazyHeight); |
2486 | 0 | } |
2487 | 0 |
|
2488 | 0 | return lazyHeight; |
2489 | 0 | } |
2490 | | |
2491 | | void |
2492 | | nsFrameLoader::SetClipSubdocument(bool aClip) |
2493 | 0 | { |
2494 | 0 | mClipSubdocument = aClip; |
2495 | 0 | nsIFrame* frame = GetPrimaryFrameOfOwningContent(); |
2496 | 0 | if (frame) { |
2497 | 0 | frame->InvalidateFrame(); |
2498 | 0 | frame->PresShell()-> |
2499 | 0 | FrameNeedsReflow(frame, nsIPresShell::eResize, NS_FRAME_IS_DIRTY); |
2500 | 0 | nsSubDocumentFrame* subdocFrame = do_QueryFrame(frame); |
2501 | 0 | if (subdocFrame) { |
2502 | 0 | nsIFrame* subdocRootFrame = subdocFrame->GetSubdocumentRootFrame(); |
2503 | 0 | if (subdocRootFrame) { |
2504 | 0 | nsIFrame* subdocRootScrollFrame = subdocRootFrame->PresShell()-> |
2505 | 0 | GetRootScrollFrame(); |
2506 | 0 | if (subdocRootScrollFrame) { |
2507 | 0 | frame->PresShell()-> |
2508 | 0 | FrameNeedsReflow(frame, nsIPresShell::eResize, NS_FRAME_IS_DIRTY); |
2509 | 0 | } |
2510 | 0 | } |
2511 | 0 | } |
2512 | 0 | } |
2513 | 0 | } |
2514 | | |
2515 | | void |
2516 | | nsFrameLoader::SetClampScrollPosition(bool aClamp) |
2517 | 0 | { |
2518 | 0 | mClampScrollPosition = aClamp; |
2519 | 0 |
|
2520 | 0 | // When turning clamping on, make sure the current position is clamped. |
2521 | 0 | if (aClamp) { |
2522 | 0 | nsIFrame* frame = GetPrimaryFrameOfOwningContent(); |
2523 | 0 | nsSubDocumentFrame* subdocFrame = do_QueryFrame(frame); |
2524 | 0 | if (subdocFrame) { |
2525 | 0 | nsIFrame* subdocRootFrame = subdocFrame->GetSubdocumentRootFrame(); |
2526 | 0 | if (subdocRootFrame) { |
2527 | 0 | nsIScrollableFrame* subdocRootScrollFrame = subdocRootFrame->PresShell()-> |
2528 | 0 | GetRootScrollFrameAsScrollable(); |
2529 | 0 | if (subdocRootScrollFrame) { |
2530 | 0 | subdocRootScrollFrame->ScrollTo(subdocRootScrollFrame->GetScrollPosition(), nsIScrollableFrame::INSTANT); |
2531 | 0 | } |
2532 | 0 | } |
2533 | 0 | } |
2534 | 0 | } |
2535 | 0 | } |
2536 | | |
2537 | | static |
2538 | | Tuple<ContentParent*, TabParent*> |
2539 | | GetContentParent(Element* aBrowser) |
2540 | 0 | { |
2541 | 0 | using ReturnTuple = Tuple<ContentParent*, TabParent*>; |
2542 | 0 |
|
2543 | 0 | nsCOMPtr<nsIBrowser> browser = do_QueryInterface(aBrowser); |
2544 | 0 | if (!browser) { |
2545 | 0 | return ReturnTuple(nullptr, nullptr); |
2546 | 0 | } |
2547 | 0 | |
2548 | 0 | RefPtr<nsFrameLoader> otherLoader; |
2549 | 0 | browser->GetSameProcessAsFrameLoader(getter_AddRefs(otherLoader)); |
2550 | 0 | if (!otherLoader) { |
2551 | 0 | return ReturnTuple(nullptr, nullptr); |
2552 | 0 | } |
2553 | 0 | |
2554 | 0 | TabParent* tabParent = TabParent::GetFrom(otherLoader); |
2555 | 0 | if (tabParent && |
2556 | 0 | tabParent->Manager() && |
2557 | 0 | tabParent->Manager()->IsContentParent()) { |
2558 | 0 | return MakeTuple(tabParent->Manager()->AsContentParent(), tabParent); |
2559 | 0 | } |
2560 | 0 | |
2561 | 0 | return ReturnTuple(nullptr, nullptr); |
2562 | 0 | } |
2563 | | |
2564 | | bool |
2565 | | nsFrameLoader::TryRemoteBrowser() |
2566 | 0 | { |
2567 | 0 | NS_ASSERTION(!mRemoteBrowser, "TryRemoteBrowser called with a remote browser already?"); |
2568 | 0 |
|
2569 | 0 | if (!mOwnerContent) { |
2570 | 0 | return false; |
2571 | 0 | } |
2572 | 0 | |
2573 | 0 | //XXXsmaug Per spec (2014/08/21) frameloader should not work in case the |
2574 | 0 | // element isn't in document, only in shadow dom, but that will change |
2575 | 0 | // https://www.w3.org/Bugs/Public/show_bug.cgi?id=26365#c0 |
2576 | 0 | nsIDocument* doc = mOwnerContent->GetComposedDoc(); |
2577 | 0 | if (!doc) { |
2578 | 0 | return false; |
2579 | 0 | } |
2580 | 0 | |
2581 | 0 | MOZ_RELEASE_ASSERT(!doc->IsResourceDoc(), "We shouldn't even exist"); |
2582 | 0 |
|
2583 | 0 | if (!doc->IsActive()) { |
2584 | 0 | // Don't allow subframe loads in non-active documents. |
2585 | 0 | // (See bug 610571 comment 5.) |
2586 | 0 | return false; |
2587 | 0 | } |
2588 | 0 | |
2589 | 0 | nsCOMPtr<nsPIDOMWindowOuter> parentWin = doc->GetWindow(); |
2590 | 0 | if (!parentWin) { |
2591 | 0 | return false; |
2592 | 0 | } |
2593 | 0 | |
2594 | 0 | nsCOMPtr<nsIDocShell> parentDocShell = parentWin->GetDocShell(); |
2595 | 0 | if (!parentDocShell) { |
2596 | 0 | return false; |
2597 | 0 | } |
2598 | 0 | |
2599 | 0 | TabParent* openingTab = TabParent::GetFrom(parentDocShell->GetOpener()); |
2600 | 0 | RefPtr<ContentParent> openerContentParent; |
2601 | 0 | RefPtr<TabParent> sameTabGroupAs; |
2602 | 0 |
|
2603 | 0 | if (openingTab && |
2604 | 0 | openingTab->Manager() && |
2605 | 0 | openingTab->Manager()->IsContentParent()) { |
2606 | 0 | openerContentParent = openingTab->Manager()->AsContentParent(); |
2607 | 0 | } |
2608 | 0 |
|
2609 | 0 | // <iframe mozbrowser> gets to skip these checks. |
2610 | 0 | // iframes for JS plugins also get to skip these checks. We control the URL that gets |
2611 | 0 | // loaded, but the load is triggered from the document containing the plugin. |
2612 | 0 | if (!OwnerIsMozBrowserFrame() && !IsForJSPlugin()) { |
2613 | 0 | if (parentDocShell->ItemType() != nsIDocShellTreeItem::typeChrome) { |
2614 | 0 | // Allow about:addon an exception to this rule so it can load remote |
2615 | 0 | // extension options pages. |
2616 | 0 | // |
2617 | 0 | // Note that the new frame's message manager will not be a child of the |
2618 | 0 | // chrome window message manager, and, the values of window.top and |
2619 | 0 | // window.parent will be different than they would be for a non-remote |
2620 | 0 | // frame. |
2621 | 0 | nsCOMPtr<nsIWebNavigation> parentWebNav; |
2622 | 0 | nsCOMPtr<nsIURI> aboutAddons; |
2623 | 0 | nsCOMPtr<nsIURI> parentURI; |
2624 | 0 | bool equals; |
2625 | 0 | if (!((parentWebNav = do_GetInterface(parentDocShell)) && |
2626 | 0 | NS_SUCCEEDED(NS_NewURI(getter_AddRefs(aboutAddons), "about:addons")) && |
2627 | 0 | NS_SUCCEEDED(parentWebNav->GetCurrentURI(getter_AddRefs(parentURI))) && |
2628 | 0 | NS_SUCCEEDED(parentURI->EqualsExceptRef(aboutAddons, &equals)) && equals)) { |
2629 | 0 | return false; |
2630 | 0 | } |
2631 | 0 | } |
2632 | 0 | |
2633 | 0 | if (!mOwnerContent->IsXULElement()) { |
2634 | 0 | return false; |
2635 | 0 | } |
2636 | 0 | |
2637 | 0 | if (!mOwnerContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type, |
2638 | 0 | nsGkAtoms::content, eIgnoreCase)) { |
2639 | 0 | return false; |
2640 | 0 | } |
2641 | 0 | |
2642 | 0 | // Try to get the related content parent from our browser element. |
2643 | 0 | Tie(openerContentParent, sameTabGroupAs) = GetContentParent(mOwnerContent); |
2644 | 0 | } |
2645 | 0 |
|
2646 | 0 | uint32_t chromeFlags = 0; |
2647 | 0 | nsCOMPtr<nsIDocShellTreeOwner> parentOwner; |
2648 | 0 | if (NS_FAILED(parentDocShell->GetTreeOwner(getter_AddRefs(parentOwner))) || |
2649 | 0 | !parentOwner) { |
2650 | 0 | return false; |
2651 | 0 | } |
2652 | 0 | nsCOMPtr<nsIXULWindow> window(do_GetInterface(parentOwner)); |
2653 | 0 | if (window && NS_FAILED(window->GetChromeFlags(&chromeFlags))) { |
2654 | 0 | return false; |
2655 | 0 | } |
2656 | 0 | |
2657 | 0 | AUTO_PROFILER_LABEL("nsFrameLoader::TryRemoteBrowser:Create", OTHER); |
2658 | 0 |
|
2659 | 0 | MutableTabContext context; |
2660 | 0 | nsresult rv = GetNewTabContext(&context); |
2661 | 0 | NS_ENSURE_SUCCESS(rv, false); |
2662 | 0 |
|
2663 | 0 | uint64_t nextTabParentId = 0; |
2664 | 0 | if (mOwnerContent) { |
2665 | 0 | nsAutoString nextTabParentIdAttr; |
2666 | 0 | mOwnerContent->GetAttr(kNameSpaceID_None, nsGkAtoms::nextTabParentId, |
2667 | 0 | nextTabParentIdAttr); |
2668 | 0 | nextTabParentId = strtoull(NS_ConvertUTF16toUTF8(nextTabParentIdAttr).get(), |
2669 | 0 | nullptr, 10); |
2670 | 0 |
|
2671 | 0 | // We may be in a window that was just opened, so try the |
2672 | 0 | // nsIBrowserDOMWindow API as a backup. |
2673 | 0 | if (!nextTabParentId && window) { |
2674 | 0 | Unused << window->GetNextTabParentId(&nextTabParentId); |
2675 | 0 | } |
2676 | 0 | } |
2677 | 0 |
|
2678 | 0 | nsCOMPtr<Element> ownerElement = mOwnerContent; |
2679 | 0 | mRemoteBrowser = ContentParent::CreateBrowser(context, ownerElement, |
2680 | 0 | openerContentParent, |
2681 | 0 | sameTabGroupAs, |
2682 | 0 | nextTabParentId); |
2683 | 0 | if (!mRemoteBrowser) { |
2684 | 0 | return false; |
2685 | 0 | } |
2686 | 0 | // Now that mRemoteBrowser is set, we can initialize the RenderFrameParent |
2687 | 0 | mRemoteBrowser->InitRenderFrame(); |
2688 | 0 |
|
2689 | 0 | MaybeUpdatePrimaryTabParent(eTabParentChanged); |
2690 | 0 |
|
2691 | 0 | mChildID = mRemoteBrowser->Manager()->ChildID(); |
2692 | 0 |
|
2693 | 0 | nsCOMPtr<nsIDocShellTreeItem> rootItem; |
2694 | 0 | parentDocShell->GetRootTreeItem(getter_AddRefs(rootItem)); |
2695 | 0 | nsCOMPtr<nsPIDOMWindowOuter> rootWin = rootItem->GetWindow(); |
2696 | 0 | nsCOMPtr<nsIDOMChromeWindow> rootChromeWin = do_QueryInterface(rootWin); |
2697 | 0 |
|
2698 | 0 | if (rootChromeWin) { |
2699 | 0 | nsCOMPtr<nsIBrowserDOMWindow> browserDOMWin; |
2700 | 0 | rootChromeWin->GetBrowserDOMWindow(getter_AddRefs(browserDOMWin)); |
2701 | 0 | mRemoteBrowser->SetBrowserDOMWindow(browserDOMWin); |
2702 | 0 | } |
2703 | 0 |
|
2704 | 0 | // Set up a parent SHistory |
2705 | 0 | if (XRE_IsParentProcess()) { |
2706 | 0 | // XXX(nika): Once we get out of process iframes we won't want to |
2707 | 0 | // unconditionally set this up. What do we do for iframes in a chrome loaded |
2708 | 0 | // document for example? |
2709 | 0 | mParentSHistory = new ParentSHistory(this); |
2710 | 0 | } |
2711 | 0 |
|
2712 | 0 | // For xul:browsers, update some settings based on attributes: |
2713 | 0 | if (mOwnerContent->IsXULElement()) { |
2714 | 0 | // Send down the name of the browser through mRemoteBrowser if it is set. |
2715 | 0 | nsAutoString frameName; |
2716 | 0 | mOwnerContent->GetAttr(kNameSpaceID_None, nsGkAtoms::name, frameName); |
2717 | 0 | if (nsContentUtils::IsOverridingWindowName(frameName)) { |
2718 | 0 | Unused << mRemoteBrowser->SendSetWindowName(frameName); |
2719 | 0 | } |
2720 | 0 | // Allow scripts to close the window if the browser specified so: |
2721 | 0 | if (mOwnerContent->AttrValueIs(kNameSpaceID_None, |
2722 | 0 | nsGkAtoms::allowscriptstoclose, |
2723 | 0 | nsGkAtoms::_true, eCaseMatters)) { |
2724 | 0 | Unused << mRemoteBrowser->SendAllowScriptsToClose(); |
2725 | 0 | } |
2726 | 0 | } |
2727 | 0 |
|
2728 | 0 | ReallyLoadFrameScripts(); |
2729 | 0 | InitializeBrowserAPI(); |
2730 | 0 |
|
2731 | 0 | return true; |
2732 | 0 | } |
2733 | | |
2734 | | mozilla::dom::PBrowserParent* |
2735 | | nsFrameLoader::GetRemoteBrowser() const |
2736 | 0 | { |
2737 | 0 | return mRemoteBrowser; |
2738 | 0 | } |
2739 | | |
2740 | | RenderFrameParent* |
2741 | | nsFrameLoader::GetCurrentRenderFrame() const |
2742 | 0 | { |
2743 | 0 | if (mRemoteBrowser) { |
2744 | 0 | return mRemoteBrowser->GetRenderFrame(); |
2745 | 0 | } |
2746 | 0 | return nullptr; |
2747 | 0 | } |
2748 | | |
2749 | | void |
2750 | | nsFrameLoader::ActivateRemoteFrame(ErrorResult& aRv) |
2751 | 0 | { |
2752 | 0 | if (!mRemoteBrowser) { |
2753 | 0 | aRv.Throw(NS_ERROR_UNEXPECTED); |
2754 | 0 | return; |
2755 | 0 | } |
2756 | 0 | |
2757 | 0 | mRemoteBrowser->Activate(); |
2758 | 0 | } |
2759 | | |
2760 | | void |
2761 | | nsFrameLoader::DeactivateRemoteFrame(ErrorResult& aRv) |
2762 | 0 | { |
2763 | 0 | if (!mRemoteBrowser) { |
2764 | 0 | aRv.Throw(NS_ERROR_UNEXPECTED); |
2765 | 0 | return; |
2766 | 0 | } |
2767 | 0 | |
2768 | 0 | mRemoteBrowser->Deactivate(); |
2769 | 0 | } |
2770 | | |
2771 | | void |
2772 | | nsFrameLoader::SendCrossProcessMouseEvent(const nsAString& aType, |
2773 | | float aX, |
2774 | | float aY, |
2775 | | int32_t aButton, |
2776 | | int32_t aClickCount, |
2777 | | int32_t aModifiers, |
2778 | | bool aIgnoreRootScrollFrame, |
2779 | | ErrorResult& aRv) |
2780 | 0 | { |
2781 | 0 | if (!mRemoteBrowser) { |
2782 | 0 | aRv.Throw(NS_ERROR_FAILURE); |
2783 | 0 | return; |
2784 | 0 | } |
2785 | 0 | |
2786 | 0 | mRemoteBrowser->SendMouseEvent(aType, aX, aY, aButton, |
2787 | 0 | aClickCount, aModifiers, |
2788 | 0 | aIgnoreRootScrollFrame); |
2789 | 0 | } |
2790 | | |
2791 | | void |
2792 | | nsFrameLoader::ActivateFrameEvent(const nsAString& aType, bool aCapture, ErrorResult& aRv) |
2793 | 0 | { |
2794 | 0 | if (!mRemoteBrowser) { |
2795 | 0 | aRv.Throw(NS_ERROR_FAILURE); |
2796 | 0 | return; |
2797 | 0 | } |
2798 | 0 | |
2799 | 0 | bool ok = mRemoteBrowser->SendActivateFrameEvent(nsString(aType), aCapture); |
2800 | 0 | if (!ok) { |
2801 | 0 | aRv.Throw(NS_ERROR_NOT_AVAILABLE); |
2802 | 0 | } |
2803 | 0 | } |
2804 | | |
2805 | | nsresult |
2806 | | nsFrameLoader::CreateStaticClone(nsFrameLoader* aDest) |
2807 | 0 | { |
2808 | 0 | aDest->MaybeCreateDocShell(); |
2809 | 0 | NS_ENSURE_STATE(aDest->mDocShell); |
2810 | 0 |
|
2811 | 0 | nsCOMPtr<nsIDocument> kungFuDeathGrip = aDest->mDocShell->GetDocument(); |
2812 | 0 | Unused << kungFuDeathGrip; |
2813 | 0 |
|
2814 | 0 | nsCOMPtr<nsIContentViewer> viewer; |
2815 | 0 | aDest->mDocShell->GetContentViewer(getter_AddRefs(viewer)); |
2816 | 0 | NS_ENSURE_STATE(viewer); |
2817 | 0 |
|
2818 | 0 | nsIDocShell* origDocShell = GetDocShell(IgnoreErrors()); |
2819 | 0 | NS_ENSURE_STATE(origDocShell); |
2820 | 0 |
|
2821 | 0 | nsCOMPtr<nsIDocument> doc = origDocShell->GetDocument(); |
2822 | 0 | NS_ENSURE_STATE(doc); |
2823 | 0 |
|
2824 | 0 | nsCOMPtr<nsIDocument> clonedDoc = doc->CreateStaticClone(aDest->mDocShell); |
2825 | 0 |
|
2826 | 0 | viewer->SetDocument(clonedDoc); |
2827 | 0 | return NS_OK; |
2828 | 0 | } |
2829 | | |
2830 | | bool |
2831 | | nsFrameLoader::DoLoadMessageManagerScript(const nsAString& aURL, bool aRunInGlobalScope) |
2832 | 0 | { |
2833 | 0 | auto* tabParent = TabParent::GetFrom(GetRemoteBrowser()); |
2834 | 0 | if (tabParent) { |
2835 | 0 | return tabParent->SendLoadRemoteScript(nsString(aURL), aRunInGlobalScope); |
2836 | 0 | } |
2837 | 0 | RefPtr<InProcessTabChildMessageManager> tabChild = GetTabChildMessageManager(); |
2838 | 0 | if (tabChild) { |
2839 | 0 | tabChild->LoadFrameScript(aURL, aRunInGlobalScope); |
2840 | 0 | } |
2841 | 0 | return true; |
2842 | 0 | } |
2843 | | |
2844 | | class nsAsyncMessageToChild : public nsSameProcessAsyncMessageBase, |
2845 | | public Runnable |
2846 | | { |
2847 | | public: |
2848 | | nsAsyncMessageToChild(JS::RootingContext* aRootingCx, |
2849 | | JS::Handle<JSObject*> aCpows, |
2850 | | nsFrameLoader* aFrameLoader) |
2851 | | : nsSameProcessAsyncMessageBase(aRootingCx, aCpows) |
2852 | | , mozilla::Runnable("nsAsyncMessageToChild") |
2853 | | , mFrameLoader(aFrameLoader) |
2854 | 0 | { |
2855 | 0 | } |
2856 | | |
2857 | | NS_IMETHOD Run() override |
2858 | 0 | { |
2859 | 0 | InProcessTabChildMessageManager* tabChild = mFrameLoader->mChildMessageManager; |
2860 | 0 | // Since bug 1126089, messages can arrive even when the docShell is destroyed. |
2861 | 0 | // Here we make sure that those messages are not delivered. |
2862 | 0 | if (tabChild && tabChild->GetInnerManager() && mFrameLoader->GetExistingDocShell()) { |
2863 | 0 | JS::Rooted<JSObject*> kungFuDeathGrip(dom::RootingCx(), tabChild->GetWrapper()); |
2864 | 0 | ReceiveMessage(static_cast<EventTarget*>(tabChild), mFrameLoader, |
2865 | 0 | tabChild->GetInnerManager()); |
2866 | 0 | } |
2867 | 0 | return NS_OK; |
2868 | 0 | } |
2869 | | RefPtr<nsFrameLoader> mFrameLoader; |
2870 | | }; |
2871 | | |
2872 | | nsresult |
2873 | | nsFrameLoader::DoSendAsyncMessage(JSContext* aCx, |
2874 | | const nsAString& aMessage, |
2875 | | StructuredCloneData& aData, |
2876 | | JS::Handle<JSObject *> aCpows, |
2877 | | nsIPrincipal* aPrincipal) |
2878 | 0 | { |
2879 | 0 | TabParent* tabParent = mRemoteBrowser; |
2880 | 0 | if (tabParent) { |
2881 | 0 | ClonedMessageData data; |
2882 | 0 | nsIContentParent* cp = tabParent->Manager(); |
2883 | 0 | if (!BuildClonedMessageDataForParent(cp, aData, data)) { |
2884 | 0 | MOZ_CRASH(); |
2885 | 0 | return NS_ERROR_DOM_DATA_CLONE_ERR; |
2886 | 0 | } |
2887 | 0 | InfallibleTArray<mozilla::jsipc::CpowEntry> cpows; |
2888 | 0 | jsipc::CPOWManager* mgr = cp->GetCPOWManager(); |
2889 | 0 | if (aCpows && (!mgr || !mgr->Wrap(aCx, aCpows, &cpows))) { |
2890 | 0 | return NS_ERROR_UNEXPECTED; |
2891 | 0 | } |
2892 | 0 | if (tabParent->SendAsyncMessage(nsString(aMessage), cpows, |
2893 | 0 | IPC::Principal(aPrincipal), data)) { |
2894 | 0 | return NS_OK; |
2895 | 0 | } else { |
2896 | 0 | return NS_ERROR_UNEXPECTED; |
2897 | 0 | } |
2898 | 0 | } |
2899 | 0 | |
2900 | 0 | if (mChildMessageManager) { |
2901 | 0 | JS::RootingContext* rcx = JS::RootingContext::get(aCx); |
2902 | 0 | RefPtr<nsAsyncMessageToChild> ev = new nsAsyncMessageToChild(rcx, aCpows, this); |
2903 | 0 | nsresult rv = ev->Init(aMessage, aData, aPrincipal); |
2904 | 0 | if (NS_FAILED(rv)) { |
2905 | 0 | return rv; |
2906 | 0 | } |
2907 | 0 | rv = NS_DispatchToCurrentThread(ev); |
2908 | 0 | if (NS_FAILED(rv)) { |
2909 | 0 | return rv; |
2910 | 0 | } |
2911 | 0 | return rv; |
2912 | 0 | } |
2913 | 0 | |
2914 | 0 | // We don't have any targets to send our asynchronous message to. |
2915 | 0 | return NS_ERROR_UNEXPECTED; |
2916 | 0 | } |
2917 | | |
2918 | | already_AddRefed<MessageSender> |
2919 | | nsFrameLoader::GetMessageManager() |
2920 | 0 | { |
2921 | 0 | EnsureMessageManager(); |
2922 | 0 | return do_AddRef(mMessageManager); |
2923 | 0 | } |
2924 | | |
2925 | | nsresult |
2926 | | nsFrameLoader::EnsureMessageManager() |
2927 | 0 | { |
2928 | 0 | NS_ENSURE_STATE(mOwnerContent); |
2929 | 0 |
|
2930 | 0 | if (mMessageManager) { |
2931 | 0 | return NS_OK; |
2932 | 0 | } |
2933 | 0 | |
2934 | 0 | if (!mIsTopLevelContent && |
2935 | 0 | !OwnerIsMozBrowserFrame() && |
2936 | 0 | !IsRemoteFrame() && |
2937 | 0 | !(mOwnerContent->IsXULElement() && |
2938 | 0 | mOwnerContent->AttrValueIs(kNameSpaceID_None, |
2939 | 0 | nsGkAtoms::forcemessagemanager, |
2940 | 0 | nsGkAtoms::_true, eCaseMatters))) { |
2941 | 0 | return NS_OK; |
2942 | 0 | } |
2943 | 0 | |
2944 | 0 | RefPtr<nsGlobalWindowOuter> window = |
2945 | 0 | nsGlobalWindowOuter::Cast(GetOwnerDoc()->GetWindow()); |
2946 | 0 | RefPtr<ChromeMessageBroadcaster> parentManager; |
2947 | 0 |
|
2948 | 0 | if (window && window->IsChromeWindow()) { |
2949 | 0 | nsAutoString messagemanagergroup; |
2950 | 0 | if (mOwnerContent->IsXULElement() && |
2951 | 0 | mOwnerContent->GetAttr(kNameSpaceID_None, |
2952 | 0 | nsGkAtoms::messagemanagergroup, |
2953 | 0 | messagemanagergroup)) { |
2954 | 0 | parentManager = window->GetGroupMessageManager(messagemanagergroup); |
2955 | 0 | } |
2956 | 0 |
|
2957 | 0 | if (!parentManager) { |
2958 | 0 | parentManager = window->GetMessageManager(); |
2959 | 0 | } |
2960 | 0 | } else { |
2961 | 0 | parentManager = nsFrameMessageManager::GetGlobalMessageManager(); |
2962 | 0 | } |
2963 | 0 |
|
2964 | 0 | mMessageManager = new ChromeMessageSender(parentManager); |
2965 | 0 | if (!IsRemoteFrame()) { |
2966 | 0 | nsresult rv = MaybeCreateDocShell(); |
2967 | 0 | if (NS_FAILED(rv)) { |
2968 | 0 | return rv; |
2969 | 0 | } |
2970 | 0 | NS_ASSERTION(mDocShell, |
2971 | 0 | "MaybeCreateDocShell succeeded, but null mDocShell"); |
2972 | 0 | if (!mDocShell) { |
2973 | 0 | return NS_ERROR_FAILURE; |
2974 | 0 | } |
2975 | 0 | mChildMessageManager = |
2976 | 0 | InProcessTabChildMessageManager::Create(mDocShell, mOwnerContent, mMessageManager); |
2977 | 0 | NS_ENSURE_TRUE(mChildMessageManager, NS_ERROR_UNEXPECTED); |
2978 | 0 | } |
2979 | 0 | return NS_OK; |
2980 | 0 | } |
2981 | | |
2982 | | nsresult |
2983 | | nsFrameLoader::ReallyLoadFrameScripts() |
2984 | 0 | { |
2985 | 0 | nsresult rv = EnsureMessageManager(); |
2986 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
2987 | 0 | return rv; |
2988 | 0 | } |
2989 | 0 | if (mMessageManager) { |
2990 | 0 | mMessageManager->InitWithCallback(this); |
2991 | 0 | } |
2992 | 0 | return NS_OK; |
2993 | 0 | } |
2994 | | |
2995 | | already_AddRefed<Element> |
2996 | | nsFrameLoader::GetOwnerElement() |
2997 | 0 | { |
2998 | 0 | return do_AddRef(mOwnerContent); |
2999 | 0 | } |
3000 | | |
3001 | | void |
3002 | | nsFrameLoader::SetRemoteBrowser(nsITabParent* aTabParent) |
3003 | 0 | { |
3004 | 0 | MOZ_ASSERT(!mRemoteBrowser); |
3005 | 0 | mRemoteFrame = true; |
3006 | 0 | mRemoteBrowser = TabParent::GetFrom(aTabParent); |
3007 | 0 | mChildID = mRemoteBrowser ? mRemoteBrowser->Manager()->ChildID() : 0; |
3008 | 0 | MaybeUpdatePrimaryTabParent(eTabParentChanged); |
3009 | 0 | ReallyLoadFrameScripts(); |
3010 | 0 | InitializeBrowserAPI(); |
3011 | 0 | ShowRemoteFrame(ScreenIntSize(0, 0)); |
3012 | 0 | } |
3013 | | |
3014 | | void |
3015 | | nsFrameLoader::SetDetachedSubdocFrame(nsIFrame* aDetachedFrame, |
3016 | | nsIDocument* aContainerDoc) |
3017 | 0 | { |
3018 | 0 | mDetachedSubdocFrame = aDetachedFrame; |
3019 | 0 | mContainerDocWhileDetached = aContainerDoc; |
3020 | 0 | } |
3021 | | |
3022 | | nsIFrame* |
3023 | | nsFrameLoader::GetDetachedSubdocFrame(nsIDocument** aContainerDoc) const |
3024 | 0 | { |
3025 | 0 | NS_IF_ADDREF(*aContainerDoc = mContainerDocWhileDetached); |
3026 | 0 | return mDetachedSubdocFrame.GetFrame(); |
3027 | 0 | } |
3028 | | |
3029 | | void |
3030 | | nsFrameLoader::ApplySandboxFlags(uint32_t sandboxFlags) |
3031 | 0 | { |
3032 | 0 | if (mDocShell) { |
3033 | 0 | uint32_t parentSandboxFlags = mOwnerContent->OwnerDoc()->GetSandboxFlags(); |
3034 | 0 |
|
3035 | 0 | // The child can only add restrictions, never remove them. |
3036 | 0 | sandboxFlags |= parentSandboxFlags; |
3037 | 0 |
|
3038 | 0 | // If this frame is a receiving browsing context, we should add |
3039 | 0 | // sandboxed auxiliary navigation flag to sandboxFlags. See |
3040 | 0 | // https://w3c.github.io/presentation-api/#creating-a-receiving-browsing-context |
3041 | 0 | nsAutoString presentationURL; |
3042 | 0 | nsContentUtils::GetPresentationURL(mDocShell, presentationURL); |
3043 | 0 | if (!presentationURL.IsEmpty()) { |
3044 | 0 | sandboxFlags |= SANDBOXED_AUXILIARY_NAVIGATION; |
3045 | 0 | } |
3046 | 0 | mDocShell->SetSandboxFlags(sandboxFlags); |
3047 | 0 | } |
3048 | 0 | } |
3049 | | |
3050 | | /* virtual */ void |
3051 | | nsFrameLoader::AttributeChanged(mozilla::dom::Element* aElement, |
3052 | | int32_t aNameSpaceID, |
3053 | | nsAtom* aAttribute, |
3054 | | int32_t aModType, |
3055 | | const nsAttrValue* aOldValue) |
3056 | 0 | { |
3057 | 0 | MOZ_ASSERT(mObservingOwnerContent); |
3058 | 0 |
|
3059 | 0 | if (aNameSpaceID != kNameSpaceID_None || |
3060 | 0 | (aAttribute != TypeAttrName() && aAttribute != nsGkAtoms::primary)) { |
3061 | 0 | return; |
3062 | 0 | } |
3063 | 0 | |
3064 | 0 | if (aElement != mOwnerContent) { |
3065 | 0 | return; |
3066 | 0 | } |
3067 | 0 | |
3068 | 0 | // Note: This logic duplicates a lot of logic in |
3069 | 0 | // MaybeCreateDocshell. We should fix that. |
3070 | 0 | |
3071 | 0 | // Notify our enclosing chrome that our type has changed. We only do this |
3072 | 0 | // if our parent is chrome, since in all other cases we're random content |
3073 | 0 | // subframes and the treeowner shouldn't worry about us. |
3074 | 0 | if (!mDocShell) { |
3075 | 0 | MaybeUpdatePrimaryTabParent(eTabParentChanged); |
3076 | 0 | return; |
3077 | 0 | } |
3078 | 0 | |
3079 | 0 | nsCOMPtr<nsIDocShellTreeItem> parentItem; |
3080 | 0 | mDocShell->GetParent(getter_AddRefs(parentItem)); |
3081 | 0 | if (!parentItem) { |
3082 | 0 | return; |
3083 | 0 | } |
3084 | 0 | |
3085 | 0 | if (parentItem->ItemType() != nsIDocShellTreeItem::typeChrome) { |
3086 | 0 | return; |
3087 | 0 | } |
3088 | 0 | |
3089 | 0 | nsCOMPtr<nsIDocShellTreeOwner> parentTreeOwner; |
3090 | 0 | parentItem->GetTreeOwner(getter_AddRefs(parentTreeOwner)); |
3091 | 0 | if (!parentTreeOwner) { |
3092 | 0 | return; |
3093 | 0 | } |
3094 | 0 | |
3095 | 0 | bool is_primary = |
3096 | 0 | aElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::primary, nsGkAtoms::_true, eIgnoreCase); |
3097 | 0 |
|
3098 | 0 | #ifdef MOZ_XUL |
3099 | 0 | // when a content panel is no longer primary, hide any open popups it may have |
3100 | 0 | if (!is_primary) { |
3101 | 0 | nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); |
3102 | 0 | if (pm) |
3103 | 0 | pm->HidePopupsInDocShell(mDocShell); |
3104 | 0 | } |
3105 | 0 | #endif |
3106 | 0 |
|
3107 | 0 | parentTreeOwner->ContentShellRemoved(mDocShell); |
3108 | 0 | if (aElement->AttrValueIs(kNameSpaceID_None, TypeAttrName(), nsGkAtoms::content, eIgnoreCase)) { |
3109 | 0 | parentTreeOwner->ContentShellAdded(mDocShell, is_primary); |
3110 | 0 | } |
3111 | 0 | } |
3112 | | |
3113 | | /** |
3114 | | * Send the RequestNotifyAfterRemotePaint message to the current Tab. |
3115 | | */ |
3116 | | void |
3117 | | nsFrameLoader::RequestNotifyAfterRemotePaint() |
3118 | 0 | { |
3119 | 0 | // If remote browsing (e10s), handle this with the TabParent. |
3120 | 0 | if (mRemoteBrowser) { |
3121 | 0 | Unused << mRemoteBrowser->SendRequestNotifyAfterRemotePaint(); |
3122 | 0 | } |
3123 | 0 | } |
3124 | | |
3125 | | void |
3126 | | nsFrameLoader::RequestFrameLoaderClose(ErrorResult& aRv) |
3127 | 0 | { |
3128 | 0 | nsCOMPtr<nsIBrowser> browser = do_QueryInterface(mOwnerContent); |
3129 | 0 | if (NS_WARN_IF(!browser)) { |
3130 | 0 | // OwnerElement other than nsIBrowser is not supported yet. |
3131 | 0 | aRv.Throw(NS_ERROR_NOT_IMPLEMENTED); |
3132 | 0 | return; |
3133 | 0 | } |
3134 | 0 | |
3135 | 0 | nsresult rv = browser->CloseBrowser(); |
3136 | 0 | if (NS_FAILED(rv)) { |
3137 | 0 | aRv.Throw(rv); |
3138 | 0 | } |
3139 | 0 | } |
3140 | | |
3141 | | void |
3142 | | nsFrameLoader::RequestUpdatePosition(ErrorResult& aRv) |
3143 | 0 | { |
3144 | 0 | if (auto* tabParent = TabParent::GetFrom(GetRemoteBrowser())) { |
3145 | 0 | nsresult rv = tabParent->UpdatePosition(); |
3146 | 0 |
|
3147 | 0 | if (NS_FAILED(rv)) { |
3148 | 0 | aRv.Throw(rv); |
3149 | 0 | } |
3150 | 0 | } |
3151 | 0 | } |
3152 | | |
3153 | | void |
3154 | | nsFrameLoader::Print(uint64_t aOuterWindowID, |
3155 | | nsIPrintSettings* aPrintSettings, |
3156 | | nsIWebProgressListener* aProgressListener, |
3157 | | ErrorResult& aRv) |
3158 | 0 | { |
3159 | 0 | #if defined(NS_PRINTING) |
3160 | 0 | if (mRemoteBrowser) { |
3161 | 0 | RefPtr<embedding::PrintingParent> printingParent = |
3162 | 0 | mRemoteBrowser->Manager()->AsContentParent()->GetPrintingParent(); |
3163 | 0 |
|
3164 | 0 | embedding::PrintData printData; |
3165 | 0 | nsresult rv = printingParent->SerializeAndEnsureRemotePrintJob( |
3166 | 0 | aPrintSettings, aProgressListener, nullptr, &printData); |
3167 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
3168 | 0 | aRv.Throw(rv); |
3169 | 0 | return; |
3170 | 0 | } |
3171 | 0 | |
3172 | 0 | bool success = mRemoteBrowser->SendPrint(aOuterWindowID, printData); |
3173 | 0 | if (!success) { |
3174 | 0 | aRv.Throw(NS_ERROR_FAILURE); |
3175 | 0 | } |
3176 | 0 | return; |
3177 | 0 | } |
3178 | 0 |
|
3179 | 0 | nsGlobalWindowOuter* outerWindow = |
3180 | 0 | nsGlobalWindowOuter::GetOuterWindowWithId(aOuterWindowID); |
3181 | 0 | if (NS_WARN_IF(!outerWindow)) { |
3182 | 0 | aRv.Throw(NS_ERROR_FAILURE); |
3183 | 0 | return; |
3184 | 0 | } |
3185 | 0 | |
3186 | 0 | nsCOMPtr<nsIWebBrowserPrint> webBrowserPrint = |
3187 | 0 | do_GetInterface(outerWindow->AsOuter()); |
3188 | 0 | if (NS_WARN_IF(!webBrowserPrint)) { |
3189 | 0 | aRv.Throw(NS_ERROR_FAILURE); |
3190 | 0 | return; |
3191 | 0 | } |
3192 | 0 | |
3193 | 0 | nsresult rv = webBrowserPrint->Print(aPrintSettings, aProgressListener); |
3194 | 0 | if (NS_FAILED(rv)) { |
3195 | 0 | aRv.Throw(rv); |
3196 | 0 | return; |
3197 | 0 | } |
3198 | 0 | #endif |
3199 | 0 | } |
3200 | | |
3201 | | already_AddRefed<nsITabParent> |
3202 | | nsFrameLoader::GetTabParent() |
3203 | 0 | { |
3204 | 0 | return do_AddRef(mRemoteBrowser); |
3205 | 0 | } |
3206 | | |
3207 | | already_AddRefed<nsILoadContext> |
3208 | | nsFrameLoader::LoadContext() |
3209 | 0 | { |
3210 | 0 | nsCOMPtr<nsILoadContext> loadContext; |
3211 | 0 | if (IsRemoteFrame() && |
3212 | 0 | (mRemoteBrowser || TryRemoteBrowser())) { |
3213 | 0 | loadContext = mRemoteBrowser->GetLoadContext(); |
3214 | 0 | } else { |
3215 | 0 | loadContext = do_GetInterface(GetDocShell(IgnoreErrors())); |
3216 | 0 | } |
3217 | 0 | return loadContext.forget(); |
3218 | 0 | } |
3219 | | |
3220 | | void |
3221 | | nsFrameLoader::InitializeBrowserAPI() |
3222 | 0 | { |
3223 | 0 | if (!OwnerIsMozBrowserFrame()) { |
3224 | 0 | return; |
3225 | 0 | } |
3226 | 0 | if (!IsRemoteFrame()) { |
3227 | 0 | nsresult rv = EnsureMessageManager(); |
3228 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
3229 | 0 | return; |
3230 | 0 | } |
3231 | 0 | if (mMessageManager) { |
3232 | 0 | mMessageManager->LoadFrameScript( |
3233 | 0 | NS_LITERAL_STRING("chrome://global/content/BrowserElementChild.js"), |
3234 | 0 | /* allowDelayedLoad = */ true, |
3235 | 0 | /* aRunInGlobalScope */ true, |
3236 | 0 | IgnoreErrors()); |
3237 | 0 | } |
3238 | 0 | } |
3239 | 0 | nsCOMPtr<nsIMozBrowserFrame> browserFrame = do_QueryInterface(mOwnerContent); |
3240 | 0 | if (browserFrame) { |
3241 | 0 | browserFrame->InitializeBrowserAPI(); |
3242 | 0 | } |
3243 | 0 | } |
3244 | | |
3245 | | void |
3246 | | nsFrameLoader::DestroyBrowserFrameScripts() |
3247 | 0 | { |
3248 | 0 | if (!OwnerIsMozBrowserFrame()) { |
3249 | 0 | return; |
3250 | 0 | } |
3251 | 0 | nsCOMPtr<nsIMozBrowserFrame> browserFrame = do_QueryInterface(mOwnerContent); |
3252 | 0 | if (browserFrame) { |
3253 | 0 | browserFrame->DestroyBrowserFrameScripts(); |
3254 | 0 | } |
3255 | 0 | } |
3256 | | |
3257 | | void |
3258 | | nsFrameLoader::StartPersistence(uint64_t aOuterWindowID, |
3259 | | nsIWebBrowserPersistDocumentReceiver* aRecv, |
3260 | | ErrorResult& aRv) |
3261 | 0 | { |
3262 | 0 | MOZ_ASSERT(aRecv); |
3263 | 0 |
|
3264 | 0 | if (mRemoteBrowser) { |
3265 | 0 | mRemoteBrowser->StartPersistence(aOuterWindowID, aRecv, aRv); |
3266 | 0 | return; |
3267 | 0 | } |
3268 | 0 | |
3269 | 0 | nsCOMPtr<nsIDocument> rootDoc = |
3270 | 0 | mDocShell ? mDocShell->GetDocument() : nullptr; |
3271 | 0 | nsCOMPtr<nsIDocument> foundDoc; |
3272 | 0 | if (aOuterWindowID) { |
3273 | 0 | foundDoc = nsContentUtils::GetSubdocumentWithOuterWindowId(rootDoc, aOuterWindowID); |
3274 | 0 | } else { |
3275 | 0 | foundDoc = rootDoc; |
3276 | 0 | } |
3277 | 0 |
|
3278 | 0 | if (!foundDoc) { |
3279 | 0 | aRecv->OnError(NS_ERROR_NO_CONTENT); |
3280 | 0 | } else { |
3281 | 0 | nsCOMPtr<nsIWebBrowserPersistDocument> pdoc = |
3282 | 0 | new mozilla::WebBrowserPersistLocalDocument(foundDoc); |
3283 | 0 | aRecv->OnDocumentReady(pdoc); |
3284 | 0 | } |
3285 | 0 | } |
3286 | | |
3287 | | void |
3288 | | nsFrameLoader::MaybeUpdatePrimaryTabParent(TabParentChange aChange) |
3289 | 0 | { |
3290 | 0 | if (mRemoteBrowser && mOwnerContent) { |
3291 | 0 | nsCOMPtr<nsIDocShell> docShell = mOwnerContent->OwnerDoc()->GetDocShell(); |
3292 | 0 | if (!docShell) { |
3293 | 0 | return; |
3294 | 0 | } |
3295 | 0 | |
3296 | 0 | int32_t parentType = docShell->ItemType(); |
3297 | 0 | if (parentType != nsIDocShellTreeItem::typeChrome) { |
3298 | 0 | return; |
3299 | 0 | } |
3300 | 0 | |
3301 | 0 | nsCOMPtr<nsIDocShellTreeOwner> parentTreeOwner; |
3302 | 0 | docShell->GetTreeOwner(getter_AddRefs(parentTreeOwner)); |
3303 | 0 | if (!parentTreeOwner) { |
3304 | 0 | return; |
3305 | 0 | } |
3306 | 0 | |
3307 | 0 | if (!mObservingOwnerContent) { |
3308 | 0 | mOwnerContent->AddMutationObserver(this); |
3309 | 0 | mObservingOwnerContent = true; |
3310 | 0 | } |
3311 | 0 |
|
3312 | 0 | parentTreeOwner->TabParentRemoved(mRemoteBrowser); |
3313 | 0 | if (aChange == eTabParentChanged) { |
3314 | 0 | bool isPrimary = |
3315 | 0 | mOwnerContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::primary, |
3316 | 0 | nsGkAtoms::_true, eIgnoreCase); |
3317 | 0 | parentTreeOwner->TabParentAdded(mRemoteBrowser, isPrimary); |
3318 | 0 | } |
3319 | 0 | } |
3320 | 0 | } |
3321 | | |
3322 | | nsresult |
3323 | | nsFrameLoader::GetNewTabContext(MutableTabContext* aTabContext, |
3324 | | nsIURI* aURI) |
3325 | 0 | { |
3326 | 0 | if (IsForJSPlugin()) { |
3327 | 0 | return aTabContext->SetTabContextForJSPluginFrame(mJSPluginID) ? NS_OK : |
3328 | 0 | NS_ERROR_FAILURE; |
3329 | 0 | } |
3330 | 0 |
|
3331 | 0 | OriginAttributes attrs; |
3332 | 0 | attrs.mInIsolatedMozBrowser = OwnerIsIsolatedMozBrowserFrame(); |
3333 | 0 | nsresult rv; |
3334 | 0 |
|
3335 | 0 | attrs.mAppId = nsIScriptSecurityManager::NO_APP_ID; |
3336 | 0 |
|
3337 | 0 | // set the userContextId on the attrs before we pass them into |
3338 | 0 | // the tab context |
3339 | 0 | rv = PopulateUserContextIdFromAttribute(attrs); |
3340 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
3341 | 0 |
|
3342 | 0 | nsAutoString presentationURLStr; |
3343 | 0 | mOwnerContent->GetAttr(kNameSpaceID_None, |
3344 | 0 | nsGkAtoms::mozpresentation, |
3345 | 0 | presentationURLStr); |
3346 | 0 |
|
3347 | 0 | nsCOMPtr<nsIDocShell> docShell = mOwnerContent->OwnerDoc()->GetDocShell(); |
3348 | 0 | nsCOMPtr<nsILoadContext> parentContext = do_QueryInterface(docShell); |
3349 | 0 | NS_ENSURE_STATE(parentContext); |
3350 | 0 |
|
3351 | 0 | bool isPrivate = parentContext->UsePrivateBrowsing(); |
3352 | 0 | attrs.SyncAttributesWithPrivateBrowsing(isPrivate); |
3353 | 0 |
|
3354 | 0 | UIStateChangeType showAccelerators = UIStateChangeType_NoChange; |
3355 | 0 | UIStateChangeType showFocusRings = UIStateChangeType_NoChange; |
3356 | 0 | uint64_t chromeOuterWindowID = 0; |
3357 | 0 |
|
3358 | 0 | nsIDocument* doc = mOwnerContent->OwnerDoc(); |
3359 | 0 | if (doc) { |
3360 | 0 | nsCOMPtr<nsPIWindowRoot> root = nsContentUtils::GetWindowRoot(doc); |
3361 | 0 | if (root) { |
3362 | 0 | showAccelerators = |
3363 | 0 | root->ShowAccelerators() ? UIStateChangeType_Set : UIStateChangeType_Clear; |
3364 | 0 | showFocusRings = |
3365 | 0 | root->ShowFocusRings() ? UIStateChangeType_Set : UIStateChangeType_Clear; |
3366 | 0 |
|
3367 | 0 | nsPIDOMWindowOuter* outerWin = root->GetWindow(); |
3368 | 0 | if (outerWin) { |
3369 | 0 | chromeOuterWindowID = outerWin->WindowID(); |
3370 | 0 | } |
3371 | 0 | } |
3372 | 0 | } |
3373 | 0 |
|
3374 | 0 | bool tabContextUpdated = |
3375 | 0 | aTabContext->SetTabContext(OwnerIsMozBrowserFrame(), |
3376 | 0 | chromeOuterWindowID, |
3377 | 0 | showAccelerators, |
3378 | 0 | showFocusRings, |
3379 | 0 | attrs, |
3380 | 0 | presentationURLStr); |
3381 | 0 | NS_ENSURE_STATE(tabContextUpdated); |
3382 | 0 |
|
3383 | 0 | return NS_OK; |
3384 | 0 | } |
3385 | | |
3386 | | nsresult |
3387 | | nsFrameLoader::PopulateUserContextIdFromAttribute(OriginAttributes& aAttr) |
3388 | 0 | { |
3389 | 0 | if (aAttr.mUserContextId == |
3390 | 0 | nsIScriptSecurityManager::DEFAULT_USER_CONTEXT_ID) { |
3391 | 0 | // Grab the userContextId from owner if XUL or mozbrowser frame |
3392 | 0 | nsAutoString userContextIdStr; |
3393 | 0 | int32_t namespaceID = mOwnerContent->GetNameSpaceID(); |
3394 | 0 | if ((namespaceID == kNameSpaceID_XUL || OwnerIsMozBrowserFrame()) && |
3395 | 0 | mOwnerContent->GetAttr(kNameSpaceID_None, nsGkAtoms::usercontextid, |
3396 | 0 | userContextIdStr) && |
3397 | 0 | !userContextIdStr.IsEmpty()) { |
3398 | 0 | nsresult rv; |
3399 | 0 | aAttr.mUserContextId = userContextIdStr.ToInteger(&rv); |
3400 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
3401 | 0 | } |
3402 | 0 | } |
3403 | 0 |
|
3404 | 0 | return NS_OK; |
3405 | 0 | } |
3406 | | |
3407 | | ProcessMessageManager* |
3408 | | nsFrameLoader::GetProcessMessageManager() const |
3409 | 0 | { |
3410 | 0 | return mRemoteBrowser ? mRemoteBrowser->Manager()->GetMessageManager() |
3411 | 0 | : nullptr; |
3412 | 0 | }; |
3413 | | |
3414 | | JSObject* |
3415 | | nsFrameLoader::WrapObject(JSContext* cx, JS::Handle<JSObject*> aGivenProto) |
3416 | 0 | { |
3417 | 0 | JS::RootedObject result(cx); |
3418 | 0 | FrameLoader_Binding::Wrap(cx, this, this, aGivenProto, &result); |
3419 | 0 | return result; |
3420 | 0 | } |