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