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/. */
7 #include "mozilla/dom/DOMIntersectionObserver.h"
8 #include "mozilla/dom/HTMLIFrameElement.h"
9 #include "mozilla/dom/ContentChild.h"
10 #include "mozilla/dom/Document.h"
11 #include "mozilla/dom/HTMLIFrameElementBinding.h"
12 #include "mozilla/dom/FeaturePolicy.h"
13 #include "mozilla/MappedDeclarationsBuilder.h"
14 #include "mozilla/NullPrincipal.h"
15 #include "mozilla/StaticPrefs_dom.h"
16 #include "nsSubDocumentFrame.h"
18 #include "nsContentUtils.h"
19 #include "nsSandboxFlags.h"
20 #include "nsNetUtil.h"
22 NS_IMPL_NS_NEW_HTML_ELEMENT_CHECK_PARSER(IFrame
)
24 namespace mozilla::dom
{
26 NS_IMPL_CYCLE_COLLECTION_CLASS(HTMLIFrameElement
)
28 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(HTMLIFrameElement
,
29 nsGenericHTMLFrameElement
)
30 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFeaturePolicy
)
31 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSandbox
)
32 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
34 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(HTMLIFrameElement
,
35 nsGenericHTMLFrameElement
)
36 NS_IMPL_CYCLE_COLLECTION_UNLINK(mFeaturePolicy
)
37 NS_IMPL_CYCLE_COLLECTION_UNLINK(mSandbox
)
38 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
40 NS_IMPL_ADDREF_INHERITED(HTMLIFrameElement
, nsGenericHTMLFrameElement
)
41 NS_IMPL_RELEASE_INHERITED(HTMLIFrameElement
, nsGenericHTMLFrameElement
)
43 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(HTMLIFrameElement
)
44 NS_INTERFACE_MAP_END_INHERITING(nsGenericHTMLFrameElement
)
47 const DOMTokenListSupportedToken
HTMLIFrameElement::sSupportedSandboxTokens
[] =
49 #define SANDBOX_KEYWORD(string, atom, flags) string,
50 #include "IframeSandboxKeywordList.h"
51 #undef SANDBOX_KEYWORD
54 HTMLIFrameElement::HTMLIFrameElement(
55 already_AddRefed
<mozilla::dom::NodeInfo
>&& aNodeInfo
,
56 FromParser aFromParser
)
57 : nsGenericHTMLFrameElement(std::move(aNodeInfo
), aFromParser
) {
58 // We always need a featurePolicy, even if not exposed.
59 mFeaturePolicy
= new mozilla::dom::FeaturePolicy(this);
60 nsCOMPtr
<nsIPrincipal
> origin
= GetFeaturePolicyDefaultOrigin();
62 mFeaturePolicy
->SetDefaultOrigin(origin
);
65 HTMLIFrameElement::~HTMLIFrameElement() = default;
67 NS_IMPL_ELEMENT_CLONE(HTMLIFrameElement
)
69 void HTMLIFrameElement::BindToBrowsingContext(BrowsingContext
*) {
70 RefreshFeaturePolicy(true /* parse the feature policy attribute */);
73 bool HTMLIFrameElement::ParseAttribute(int32_t aNamespaceID
, nsAtom
* aAttribute
,
74 const nsAString
& aValue
,
75 nsIPrincipal
* aMaybeScriptedPrincipal
,
76 nsAttrValue
& aResult
) {
77 if (aNamespaceID
== kNameSpaceID_None
) {
78 if (aAttribute
== nsGkAtoms::marginwidth
) {
79 return aResult
.ParseNonNegativeIntValue(aValue
);
81 if (aAttribute
== nsGkAtoms::marginheight
) {
82 return aResult
.ParseNonNegativeIntValue(aValue
);
84 if (aAttribute
== nsGkAtoms::width
) {
85 return aResult
.ParseHTMLDimension(aValue
);
87 if (aAttribute
== nsGkAtoms::height
) {
88 return aResult
.ParseHTMLDimension(aValue
);
90 if (aAttribute
== nsGkAtoms::frameborder
) {
91 return ParseFrameborderValue(aValue
, aResult
);
93 if (aAttribute
== nsGkAtoms::scrolling
) {
94 return ParseScrollingValue(aValue
, aResult
);
96 if (aAttribute
== nsGkAtoms::align
) {
97 return ParseAlignValue(aValue
, aResult
);
99 if (aAttribute
== nsGkAtoms::sandbox
) {
100 aResult
.ParseAtomArray(aValue
);
103 if (aAttribute
== nsGkAtoms::loading
) {
104 return ParseLoadingAttribute(aValue
, aResult
);
108 return nsGenericHTMLFrameElement::ParseAttribute(
109 aNamespaceID
, aAttribute
, aValue
, aMaybeScriptedPrincipal
, aResult
);
112 void HTMLIFrameElement::MapAttributesIntoRule(
113 MappedDeclarationsBuilder
& aBuilder
) {
114 // frameborder: 0 | 1 (| NO | YES in quirks mode)
115 // If frameborder is 0 or No, set border to 0
116 // else leave it as the value set in html.css
117 const nsAttrValue
* value
= aBuilder
.GetAttr(nsGkAtoms::frameborder
);
118 if (value
&& value
->Type() == nsAttrValue::eEnum
) {
119 auto frameborder
= static_cast<FrameBorderProperty
>(value
->GetEnumValue());
120 if (FrameBorderProperty::No
== frameborder
||
121 FrameBorderProperty::Zero
== frameborder
) {
122 aBuilder
.SetPixelValueIfUnset(eCSSProperty_border_top_width
, 0.0f
);
123 aBuilder
.SetPixelValueIfUnset(eCSSProperty_border_right_width
, 0.0f
);
124 aBuilder
.SetPixelValueIfUnset(eCSSProperty_border_bottom_width
, 0.0f
);
125 aBuilder
.SetPixelValueIfUnset(eCSSProperty_border_left_width
, 0.0f
);
129 nsGenericHTMLElement::MapImageSizeAttributesInto(aBuilder
);
130 nsGenericHTMLElement::MapImageAlignAttributeInto(aBuilder
);
131 nsGenericHTMLElement::MapCommonAttributesInto(aBuilder
);
135 HTMLIFrameElement::IsAttributeMapped(const nsAtom
* aAttribute
) const {
136 static const MappedAttributeEntry attributes
[] = {
139 {nsGkAtoms::frameborder
},
143 static const MappedAttributeEntry
* const map
[] = {
145 sImageAlignAttributeMap
,
149 return FindAttributeDependence(aAttribute
, map
);
152 nsMapRuleToAttributesFunc
HTMLIFrameElement::GetAttributeMappingFunction()
154 return &MapAttributesIntoRule
;
157 void HTMLIFrameElement::AfterSetAttr(int32_t aNameSpaceID
, nsAtom
* aName
,
158 const nsAttrValue
* aValue
,
159 const nsAttrValue
* aOldValue
,
160 nsIPrincipal
* aMaybeScriptedPrincipal
,
162 AfterMaybeChangeAttr(aNameSpaceID
, aName
, aNotify
);
164 if (aNameSpaceID
== kNameSpaceID_None
) {
165 if (aName
== nsGkAtoms::loading
) {
166 if (aValue
&& Loading(aValue
->GetEnumValue()) == Loading::Lazy
) {
168 } else if (aOldValue
&&
169 Loading(aOldValue
->GetEnumValue()) == Loading::Lazy
) {
174 // If lazy loading and src set, set lazy loading again as we are doing a new
175 // load (lazy loading is unset after a load is complete).
176 if ((aName
== nsGkAtoms::src
|| aName
== nsGkAtoms::srcdoc
) &&
177 LoadingState() == Loading::Lazy
) {
181 if (aName
== nsGkAtoms::sandbox
) {
183 // If we have an nsFrameLoader, apply the new sandbox flags.
184 // Since this is called after the setter, the sandbox flags have
185 // alreay been updated.
186 mFrameLoader
->ApplySandboxFlags(GetSandboxFlags());
190 if (aName
== nsGkAtoms::allow
|| aName
== nsGkAtoms::src
||
191 aName
== nsGkAtoms::srcdoc
|| aName
== nsGkAtoms::sandbox
) {
192 RefreshFeaturePolicy(true /* parse the feature policy attribute */);
193 } else if (aName
== nsGkAtoms::allowfullscreen
) {
194 RefreshFeaturePolicy(false /* parse the feature policy attribute */);
198 return nsGenericHTMLFrameElement::AfterSetAttr(
199 aNameSpaceID
, aName
, aValue
, aOldValue
, aMaybeScriptedPrincipal
, aNotify
);
202 void HTMLIFrameElement::OnAttrSetButNotChanged(
203 int32_t aNamespaceID
, nsAtom
* aName
, const nsAttrValueOrString
& aValue
,
205 AfterMaybeChangeAttr(aNamespaceID
, aName
, aNotify
);
207 return nsGenericHTMLFrameElement::OnAttrSetButNotChanged(aNamespaceID
, aName
,
211 void HTMLIFrameElement::AfterMaybeChangeAttr(int32_t aNamespaceID
,
212 nsAtom
* aName
, bool aNotify
) {
213 if (aNamespaceID
== kNameSpaceID_None
) {
214 if (aName
== nsGkAtoms::srcdoc
) {
215 // Don't propagate errors from LoadSrc. The attribute was successfully
216 // set/unset, that's what we should reflect.
222 uint32_t HTMLIFrameElement::GetSandboxFlags() const {
223 const nsAttrValue
* sandboxAttr
= GetParsedAttr(nsGkAtoms::sandbox
);
224 // No sandbox attribute, no sandbox flags.
226 return SANDBOXED_NONE
;
228 return nsContentUtils::ParseSandboxAttributeToFlags(sandboxAttr
);
231 JSObject
* HTMLIFrameElement::WrapNode(JSContext
* aCx
,
232 JS::Handle
<JSObject
*> aGivenProto
) {
233 return HTMLIFrameElement_Binding::Wrap(aCx
, this, aGivenProto
);
236 mozilla::dom::FeaturePolicy
* HTMLIFrameElement::FeaturePolicy() const {
237 return mFeaturePolicy
;
240 void HTMLIFrameElement::MaybeStoreCrossOriginFeaturePolicy() {
245 // If the browsingContext is not ready (because docshell is dead), don't try
247 if (!mFrameLoader
->IsRemoteFrame() && !mFrameLoader
->GetExistingDocShell()) {
251 RefPtr
<BrowsingContext
> browsingContext
= mFrameLoader
->GetBrowsingContext();
253 if (!browsingContext
|| !browsingContext
->IsContentSubframe()) {
257 if (ContentChild
* cc
= ContentChild::GetSingleton()) {
258 Unused
<< cc
->SendSetContainerFeaturePolicy(browsingContext
,
263 already_AddRefed
<nsIPrincipal
>
264 HTMLIFrameElement::GetFeaturePolicyDefaultOrigin() const {
265 nsCOMPtr
<nsIPrincipal
> principal
;
267 if (HasAttr(nsGkAtoms::srcdoc
)) {
268 principal
= NodePrincipal();
269 return principal
.forget();
272 nsCOMPtr
<nsIURI
> nodeURI
;
273 if (GetURIAttr(nsGkAtoms::src
, nullptr, getter_AddRefs(nodeURI
)) && nodeURI
) {
274 principal
= BasePrincipal::CreateContentPrincipal(
275 nodeURI
, BasePrincipal::Cast(NodePrincipal())->OriginAttributesRef());
279 principal
= NodePrincipal();
282 return principal
.forget();
285 void HTMLIFrameElement::RefreshFeaturePolicy(bool aParseAllowAttribute
) {
286 if (aParseAllowAttribute
) {
287 mFeaturePolicy
->ResetDeclaredPolicy();
289 // The origin can change if 'src' and 'srcdoc' attributes change.
290 nsCOMPtr
<nsIPrincipal
> origin
= GetFeaturePolicyDefaultOrigin();
292 mFeaturePolicy
->SetDefaultOrigin(origin
);
295 GetAttr(nsGkAtoms::allow
, allow
);
297 if (!allow
.IsEmpty()) {
298 // Set or reset the FeaturePolicy directives.
299 mFeaturePolicy
->SetDeclaredPolicy(OwnerDoc(), allow
, NodePrincipal(),
304 if (AllowFullscreen()) {
305 mFeaturePolicy
->MaybeSetAllowedPolicy(u
"fullscreen"_ns
);
308 mFeaturePolicy
->InheritPolicy(OwnerDoc()->FeaturePolicy());
309 MaybeStoreCrossOriginFeaturePolicy();
312 void HTMLIFrameElement::UpdateLazyLoadState() {
313 // Store current base URI and referrer policy in the lazy load state.
314 mLazyLoadState
.mBaseURI
= GetBaseURI();
315 mLazyLoadState
.mReferrerPolicy
= GetReferrerPolicyAsEnum();
318 nsresult
HTMLIFrameElement::BindToTree(BindContext
& aContext
,
320 // Update lazy load state on bind to tree again if lazy loading, as the
321 // loading attribute could be set before others.
323 UpdateLazyLoadState();
326 return nsGenericHTMLFrameElement::BindToTree(aContext
, aParent
);
329 void HTMLIFrameElement::SetLazyLoading() {
334 if (!StaticPrefs::dom_iframe_lazy_loading_enabled()) {
338 // https://html.spec.whatwg.org/multipage/urls-and-fetching.html#will-lazy-load-element-steps
339 // "If scripting is disabled for element, then return false."
340 Document
* doc
= OwnerDoc();
341 if (!doc
->IsScriptEnabled() || doc
->IsStaticDocument()) {
345 doc
->EnsureLazyLoadObserver().Observe(*this);
348 UpdateLazyLoadState();
351 void HTMLIFrameElement::StopLazyLoading() {
356 mLazyLoading
= false;
358 Document
* doc
= OwnerDoc();
359 if (auto* obs
= doc
->GetLazyLoadObserver()) {
360 obs
->Unobserve(*this);
365 mLazyLoadState
.Clear();
366 if (nsSubDocumentFrame
* ourFrame
= do_QueryFrame(GetPrimaryFrame())) {
367 ourFrame
->ResetFrameLoader(nsSubDocumentFrame::RetainPaintData::No
);
371 void HTMLIFrameElement::NodeInfoChanged(Document
* aOldDoc
) {
372 nsGenericHTMLElement::NodeInfoChanged(aOldDoc
);
375 aOldDoc
->GetLazyLoadObserver()->Unobserve(*this);
376 mLazyLoading
= false;
381 } // namespace mozilla::dom