Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 : /* This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this file,
5 : * You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : #include "nsGenericHTMLFrameElement.h"
8 :
9 : #include "mozilla/dom/ContentChild.h"
10 : #include "mozilla/dom/HTMLIFrameElement.h"
11 : #include "mozilla/Preferences.h"
12 : #include "mozilla/ErrorResult.h"
13 : #include "GeckoProfiler.h"
14 : #include "nsAttrValueInlines.h"
15 : #include "nsContentUtils.h"
16 : #include "nsIDocShell.h"
17 : #include "nsIDOMDocument.h"
18 : #include "nsIFrame.h"
19 : #include "nsIInterfaceRequestorUtils.h"
20 : #include "nsIPermissionManager.h"
21 : #include "nsIPresShell.h"
22 : #include "nsIScrollable.h"
23 : #include "nsPresContext.h"
24 : #include "nsServiceManagerUtils.h"
25 : #include "nsSubDocumentFrame.h"
26 : #include "nsXULElement.h"
27 :
28 : using namespace mozilla;
29 : using namespace mozilla::dom;
30 :
31 : NS_IMPL_CYCLE_COLLECTION_CLASS(nsGenericHTMLFrameElement)
32 :
33 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsGenericHTMLFrameElement,
34 : nsGenericHTMLElement)
35 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFrameLoader)
36 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOpenerWindow)
37 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBrowserElementAPI)
38 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
39 :
40 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsGenericHTMLFrameElement,
41 : nsGenericHTMLElement)
42 0 : if (tmp->mFrameLoader) {
43 0 : tmp->mFrameLoader->Destroy();
44 : }
45 :
46 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mFrameLoader)
47 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mOpenerWindow)
48 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mBrowserElementAPI)
49 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
50 :
51 0 : NS_IMPL_ADDREF_INHERITED(nsGenericHTMLFrameElement, nsGenericHTMLElement)
52 0 : NS_IMPL_RELEASE_INHERITED(nsGenericHTMLFrameElement, nsGenericHTMLElement)
53 :
54 0 : NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(nsGenericHTMLFrameElement)
55 0 : NS_INTERFACE_TABLE_INHERITED(nsGenericHTMLFrameElement,
56 : nsIFrameLoaderOwner,
57 : nsIDOMMozBrowserFrame,
58 : nsIMozBrowserFrame)
59 0 : NS_INTERFACE_TABLE_TAIL_INHERITING(nsGenericHTMLElement)
60 0 : NS_IMPL_BOOL_ATTR(nsGenericHTMLFrameElement, Mozbrowser, mozbrowser)
61 :
62 : int32_t
63 0 : nsGenericHTMLFrameElement::TabIndexDefault()
64 : {
65 0 : return 0;
66 : }
67 :
68 0 : nsGenericHTMLFrameElement::~nsGenericHTMLFrameElement()
69 : {
70 0 : if (mFrameLoader) {
71 0 : mFrameLoader->Destroy();
72 : }
73 0 : }
74 :
75 : nsresult
76 0 : nsGenericHTMLFrameElement::GetContentDocument(nsIDOMDocument** aContentDocument)
77 : {
78 0 : NS_PRECONDITION(aContentDocument, "Null out param");
79 : nsCOMPtr<nsIDOMDocument> document =
80 0 : do_QueryInterface(GetContentDocument(*nsContentUtils::SubjectPrincipal()));
81 0 : document.forget(aContentDocument);
82 0 : return NS_OK;
83 : }
84 :
85 : nsIDocument*
86 0 : nsGenericHTMLFrameElement::GetContentDocument(nsIPrincipal& aSubjectPrincipal)
87 : {
88 0 : nsCOMPtr<nsPIDOMWindowOuter> win = GetContentWindow();
89 0 : if (!win) {
90 0 : return nullptr;
91 : }
92 :
93 0 : nsIDocument *doc = win->GetDoc();
94 0 : if (!doc) {
95 0 : return nullptr;
96 : }
97 :
98 : // Return null for cross-origin contentDocument.
99 0 : if (!aSubjectPrincipal.SubsumesConsideringDomain(doc->NodePrincipal())) {
100 0 : return nullptr;
101 : }
102 0 : return doc;
103 : }
104 :
105 : already_AddRefed<nsPIDOMWindowOuter>
106 0 : nsGenericHTMLFrameElement::GetContentWindow()
107 : {
108 0 : EnsureFrameLoader();
109 :
110 0 : if (!mFrameLoader) {
111 0 : return nullptr;
112 : }
113 :
114 0 : bool depthTooGreat = false;
115 0 : mFrameLoader->GetDepthTooGreat(&depthTooGreat);
116 0 : if (depthTooGreat) {
117 : // Claim to have no contentWindow
118 0 : return nullptr;
119 : }
120 :
121 0 : nsCOMPtr<nsIDocShell> doc_shell;
122 0 : mFrameLoader->GetDocShell(getter_AddRefs(doc_shell));
123 0 : if (!doc_shell) {
124 0 : return nullptr;
125 : }
126 :
127 0 : nsCOMPtr<nsPIDOMWindowOuter> win = doc_shell->GetWindow();
128 :
129 0 : if (!win) {
130 0 : return nullptr;
131 : }
132 :
133 0 : NS_ASSERTION(win->IsOuterWindow(),
134 : "Uh, this window should always be an outer window!");
135 :
136 0 : return win.forget();
137 : }
138 :
139 : void
140 0 : nsGenericHTMLFrameElement::EnsureFrameLoader()
141 : {
142 0 : if (!IsInComposedDoc() || mFrameLoader || mFrameLoaderCreationDisallowed) {
143 : // If frame loader is there, we just keep it around, cached
144 0 : return;
145 : }
146 :
147 : // Strangely enough, this method doesn't actually ensure that the
148 : // frameloader exists. It's more of a best-effort kind of thing.
149 : mFrameLoader = nsFrameLoader::Create(this,
150 0 : nsPIDOMWindowOuter::From(mOpenerWindow),
151 0 : mNetworkCreated);
152 0 : if (mIsPrerendered) {
153 0 : mFrameLoader->SetIsPrerendered();
154 : }
155 : }
156 :
157 : nsresult
158 0 : nsGenericHTMLFrameElement::CreateRemoteFrameLoader(nsITabParent* aTabParent)
159 : {
160 0 : MOZ_ASSERT(!mFrameLoader);
161 0 : EnsureFrameLoader();
162 0 : NS_ENSURE_STATE(mFrameLoader);
163 0 : mFrameLoader->SetRemoteBrowser(aTabParent);
164 :
165 0 : if (nsSubDocumentFrame* subdocFrame = do_QueryFrame(GetPrimaryFrame())) {
166 : // The reflow for this element already happened while we were waiting
167 : // for the iframe creation. Therefore the subdoc frame didn't have a
168 : // frameloader when UpdatePositionAndSize was supposed to be called in
169 : // ReflowFinished, and we need to do it properly now.
170 0 : mFrameLoader->UpdatePositionAndSize(subdocFrame);
171 : }
172 0 : return NS_OK;
173 : }
174 :
175 : NS_IMETHODIMP
176 0 : nsGenericHTMLFrameElement::GetFrameLoaderXPCOM(nsIFrameLoader **aFrameLoader)
177 : {
178 0 : NS_IF_ADDREF(*aFrameLoader = mFrameLoader);
179 0 : return NS_OK;
180 : }
181 :
182 : NS_IMETHODIMP_(already_AddRefed<nsFrameLoader>)
183 0 : nsGenericHTMLFrameElement::GetFrameLoader()
184 : {
185 0 : RefPtr<nsFrameLoader> loader = mFrameLoader;
186 0 : return loader.forget();
187 : }
188 :
189 : void
190 0 : nsGenericHTMLFrameElement::PresetOpenerWindow(mozIDOMWindowProxy* aWindow, ErrorResult& aRv)
191 : {
192 0 : MOZ_ASSERT(!mFrameLoader);
193 0 : mOpenerWindow = nsPIDOMWindowOuter::From(aWindow);
194 0 : }
195 :
196 : void
197 0 : nsGenericHTMLFrameElement::InternalSetFrameLoader(nsIFrameLoader* aNewFrameLoader)
198 : {
199 0 : mFrameLoader = static_cast<nsFrameLoader*>(aNewFrameLoader);
200 0 : }
201 :
202 : void
203 0 : nsGenericHTMLFrameElement::SwapFrameLoaders(HTMLIFrameElement& aOtherLoaderOwner,
204 : ErrorResult& rv)
205 : {
206 0 : if (&aOtherLoaderOwner == this) {
207 : // nothing to do
208 0 : return;
209 : }
210 :
211 0 : aOtherLoaderOwner.SwapFrameLoaders(this, rv);
212 : }
213 :
214 : void
215 0 : nsGenericHTMLFrameElement::SwapFrameLoaders(nsXULElement& aOtherLoaderOwner,
216 : ErrorResult& rv)
217 : {
218 0 : aOtherLoaderOwner.SwapFrameLoaders(this, rv);
219 0 : }
220 :
221 : void
222 0 : nsGenericHTMLFrameElement::SwapFrameLoaders(nsIFrameLoaderOwner* aOtherLoaderOwner,
223 : mozilla::ErrorResult& rv)
224 : {
225 0 : RefPtr<nsFrameLoader> loader = GetFrameLoader();
226 0 : RefPtr<nsFrameLoader> otherLoader = aOtherLoaderOwner->GetFrameLoader();
227 0 : if (!loader || !otherLoader) {
228 0 : rv.Throw(NS_ERROR_NOT_IMPLEMENTED);
229 0 : return;
230 : }
231 :
232 0 : rv = loader->SwapWithOtherLoader(otherLoader, this, aOtherLoaderOwner);
233 : }
234 :
235 : NS_IMETHODIMP
236 0 : nsGenericHTMLFrameElement::SetIsPrerendered()
237 : {
238 0 : MOZ_ASSERT(!mFrameLoader, "Please call SetIsPrerendered before frameLoader is created");
239 0 : mIsPrerendered = true;
240 0 : return NS_OK;
241 : }
242 :
243 : nsresult
244 0 : nsGenericHTMLFrameElement::LoadSrc()
245 : {
246 0 : EnsureFrameLoader();
247 :
248 0 : if (!mFrameLoader) {
249 0 : return NS_OK;
250 : }
251 :
252 0 : nsresult rv = mFrameLoader->LoadFrame();
253 : #ifdef DEBUG
254 0 : if (NS_FAILED(rv)) {
255 0 : NS_WARNING("failed to load URL");
256 : }
257 : #endif
258 :
259 0 : return rv;
260 : }
261 :
262 : nsresult
263 0 : nsGenericHTMLFrameElement::BindToTree(nsIDocument* aDocument,
264 : nsIContent* aParent,
265 : nsIContent* aBindingParent,
266 : bool aCompileEventHandlers)
267 : {
268 0 : nsresult rv = nsGenericHTMLElement::BindToTree(aDocument, aParent,
269 : aBindingParent,
270 0 : aCompileEventHandlers);
271 0 : NS_ENSURE_SUCCESS(rv, rv);
272 :
273 0 : if (IsInComposedDoc()) {
274 0 : NS_ASSERTION(!nsContentUtils::IsSafeToRunScript(),
275 : "Missing a script blocker!");
276 :
277 0 : AUTO_PROFILER_LABEL("nsGenericHTMLFrameElement::BindToTree", OTHER);
278 :
279 : // We're in a document now. Kick off the frame load.
280 0 : LoadSrc();
281 : }
282 :
283 : // We're now in document and scripts may move us, so clear
284 : // the mNetworkCreated flag.
285 0 : mNetworkCreated = false;
286 0 : return rv;
287 : }
288 :
289 : void
290 0 : nsGenericHTMLFrameElement::UnbindFromTree(bool aDeep, bool aNullParent)
291 : {
292 0 : if (mFrameLoader) {
293 : // This iframe is being taken out of the document, destroy the
294 : // iframe's frame loader (doing that will tear down the window in
295 : // this iframe).
296 : // XXXbz we really want to only partially destroy the frame
297 : // loader... we don't want to tear down the docshell. Food for
298 : // later bug.
299 0 : mFrameLoader->Destroy();
300 0 : mFrameLoader = nullptr;
301 : }
302 :
303 0 : nsGenericHTMLElement::UnbindFromTree(aDeep, aNullParent);
304 0 : }
305 :
306 : /* static */ int32_t
307 3 : nsGenericHTMLFrameElement::MapScrollingAttribute(const nsAttrValue* aValue)
308 : {
309 3 : int32_t mappedValue = nsIScrollable::Scrollbar_Auto;
310 3 : if (aValue && aValue->Type() == nsAttrValue::eEnum) {
311 0 : switch (aValue->GetEnumValue()) {
312 : case NS_STYLE_FRAME_OFF:
313 : case NS_STYLE_FRAME_NOSCROLL:
314 : case NS_STYLE_FRAME_NO:
315 0 : mappedValue = nsIScrollable::Scrollbar_Never;
316 0 : break;
317 : }
318 : }
319 3 : return mappedValue;
320 : }
321 :
322 : static bool
323 0 : PrincipalAllowsBrowserFrame(nsIPrincipal* aPrincipal)
324 : {
325 0 : nsCOMPtr<nsIPermissionManager> permMgr = mozilla::services::GetPermissionManager();
326 0 : NS_ENSURE_TRUE(permMgr, false);
327 0 : uint32_t permission = nsIPermissionManager::DENY_ACTION;
328 0 : nsresult rv = permMgr->TestPermissionFromPrincipal(aPrincipal, "browser", &permission);
329 0 : NS_ENSURE_SUCCESS(rv, false);
330 0 : return permission == nsIPermissionManager::ALLOW_ACTION;
331 : }
332 :
333 : /* virtual */ nsresult
334 0 : nsGenericHTMLFrameElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
335 : const nsAttrValue* aValue,
336 : const nsAttrValue* aOldValue, bool aNotify)
337 : {
338 0 : if (aValue) {
339 0 : nsAttrValueOrString value(aValue);
340 0 : AfterMaybeChangeAttr(aNameSpaceID, aName, &value, aNotify);
341 : } else {
342 0 : AfterMaybeChangeAttr(aNameSpaceID, aName, nullptr, aNotify);
343 : }
344 :
345 0 : if (aNameSpaceID == kNameSpaceID_None) {
346 0 : if (aName == nsGkAtoms::scrolling) {
347 0 : if (mFrameLoader) {
348 0 : nsIDocShell* docshell = mFrameLoader->GetExistingDocShell();
349 0 : nsCOMPtr<nsIScrollable> scrollable = do_QueryInterface(docshell);
350 0 : if (scrollable) {
351 : int32_t cur;
352 0 : scrollable->GetDefaultScrollbarPreferences(nsIScrollable::ScrollOrientation_X, &cur);
353 0 : int32_t val = MapScrollingAttribute(aValue);
354 0 : if (cur != val) {
355 0 : scrollable->SetDefaultScrollbarPreferences(nsIScrollable::ScrollOrientation_X, val);
356 0 : scrollable->SetDefaultScrollbarPreferences(nsIScrollable::ScrollOrientation_Y, val);
357 0 : RefPtr<nsPresContext> presContext;
358 0 : docshell->GetPresContext(getter_AddRefs(presContext));
359 0 : nsIPresShell* shell = presContext ? presContext->GetPresShell() : nullptr;
360 0 : nsIFrame* rootScroll = shell ? shell->GetRootScrollFrame() : nullptr;
361 0 : if (rootScroll) {
362 : shell->FrameNeedsReflow(rootScroll, nsIPresShell::eStyleChange,
363 0 : NS_FRAME_IS_DIRTY);
364 : }
365 : }
366 : }
367 : }
368 0 : } else if (aName == nsGkAtoms::mozbrowser) {
369 0 : mReallyIsBrowser = !!aValue && BrowserFramesEnabled() &&
370 0 : PrincipalAllowsBrowserFrame(NodePrincipal());
371 : }
372 : }
373 :
374 0 : return nsGenericHTMLElement::AfterSetAttr(aNameSpaceID, aName, aValue,
375 0 : aOldValue, aNotify);
376 : }
377 :
378 : nsresult
379 0 : nsGenericHTMLFrameElement::OnAttrSetButNotChanged(int32_t aNamespaceID,
380 : nsIAtom* aName,
381 : const nsAttrValueOrString& aValue,
382 : bool aNotify)
383 : {
384 0 : AfterMaybeChangeAttr(aNamespaceID, aName, &aValue, aNotify);
385 :
386 0 : return nsGenericHTMLElement::OnAttrSetButNotChanged(aNamespaceID, aName,
387 0 : aValue, aNotify);
388 : }
389 :
390 : void
391 0 : nsGenericHTMLFrameElement::AfterMaybeChangeAttr(int32_t aNamespaceID,
392 : nsIAtom* aName,
393 : const nsAttrValueOrString* aValue,
394 : bool aNotify)
395 : {
396 0 : if (aNamespaceID == kNameSpaceID_None) {
397 0 : if (aName == nsGkAtoms::src) {
398 0 : if (aValue && (!IsHTMLElement(nsGkAtoms::iframe) ||
399 0 : !HasAttr(kNameSpaceID_None, nsGkAtoms::srcdoc))) {
400 : // Don't propagate error here. The attribute was successfully set,
401 : // that's what we should reflect.
402 0 : LoadSrc();
403 : }
404 0 : } else if (aName == nsGkAtoms::name) {
405 : // Propagate "name" to the docshell to make browsing context names live,
406 : // per HTML5.
407 0 : nsIDocShell* docShell = mFrameLoader ? mFrameLoader->GetExistingDocShell()
408 0 : : nullptr;
409 0 : if (docShell) {
410 0 : if (aValue) {
411 0 : docShell->SetName(aValue->String());
412 : } else {
413 0 : docShell->SetName(EmptyString());
414 : }
415 : }
416 : }
417 : }
418 0 : }
419 :
420 : void
421 0 : nsGenericHTMLFrameElement::DestroyContent()
422 : {
423 0 : if (mFrameLoader) {
424 0 : mFrameLoader->Destroy();
425 0 : mFrameLoader = nullptr;
426 : }
427 :
428 0 : nsGenericHTMLElement::DestroyContent();
429 0 : }
430 :
431 : nsresult
432 0 : nsGenericHTMLFrameElement::CopyInnerTo(Element* aDest,
433 : bool aPreallocateChildren)
434 : {
435 0 : nsresult rv = nsGenericHTMLElement::CopyInnerTo(aDest, aPreallocateChildren);
436 0 : NS_ENSURE_SUCCESS(rv, rv);
437 :
438 0 : nsIDocument* doc = aDest->OwnerDoc();
439 0 : if (doc->IsStaticDocument() && mFrameLoader) {
440 : nsGenericHTMLFrameElement* dest =
441 0 : static_cast<nsGenericHTMLFrameElement*>(aDest);
442 0 : nsFrameLoader* fl = nsFrameLoader::Create(dest, nullptr, false);
443 0 : NS_ENSURE_STATE(fl);
444 0 : dest->mFrameLoader = fl;
445 0 : static_cast<nsFrameLoader*>(mFrameLoader.get())->CreateStaticClone(fl);
446 : }
447 :
448 0 : return rv;
449 : }
450 :
451 : bool
452 0 : nsGenericHTMLFrameElement::IsHTMLFocusable(bool aWithMouse,
453 : bool *aIsFocusable,
454 : int32_t *aTabIndex)
455 : {
456 0 : if (nsGenericHTMLElement::IsHTMLFocusable(aWithMouse, aIsFocusable, aTabIndex)) {
457 0 : return true;
458 : }
459 :
460 0 : *aIsFocusable = nsContentUtils::IsSubDocumentTabbable(this);
461 :
462 0 : if (!*aIsFocusable && aTabIndex) {
463 0 : *aTabIndex = -1;
464 : }
465 :
466 0 : return false;
467 : }
468 :
469 : static bool sMozBrowserFramesEnabled = false;
470 : #ifdef DEBUG
471 : static bool sBoolVarCacheInitialized = false;
472 : #endif
473 :
474 : void
475 3 : nsGenericHTMLFrameElement::InitStatics()
476 : {
477 3 : MOZ_ASSERT(!sBoolVarCacheInitialized);
478 3 : MOZ_ASSERT(NS_IsMainThread());
479 : Preferences::AddBoolVarCache(&sMozBrowserFramesEnabled,
480 3 : "dom.mozBrowserFramesEnabled");
481 : #ifdef DEBUG
482 3 : sBoolVarCacheInitialized = true;
483 : #endif
484 3 : }
485 :
486 :
487 : bool
488 17 : nsGenericHTMLFrameElement::BrowserFramesEnabled()
489 : {
490 17 : MOZ_ASSERT(sBoolVarCacheInitialized);
491 17 : return sMozBrowserFramesEnabled;
492 : }
493 :
494 : /**
495 : * Return true if this frame element really is a mozbrowser. (It
496 : * needs to have the right attributes, and its creator must have the right
497 : * permissions.)
498 : */
499 : /* [infallible] */ nsresult
500 0 : nsGenericHTMLFrameElement::GetReallyIsBrowser(bool *aOut)
501 : {
502 0 : *aOut = mReallyIsBrowser;
503 0 : return NS_OK;
504 : }
505 :
506 : /* [infallible] */ NS_IMETHODIMP
507 0 : nsGenericHTMLFrameElement::GetIsolated(bool *aOut)
508 : {
509 0 : *aOut = true;
510 :
511 0 : if (!nsContentUtils::IsSystemPrincipal(NodePrincipal())) {
512 0 : return NS_OK;
513 : }
514 :
515 : // Isolation is only disabled if the attribute is present
516 0 : *aOut = !HasAttr(kNameSpaceID_None, nsGkAtoms::noisolation);
517 0 : return NS_OK;
518 : }
519 :
520 : NS_IMETHODIMP
521 0 : nsGenericHTMLFrameElement::DisallowCreateFrameLoader()
522 : {
523 0 : MOZ_ASSERT(!mFrameLoader);
524 0 : MOZ_ASSERT(!mFrameLoaderCreationDisallowed);
525 0 : mFrameLoaderCreationDisallowed = true;
526 0 : return NS_OK;
527 : }
528 :
529 : NS_IMETHODIMP
530 0 : nsGenericHTMLFrameElement::AllowCreateFrameLoader()
531 : {
532 0 : MOZ_ASSERT(!mFrameLoader);
533 0 : MOZ_ASSERT(mFrameLoaderCreationDisallowed);
534 0 : mFrameLoaderCreationDisallowed = false;
535 0 : return NS_OK;
536 : }
537 :
538 : NS_IMETHODIMP
539 0 : nsGenericHTMLFrameElement::InitializeBrowserAPI()
540 : {
541 0 : MOZ_ASSERT(mFrameLoader);
542 0 : InitBrowserElementAPI();
543 0 : return NS_OK;
544 : }
545 :
546 : NS_IMETHODIMP
547 0 : nsGenericHTMLFrameElement::DestroyBrowserFrameScripts()
548 : {
549 0 : MOZ_ASSERT(mFrameLoader);
550 0 : DestroyBrowserElementFrameScripts();
551 0 : return NS_OK;
552 : }
|