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