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 #include "mozilla/dom/HTMLIFrameElement.h"
8 #include "mozilla/dom/HTMLIFrameElementBinding.h"
9 #include "mozilla/dom/FeaturePolicy.h"
10 #include "mozilla/MappedDeclarations.h"
11 #include "mozilla/NullPrincipal.h"
12 #include "mozilla/StaticPrefs_dom.h"
13 #include "nsMappedAttributes.h"
14 #include "nsAttrValueInlines.h"
15 #include "nsError.h"
16 #include "nsStyleConsts.h"
17 #include "nsContentUtils.h"
18 #include "nsSandboxFlags.h"
19 #include "nsNetUtil.h"
20
21 NS_IMPL_NS_NEW_HTML_ELEMENT_CHECK_PARSER(IFrame)
22
23 namespace mozilla {
24 namespace dom {
25
26 NS_IMPL_CYCLE_COLLECTION_CLASS(HTMLIFrameElement)
27
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
33
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
39
40 NS_IMPL_ADDREF_INHERITED(HTMLIFrameElement, nsGenericHTMLFrameElement)
41 NS_IMPL_RELEASE_INHERITED(HTMLIFrameElement, nsGenericHTMLFrameElement)
42
43 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(HTMLIFrameElement)
44 NS_INTERFACE_MAP_END_INHERITING(nsGenericHTMLFrameElement)
45
46 // static
47 const DOMTokenListSupportedToken HTMLIFrameElement::sSupportedSandboxTokens[] =
48 {
49 #define SANDBOX_KEYWORD(string, atom, flags) string,
50 #include "IframeSandboxKeywordList.h"
51 #undef SANDBOX_KEYWORD
52 nullptr};
53
HTMLIFrameElement(already_AddRefed<mozilla::dom::NodeInfo> && aNodeInfo,FromParser aFromParser)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();
61 MOZ_ASSERT(origin);
62 mFeaturePolicy->SetDefaultOrigin(origin);
63 }
64
65 HTMLIFrameElement::~HTMLIFrameElement() = default;
66
NS_IMPL_ELEMENT_CLONE(HTMLIFrameElement)67 NS_IMPL_ELEMENT_CLONE(HTMLIFrameElement)
68
69 void HTMLIFrameElement::BindToBrowsingContext(
70 BrowsingContext* aBrowsingContext) {
71 if (StaticPrefs::dom_security_featurePolicy_enabled()) {
72 RefreshFeaturePolicy(true /* parse the feature policy attribute */);
73 }
74 }
75
ParseAttribute(int32_t aNamespaceID,nsAtom * aAttribute,const nsAString & aValue,nsIPrincipal * aMaybeScriptedPrincipal,nsAttrValue & aResult)76 bool HTMLIFrameElement::ParseAttribute(int32_t aNamespaceID, nsAtom* aAttribute,
77 const nsAString& aValue,
78 nsIPrincipal* aMaybeScriptedPrincipal,
79 nsAttrValue& aResult) {
80 if (aNamespaceID == kNameSpaceID_None) {
81 if (aAttribute == nsGkAtoms::marginwidth) {
82 return aResult.ParseNonNegativeIntValue(aValue);
83 }
84 if (aAttribute == nsGkAtoms::marginheight) {
85 return aResult.ParseNonNegativeIntValue(aValue);
86 }
87 if (aAttribute == nsGkAtoms::width) {
88 return aResult.ParseHTMLDimension(aValue);
89 }
90 if (aAttribute == nsGkAtoms::height) {
91 return aResult.ParseHTMLDimension(aValue);
92 }
93 if (aAttribute == nsGkAtoms::frameborder) {
94 return ParseFrameborderValue(aValue, aResult);
95 }
96 if (aAttribute == nsGkAtoms::scrolling) {
97 return ParseScrollingValue(aValue, aResult);
98 }
99 if (aAttribute == nsGkAtoms::align) {
100 return ParseAlignValue(aValue, aResult);
101 }
102 if (aAttribute == nsGkAtoms::sandbox) {
103 aResult.ParseAtomArray(aValue);
104 return true;
105 }
106 }
107
108 return nsGenericHTMLFrameElement::ParseAttribute(
109 aNamespaceID, aAttribute, aValue, aMaybeScriptedPrincipal, aResult);
110 }
111
MapAttributesIntoRule(const nsMappedAttributes * aAttributes,MappedDeclarations & aDecls)112 void HTMLIFrameElement::MapAttributesIntoRule(
113 const nsMappedAttributes* aAttributes, MappedDeclarations& aDecls) {
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 = aAttributes->GetAttr(nsGkAtoms::frameborder);
118 if (value && value->Type() == nsAttrValue::eEnum) {
119 int32_t frameborder = value->GetEnumValue();
120 if (NS_STYLE_FRAME_0 == frameborder || NS_STYLE_FRAME_NO == frameborder ||
121 NS_STYLE_FRAME_OFF == frameborder) {
122 aDecls.SetPixelValueIfUnset(eCSSProperty_border_top_width, 0.0f);
123 aDecls.SetPixelValueIfUnset(eCSSProperty_border_right_width, 0.0f);
124 aDecls.SetPixelValueIfUnset(eCSSProperty_border_bottom_width, 0.0f);
125 aDecls.SetPixelValueIfUnset(eCSSProperty_border_left_width, 0.0f);
126 }
127 }
128
129 nsGenericHTMLElement::MapImageSizeAttributesInto(aAttributes, aDecls);
130 nsGenericHTMLElement::MapImageAlignAttributeInto(aAttributes, aDecls);
131 nsGenericHTMLElement::MapCommonAttributesInto(aAttributes, aDecls);
132 }
133
NS_IMETHODIMP_(bool)134 NS_IMETHODIMP_(bool)
135 HTMLIFrameElement::IsAttributeMapped(const nsAtom* aAttribute) const {
136 static const MappedAttributeEntry attributes[] = {
137 {nsGkAtoms::width},
138 {nsGkAtoms::height},
139 {nsGkAtoms::frameborder},
140 {nullptr},
141 };
142
143 static const MappedAttributeEntry* const map[] = {
144 attributes,
145 sImageAlignAttributeMap,
146 sCommonAttributeMap,
147 };
148
149 return FindAttributeDependence(aAttribute, map);
150 }
151
GetAttributeMappingFunction() const152 nsMapRuleToAttributesFunc HTMLIFrameElement::GetAttributeMappingFunction()
153 const {
154 return &MapAttributesIntoRule;
155 }
156
HasAllowFullscreenAttribute() const157 bool HTMLIFrameElement::HasAllowFullscreenAttribute() const {
158 return GetBoolAttr(nsGkAtoms::allowfullscreen) ||
159 GetBoolAttr(nsGkAtoms::mozallowfullscreen);
160 }
161
AllowFullscreen() const162 bool HTMLIFrameElement::AllowFullscreen() const {
163 if (StaticPrefs::dom_security_featurePolicy_enabled()) {
164 // The feature policy check in Document::GetFullscreenError already accounts
165 // for the allow* attributes, so we're done.
166 return true;
167 }
168 return HasAllowFullscreenAttribute();
169 }
170
AfterSetAttr(int32_t aNameSpaceID,nsAtom * aName,const nsAttrValue * aValue,const nsAttrValue * aOldValue,nsIPrincipal * aMaybeScriptedPrincipal,bool aNotify)171 nsresult HTMLIFrameElement::AfterSetAttr(int32_t aNameSpaceID, nsAtom* aName,
172 const nsAttrValue* aValue,
173 const nsAttrValue* aOldValue,
174 nsIPrincipal* aMaybeScriptedPrincipal,
175 bool aNotify) {
176 AfterMaybeChangeAttr(aNameSpaceID, aName, aNotify);
177
178 if (aNameSpaceID == kNameSpaceID_None) {
179 if (aName == nsGkAtoms::sandbox) {
180 if (mFrameLoader) {
181 // If we have an nsFrameLoader, apply the new sandbox flags.
182 // Since this is called after the setter, the sandbox flags have
183 // alreay been updated.
184 mFrameLoader->ApplySandboxFlags(GetSandboxFlags());
185 }
186 }
187
188 if (StaticPrefs::dom_security_featurePolicy_enabled()) {
189 if (aName == nsGkAtoms::allow || aName == nsGkAtoms::src ||
190 aName == nsGkAtoms::srcdoc || aName == nsGkAtoms::sandbox) {
191 RefreshFeaturePolicy(true /* parse the feature policy attribute */);
192 } else if (aName == nsGkAtoms::allowfullscreen ||
193 aName == nsGkAtoms::mozallowfullscreen ||
194 aName == nsGkAtoms::allowpaymentrequest) {
195 RefreshFeaturePolicy(false /* parse the feature policy attribute */);
196 }
197 }
198 }
199 return nsGenericHTMLFrameElement::AfterSetAttr(
200 aNameSpaceID, aName, aValue, aOldValue, aMaybeScriptedPrincipal, aNotify);
201 }
202
OnAttrSetButNotChanged(int32_t aNamespaceID,nsAtom * aName,const nsAttrValueOrString & aValue,bool aNotify)203 nsresult HTMLIFrameElement::OnAttrSetButNotChanged(
204 int32_t aNamespaceID, nsAtom* aName, const nsAttrValueOrString& aValue,
205 bool aNotify) {
206 AfterMaybeChangeAttr(aNamespaceID, aName, aNotify);
207
208 return nsGenericHTMLFrameElement::OnAttrSetButNotChanged(aNamespaceID, aName,
209 aValue, aNotify);
210 }
211
AfterMaybeChangeAttr(int32_t aNamespaceID,nsAtom * aName,bool aNotify)212 void HTMLIFrameElement::AfterMaybeChangeAttr(int32_t aNamespaceID,
213 nsAtom* aName, bool aNotify) {
214 if (aNamespaceID == kNameSpaceID_None) {
215 if (aName == nsGkAtoms::srcdoc) {
216 // Don't propagate errors from LoadSrc. The attribute was successfully
217 // set/unset, that's what we should reflect.
218 LoadSrc();
219 }
220 }
221 }
222
GetSandboxFlags() const223 uint32_t HTMLIFrameElement::GetSandboxFlags() const {
224 const nsAttrValue* sandboxAttr = GetParsedAttr(nsGkAtoms::sandbox);
225 // No sandbox attribute, no sandbox flags.
226 if (!sandboxAttr) {
227 return SANDBOXED_NONE;
228 }
229 return nsContentUtils::ParseSandboxAttributeToFlags(sandboxAttr);
230 }
231
WrapNode(JSContext * aCx,JS::Handle<JSObject * > aGivenProto)232 JSObject* HTMLIFrameElement::WrapNode(JSContext* aCx,
233 JS::Handle<JSObject*> aGivenProto) {
234 return HTMLIFrameElement_Binding::Wrap(aCx, this, aGivenProto);
235 }
236
FeaturePolicy() const237 mozilla::dom::FeaturePolicy* HTMLIFrameElement::FeaturePolicy() const {
238 return mFeaturePolicy;
239 }
240
MaybeStoreCrossOriginFeaturePolicy()241 void HTMLIFrameElement::MaybeStoreCrossOriginFeaturePolicy() {
242 if (!mFrameLoader) {
243 return;
244 }
245
246 // If the browsingContext is not ready (because docshell is dead), don't try
247 // to create one.
248 if (!mFrameLoader->IsRemoteFrame() && !mFrameLoader->GetExistingDocShell()) {
249 return;
250 }
251
252 RefPtr<BrowsingContext> browsingContext = mFrameLoader->GetBrowsingContext();
253
254 if (!browsingContext || !browsingContext->IsContentSubframe()) {
255 return;
256 }
257
258 // If we are in subframe cross origin, store the featurePolicy to
259 // browsingContext
260 nsPIDOMWindowOuter* topWindow = browsingContext->Top()->GetDOMWindow();
261 if (NS_WARN_IF(!topWindow)) {
262 return;
263 }
264
265 Document* topLevelDocument = topWindow->GetExtantDoc();
266 if (NS_WARN_IF(!topLevelDocument)) {
267 return;
268 }
269
270 if (!NS_SUCCEEDED(nsContentUtils::CheckSameOrigin(topLevelDocument, this))) {
271 return;
272 }
273
274 browsingContext->SetFeaturePolicy(mFeaturePolicy);
275 }
276
277 already_AddRefed<nsIPrincipal>
GetFeaturePolicyDefaultOrigin() const278 HTMLIFrameElement::GetFeaturePolicyDefaultOrigin() const {
279 nsCOMPtr<nsIPrincipal> principal;
280
281 if (HasAttr(kNameSpaceID_None, nsGkAtoms::srcdoc)) {
282 principal = NodePrincipal();
283 return principal.forget();
284 }
285
286 nsCOMPtr<nsIURI> nodeURI;
287 if (GetURIAttr(nsGkAtoms::src, nullptr, getter_AddRefs(nodeURI)) && nodeURI) {
288 principal = BasePrincipal::CreateContentPrincipal(
289 nodeURI, BasePrincipal::Cast(NodePrincipal())->OriginAttributesRef());
290 }
291
292 if (!principal) {
293 principal = NodePrincipal();
294 }
295
296 return principal.forget();
297 }
298
RefreshFeaturePolicy(bool aParseAllowAttribute)299 void HTMLIFrameElement::RefreshFeaturePolicy(bool aParseAllowAttribute) {
300 MOZ_ASSERT(StaticPrefs::dom_security_featurePolicy_enabled());
301
302 if (aParseAllowAttribute) {
303 mFeaturePolicy->ResetDeclaredPolicy();
304
305 // The origin can change if 'src' and 'srcdoc' attributes change.
306 nsCOMPtr<nsIPrincipal> origin = GetFeaturePolicyDefaultOrigin();
307 MOZ_ASSERT(origin);
308 mFeaturePolicy->SetDefaultOrigin(origin);
309
310 nsAutoString allow;
311 GetAttr(nsGkAtoms::allow, allow);
312
313 if (!allow.IsEmpty()) {
314 // Set or reset the FeaturePolicy directives.
315 mFeaturePolicy->SetDeclaredPolicy(OwnerDoc(), allow, NodePrincipal(),
316 origin);
317 }
318 }
319
320 if (AllowPaymentRequest()) {
321 mFeaturePolicy->MaybeSetAllowedPolicy(NS_LITERAL_STRING("payment"));
322 }
323
324 if (HasAllowFullscreenAttribute()) {
325 mFeaturePolicy->MaybeSetAllowedPolicy(u"fullscreen"_ns);
326 }
327
328 mFeaturePolicy->InheritPolicy(OwnerDoc()->FeaturePolicy());
329 MaybeStoreCrossOriginFeaturePolicy();
330 }
331
332 } // namespace dom
333 } // namespace mozilla
334