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/Document.h"
10 #include "mozilla/dom/HTMLIFrameElement.h"
11 #include "mozilla/dom/XULFrameElement.h"
12 #include "mozilla/dom/BrowserBridgeChild.h"
13 #include "mozilla/dom/WindowProxyHolder.h"
14 #include "mozilla/Preferences.h"
15 #include "mozilla/PresShell.h"
16 #include "mozilla/ProfilerLabels.h"
17 #include "mozilla/StaticPrefs_dom.h"
18 #include "mozilla/ErrorResult.h"
19 #include "nsAttrValueInlines.h"
20 #include "nsContentUtils.h"
21 #include "nsIDocShell.h"
22 #include "nsIFrame.h"
23 #include "nsIInterfaceRequestorUtils.h"
24 #include "nsIPermissionManager.h"
25 #include "nsPresContext.h"
26 #include "nsServiceManagerUtils.h"
27 #include "nsSubDocumentFrame.h"
28 #include "nsAttrValueOrString.h"
29 
30 using namespace mozilla;
31 using namespace mozilla::dom;
32 
33 NS_IMPL_CYCLE_COLLECTION_CLASS(nsGenericHTMLFrameElement)
34 
35 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsGenericHTMLFrameElement,
36                                                   nsGenericHTMLElement)
37   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFrameLoader)
38   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBrowserElementAPI)
39 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
40 
41 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsGenericHTMLFrameElement,
42                                                 nsGenericHTMLElement)
43   if (tmp->mFrameLoader) {
44     tmp->mFrameLoader->Destroy();
45   }
46 
47   NS_IMPL_CYCLE_COLLECTION_UNLINK(mFrameLoader)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mBrowserElementAPI)48   NS_IMPL_CYCLE_COLLECTION_UNLINK(mBrowserElementAPI)
49 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
50 
51 NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED(
52     nsGenericHTMLFrameElement, nsGenericHTMLElement, nsFrameLoaderOwner,
53     nsIDOMMozBrowserFrame, nsIMozBrowserFrame, nsGenericHTMLFrameElement)
54 
55 NS_IMETHODIMP
56 nsGenericHTMLFrameElement::GetMozbrowser(bool* aValue) {
57   *aValue = GetBoolAttr(nsGkAtoms::mozbrowser);
58   return NS_OK;
59 }
60 NS_IMETHODIMP
SetMozbrowser(bool aValue)61 nsGenericHTMLFrameElement::SetMozbrowser(bool aValue) {
62   return SetBoolAttr(nsGkAtoms::mozbrowser, aValue);
63 }
64 
TabIndexDefault()65 int32_t nsGenericHTMLFrameElement::TabIndexDefault() { return 0; }
66 
~nsGenericHTMLFrameElement()67 nsGenericHTMLFrameElement::~nsGenericHTMLFrameElement() {
68   if (mFrameLoader) {
69     mFrameLoader->Destroy();
70   }
71 }
72 
GetContentDocument(nsIPrincipal & aSubjectPrincipal)73 Document* nsGenericHTMLFrameElement::GetContentDocument(
74     nsIPrincipal& aSubjectPrincipal) {
75   RefPtr<BrowsingContext> bc = GetContentWindowInternal();
76   if (!bc) {
77     return nullptr;
78   }
79 
80   nsPIDOMWindowOuter* window = bc->GetDOMWindow();
81   if (!window) {
82     // Either our browsing context contents are out-of-process (in which case
83     // clearly this is a cross-origin call and we should return null), or our
84     // browsing context is torn-down enough to no longer have a window or a
85     // document, and we should still return null.
86     return nullptr;
87   }
88   Document* doc = window->GetDoc();
89   if (!doc) {
90     return nullptr;
91   }
92 
93   // Return null for cross-origin contentDocument.
94   if (!aSubjectPrincipal.SubsumesConsideringDomain(doc->NodePrincipal())) {
95     return nullptr;
96   }
97   return doc;
98 }
99 
GetContentWindowInternal()100 BrowsingContext* nsGenericHTMLFrameElement::GetContentWindowInternal() {
101   EnsureFrameLoader();
102 
103   if (!mFrameLoader) {
104     return nullptr;
105   }
106 
107   if (mFrameLoader->DepthTooGreat()) {
108     // Claim to have no contentWindow
109     return nullptr;
110   }
111 
112   RefPtr<BrowsingContext> bc = mFrameLoader->GetBrowsingContext();
113   return bc;
114 }
115 
GetContentWindow()116 Nullable<WindowProxyHolder> nsGenericHTMLFrameElement::GetContentWindow() {
117   RefPtr<BrowsingContext> bc = GetContentWindowInternal();
118   if (!bc) {
119     return nullptr;
120   }
121   return WindowProxyHolder(bc);
122 }
123 
EnsureFrameLoader()124 void nsGenericHTMLFrameElement::EnsureFrameLoader() {
125   if (!IsInComposedDoc() || mFrameLoader || OwnerDoc()->IsStaticDocument()) {
126     // If frame loader is there, we just keep it around, cached
127     return;
128   }
129 
130   // Strangely enough, this method doesn't actually ensure that the
131   // frameloader exists.  It's more of a best-effort kind of thing.
132   mFrameLoader = nsFrameLoader::Create(this, mNetworkCreated);
133 }
134 
SwapFrameLoaders(HTMLIFrameElement & aOtherLoaderOwner,ErrorResult & rv)135 void nsGenericHTMLFrameElement::SwapFrameLoaders(
136     HTMLIFrameElement& aOtherLoaderOwner, ErrorResult& rv) {
137   if (&aOtherLoaderOwner == this) {
138     // nothing to do
139     return;
140   }
141 
142   aOtherLoaderOwner.SwapFrameLoaders(this, rv);
143 }
144 
SwapFrameLoaders(XULFrameElement & aOtherLoaderOwner,ErrorResult & rv)145 void nsGenericHTMLFrameElement::SwapFrameLoaders(
146     XULFrameElement& aOtherLoaderOwner, ErrorResult& rv) {
147   aOtherLoaderOwner.SwapFrameLoaders(this, rv);
148 }
149 
SwapFrameLoaders(nsFrameLoaderOwner * aOtherLoaderOwner,mozilla::ErrorResult & rv)150 void nsGenericHTMLFrameElement::SwapFrameLoaders(
151     nsFrameLoaderOwner* aOtherLoaderOwner, mozilla::ErrorResult& rv) {
152   if (RefPtr<Document> doc = GetComposedDoc()) {
153     // SwapWithOtherLoader relies on frames being up-to-date.
154     doc->FlushPendingNotifications(FlushType::Frames);
155   }
156 
157   RefPtr<nsFrameLoader> loader = GetFrameLoader();
158   RefPtr<nsFrameLoader> otherLoader = aOtherLoaderOwner->GetFrameLoader();
159   if (!loader || !otherLoader) {
160     rv.Throw(NS_ERROR_NOT_IMPLEMENTED);
161     return;
162   }
163 
164   rv = loader->SwapWithOtherLoader(otherLoader, this, aOtherLoaderOwner);
165 }
166 
LoadSrc()167 void nsGenericHTMLFrameElement::LoadSrc() {
168   EnsureFrameLoader();
169 
170   if (!mFrameLoader) {
171     return;
172   }
173 
174   bool origSrc = !mSrcLoadHappened;
175   mSrcLoadHappened = true;
176   mFrameLoader->LoadFrame(origSrc);
177 }
178 
BindToTree(BindContext & aContext,nsINode & aParent)179 nsresult nsGenericHTMLFrameElement::BindToTree(BindContext& aContext,
180                                                nsINode& aParent) {
181   nsresult rv = nsGenericHTMLElement::BindToTree(aContext, aParent);
182   NS_ENSURE_SUCCESS(rv, rv);
183 
184   if (IsInComposedDoc()) {
185     NS_ASSERTION(!nsContentUtils::IsSafeToRunScript(),
186                  "Missing a script blocker!");
187 
188     AUTO_PROFILER_LABEL("nsGenericHTMLFrameElement::BindToTree", OTHER);
189 
190     // We're in a document now.  Kick off the frame load.
191     LoadSrc();
192   }
193 
194   // We're now in document and scripts may move us, so clear
195   // the mNetworkCreated flag.
196   mNetworkCreated = false;
197   return rv;
198 }
199 
UnbindFromTree(bool aNullParent)200 void nsGenericHTMLFrameElement::UnbindFromTree(bool aNullParent) {
201   if (mFrameLoader) {
202     // This iframe is being taken out of the document, destroy the
203     // iframe's frame loader (doing that will tear down the window in
204     // this iframe).
205     // XXXbz we really want to only partially destroy the frame
206     // loader... we don't want to tear down the docshell.  Food for
207     // later bug.
208     mFrameLoader->Destroy();
209     mFrameLoader = nullptr;
210   }
211 
212   nsGenericHTMLElement::UnbindFromTree(aNullParent);
213 }
214 
215 /* static */
MapScrollingAttribute(const nsAttrValue * aValue)216 ScrollbarPreference nsGenericHTMLFrameElement::MapScrollingAttribute(
217     const nsAttrValue* aValue) {
218   if (aValue && aValue->Type() == nsAttrValue::eEnum) {
219     switch (aValue->GetEnumValue()) {
220       case NS_STYLE_FRAME_OFF:
221       case NS_STYLE_FRAME_NOSCROLL:
222       case NS_STYLE_FRAME_NO:
223         return ScrollbarPreference::Never;
224     }
225   }
226   return ScrollbarPreference::Auto;
227 }
228 
229 /* virtual */
AfterSetAttr(int32_t aNameSpaceID,nsAtom * aName,const nsAttrValue * aValue,const nsAttrValue * aOldValue,nsIPrincipal * aMaybeScriptedPrincipal,bool aNotify)230 nsresult nsGenericHTMLFrameElement::AfterSetAttr(
231     int32_t aNameSpaceID, nsAtom* aName, const nsAttrValue* aValue,
232     const nsAttrValue* aOldValue, nsIPrincipal* aMaybeScriptedPrincipal,
233     bool aNotify) {
234   if (aValue) {
235     nsAttrValueOrString value(aValue);
236     AfterMaybeChangeAttr(aNameSpaceID, aName, &value, aMaybeScriptedPrincipal,
237                          aNotify);
238   } else {
239     AfterMaybeChangeAttr(aNameSpaceID, aName, nullptr, aMaybeScriptedPrincipal,
240                          aNotify);
241   }
242 
243   if (aNameSpaceID == kNameSpaceID_None) {
244     if (aName == nsGkAtoms::scrolling) {
245       if (mFrameLoader) {
246         ScrollbarPreference pref = MapScrollingAttribute(aValue);
247         if (nsDocShell* docshell = mFrameLoader->GetExistingDocShell()) {
248           docshell->SetScrollbarPreference(pref);
249         } else if (auto* child = mFrameLoader->GetBrowserBridgeChild()) {
250           // NOTE(emilio): We intentionally don't deal with the
251           // GetBrowserParent() case, and only deal with the fission iframe
252           // case. We could make it work, but it's a bit of boilerplate for
253           // something that we don't use, and we'd need to think how it
254           // interacts with the scrollbar window flags...
255           child->SendScrollbarPreferenceChanged(pref);
256         }
257       }
258     } else if (aName == nsGkAtoms::mozbrowser) {
259       mReallyIsBrowser = !!aValue && XRE_IsParentProcess() &&
260                          NodePrincipal()->IsSystemPrincipal();
261     }
262   }
263 
264   return nsGenericHTMLElement::AfterSetAttr(
265       aNameSpaceID, aName, aValue, aOldValue, aMaybeScriptedPrincipal, aNotify);
266 }
267 
OnAttrSetButNotChanged(int32_t aNamespaceID,nsAtom * aName,const nsAttrValueOrString & aValue,bool aNotify)268 nsresult nsGenericHTMLFrameElement::OnAttrSetButNotChanged(
269     int32_t aNamespaceID, nsAtom* aName, const nsAttrValueOrString& aValue,
270     bool aNotify) {
271   AfterMaybeChangeAttr(aNamespaceID, aName, &aValue, nullptr, aNotify);
272 
273   return nsGenericHTMLElement::OnAttrSetButNotChanged(aNamespaceID, aName,
274                                                       aValue, aNotify);
275 }
276 
AfterMaybeChangeAttr(int32_t aNamespaceID,nsAtom * aName,const nsAttrValueOrString * aValue,nsIPrincipal * aMaybeScriptedPrincipal,bool aNotify)277 void nsGenericHTMLFrameElement::AfterMaybeChangeAttr(
278     int32_t aNamespaceID, nsAtom* aName, const nsAttrValueOrString* aValue,
279     nsIPrincipal* aMaybeScriptedPrincipal, bool aNotify) {
280   if (aNamespaceID == kNameSpaceID_None) {
281     if (aName == nsGkAtoms::src) {
282       mSrcTriggeringPrincipal = nsContentUtils::GetAttrTriggeringPrincipal(
283           this, aValue ? aValue->String() : u""_ns, aMaybeScriptedPrincipal);
284       if (!IsHTMLElement(nsGkAtoms::iframe) ||
285           !HasAttr(kNameSpaceID_None, nsGkAtoms::srcdoc)) {
286         // Don't propagate error here. The attribute was successfully
287         // set or removed; that's what we should reflect.
288         LoadSrc();
289       }
290     } else if (aName == nsGkAtoms::name) {
291       // Propagate "name" to the browsing context per HTML5.
292       RefPtr<BrowsingContext> bc =
293           mFrameLoader ? mFrameLoader->GetExtantBrowsingContext() : nullptr;
294       if (bc) {
295         MOZ_ALWAYS_SUCCEEDS(bc->SetName(aValue ? aValue->String() : u""_ns));
296       }
297     }
298   }
299 }
300 
DestroyContent()301 void nsGenericHTMLFrameElement::DestroyContent() {
302   if (mFrameLoader) {
303     mFrameLoader->Destroy();
304     mFrameLoader = nullptr;
305   }
306 
307   nsGenericHTMLElement::DestroyContent();
308 }
309 
CopyInnerTo(Element * aDest)310 nsresult nsGenericHTMLFrameElement::CopyInnerTo(Element* aDest) {
311   nsresult rv = nsGenericHTMLElement::CopyInnerTo(aDest);
312   NS_ENSURE_SUCCESS(rv, rv);
313 
314   Document* doc = aDest->OwnerDoc();
315   if (doc->IsStaticDocument() && mFrameLoader) {
316     nsGenericHTMLFrameElement* dest =
317         static_cast<nsGenericHTMLFrameElement*>(aDest);
318     doc->AddPendingFrameStaticClone(dest, mFrameLoader);
319   }
320 
321   return rv;
322 }
323 
IsHTMLFocusable(bool aWithMouse,bool * aIsFocusable,int32_t * aTabIndex)324 bool nsGenericHTMLFrameElement::IsHTMLFocusable(bool aWithMouse,
325                                                 bool* aIsFocusable,
326                                                 int32_t* aTabIndex) {
327   if (nsGenericHTMLElement::IsHTMLFocusable(aWithMouse, aIsFocusable,
328                                             aTabIndex)) {
329     return true;
330   }
331 
332   *aIsFocusable = nsContentUtils::IsSubDocumentTabbable(this);
333 
334   if (!*aIsFocusable && aTabIndex) {
335     *aTabIndex = -1;
336   }
337 
338   return false;
339 }
340 
341 /**
342  * Return true if this frame element really is a mozbrowser.  (It
343  * needs to have the right attributes, and its creator must have the right
344  * permissions.)
345  */
346 /* [infallible] */
GetReallyIsBrowser(bool * aOut)347 nsresult nsGenericHTMLFrameElement::GetReallyIsBrowser(bool* aOut) {
348   *aOut = mReallyIsBrowser;
349   return NS_OK;
350 }
351 
352 NS_IMETHODIMP
InitializeBrowserAPI()353 nsGenericHTMLFrameElement::InitializeBrowserAPI() {
354   MOZ_ASSERT(mFrameLoader);
355   InitBrowserElementAPI();
356   return NS_OK;
357 }
358 
359 NS_IMETHODIMP
DestroyBrowserFrameScripts()360 nsGenericHTMLFrameElement::DestroyBrowserFrameScripts() {
361   MOZ_ASSERT(mFrameLoader);
362   DestroyBrowserElementFrameScripts();
363   return NS_OK;
364 }
365