HTMLIFrameElement.cpp (14360B)
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 9 #include "mozilla/MappedDeclarationsBuilder.h" 10 #include "mozilla/NullPrincipal.h" 11 #include "mozilla/StaticPrefs_dom.h" 12 #include "mozilla/dom/ContentChild.h" 13 #include "mozilla/dom/DOMIntersectionObserver.h" 14 #include "mozilla/dom/Document.h" 15 #include "mozilla/dom/FeaturePolicy.h" 16 #include "mozilla/dom/HTMLIFrameElementBinding.h" 17 #include "mozilla/dom/TrustedTypeUtils.h" 18 #include "mozilla/dom/TrustedTypesConstants.h" 19 #include "nsContentUtils.h" 20 #include "nsError.h" 21 #include "nsNetUtil.h" 22 #include "nsSandboxFlags.h" 23 #include "nsSubDocumentFrame.h" 24 25 NS_IMPL_NS_NEW_HTML_ELEMENT_CHECK_PARSER(IFrame) 26 27 namespace mozilla::dom { 28 29 NS_IMPL_CYCLE_COLLECTION_CLASS(HTMLIFrameElement) 30 31 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(HTMLIFrameElement, 32 nsGenericHTMLFrameElement) 33 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFeaturePolicy) 34 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSandbox) 35 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END 36 37 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(HTMLIFrameElement, 38 nsGenericHTMLFrameElement) 39 NS_IMPL_CYCLE_COLLECTION_UNLINK(mFeaturePolicy) 40 NS_IMPL_CYCLE_COLLECTION_UNLINK(mSandbox) 41 NS_IMPL_CYCLE_COLLECTION_UNLINK_END 42 43 NS_IMPL_ADDREF_INHERITED(HTMLIFrameElement, nsGenericHTMLFrameElement) 44 NS_IMPL_RELEASE_INHERITED(HTMLIFrameElement, nsGenericHTMLFrameElement) 45 46 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(HTMLIFrameElement) 47 NS_INTERFACE_MAP_END_INHERITING(nsGenericHTMLFrameElement) 48 49 // static 50 const DOMTokenListSupportedToken HTMLIFrameElement::sSupportedSandboxTokens[] = 51 { 52 #define SANDBOX_KEYWORD(string, atom, flags) string, 53 #include "IframeSandboxKeywordList.h" 54 #undef SANDBOX_KEYWORD 55 nullptr}; 56 57 HTMLIFrameElement::HTMLIFrameElement( 58 already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo, 59 FromParser aFromParser) 60 : nsGenericHTMLFrameElement(std::move(aNodeInfo), aFromParser) { 61 // We always need a featurePolicy, even if not exposed. 62 mFeaturePolicy = new mozilla::dom::FeaturePolicy(this); 63 nsCOMPtr<nsIPrincipal> origin = GetFeaturePolicyDefaultOrigin(); 64 MOZ_ASSERT(origin); 65 mFeaturePolicy->SetDefaultOrigin(origin); 66 } 67 68 HTMLIFrameElement::~HTMLIFrameElement() = default; 69 70 NS_IMPL_ELEMENT_CLONE(HTMLIFrameElement) 71 72 void HTMLIFrameElement::BindToBrowsingContext(BrowsingContext*) { 73 RefreshFeaturePolicy(true /* parse the feature policy attribute */); 74 RefreshEmbedderReferrerPolicy( 75 ReferrerPolicyFromAttr(GetParsedAttr(nsGkAtoms::referrerpolicy))); 76 } 77 78 bool HTMLIFrameElement::ParseAttribute(int32_t aNamespaceID, nsAtom* aAttribute, 79 const nsAString& aValue, 80 nsIPrincipal* aMaybeScriptedPrincipal, 81 nsAttrValue& aResult) { 82 if (aNamespaceID == kNameSpaceID_None) { 83 if (aAttribute == nsGkAtoms::marginwidth) { 84 return aResult.ParseNonNegativeIntValue(aValue); 85 } 86 if (aAttribute == nsGkAtoms::marginheight) { 87 return aResult.ParseNonNegativeIntValue(aValue); 88 } 89 if (aAttribute == nsGkAtoms::width) { 90 return aResult.ParseHTMLDimension(aValue); 91 } 92 if (aAttribute == nsGkAtoms::height) { 93 return aResult.ParseHTMLDimension(aValue); 94 } 95 if (aAttribute == nsGkAtoms::frameborder) { 96 return ParseFrameborderValue(aValue, aResult); 97 } 98 if (aAttribute == nsGkAtoms::scrolling) { 99 return ParseScrollingValue(aValue, aResult); 100 } 101 if (aAttribute == nsGkAtoms::align) { 102 return ParseAlignValue(aValue, aResult); 103 } 104 if (aAttribute == nsGkAtoms::sandbox) { 105 aResult.ParseAtomArray(aValue); 106 return true; 107 } 108 if (aAttribute == nsGkAtoms::loading) { 109 return ParseLoadingAttribute(aValue, aResult); 110 } 111 } 112 113 return nsGenericHTMLFrameElement::ParseAttribute( 114 aNamespaceID, aAttribute, aValue, aMaybeScriptedPrincipal, aResult); 115 } 116 117 void HTMLIFrameElement::MapAttributesIntoRule( 118 MappedDeclarationsBuilder& aBuilder) { 119 // frameborder: 0 | 1 (| NO | YES in quirks mode) 120 // If frameborder is 0 or No, set border to 0 121 // else leave it as the value set in html.css 122 const nsAttrValue* value = aBuilder.GetAttr(nsGkAtoms::frameborder); 123 if (value && value->Type() == nsAttrValue::eEnum) { 124 auto frameborder = static_cast<FrameBorderProperty>(value->GetEnumValue()); 125 if (FrameBorderProperty::No == frameborder || 126 FrameBorderProperty::Zero == frameborder) { 127 aBuilder.SetPixelValueIfUnset(eCSSProperty_border_top_width, 0.0f); 128 aBuilder.SetPixelValueIfUnset(eCSSProperty_border_right_width, 0.0f); 129 aBuilder.SetPixelValueIfUnset(eCSSProperty_border_bottom_width, 0.0f); 130 aBuilder.SetPixelValueIfUnset(eCSSProperty_border_left_width, 0.0f); 131 } 132 } 133 134 nsGenericHTMLElement::MapImageSizeAttributesInto(aBuilder); 135 nsGenericHTMLElement::MapImageAlignAttributeInto(aBuilder); 136 nsGenericHTMLElement::MapCommonAttributesInto(aBuilder); 137 } 138 139 NS_IMETHODIMP_(bool) 140 HTMLIFrameElement::IsAttributeMapped(const nsAtom* aAttribute) const { 141 static const MappedAttributeEntry attributes[] = { 142 {nsGkAtoms::width}, 143 {nsGkAtoms::height}, 144 {nsGkAtoms::frameborder}, 145 {nullptr}, 146 }; 147 148 static const MappedAttributeEntry* const map[] = { 149 attributes, 150 sImageAlignAttributeMap, 151 sCommonAttributeMap, 152 }; 153 154 return FindAttributeDependence(aAttribute, map); 155 } 156 157 nsMapRuleToAttributesFunc HTMLIFrameElement::GetAttributeMappingFunction() 158 const { 159 return &MapAttributesIntoRule; 160 } 161 162 void HTMLIFrameElement::AfterSetAttr(int32_t aNameSpaceID, nsAtom* aName, 163 const nsAttrValue* aValue, 164 const nsAttrValue* aOldValue, 165 nsIPrincipal* aMaybeScriptedPrincipal, 166 bool aNotify) { 167 AfterMaybeChangeAttr(aNameSpaceID, aName, aNotify); 168 169 if (aNameSpaceID == kNameSpaceID_None) { 170 if (aName == nsGkAtoms::loading) { 171 if (aValue && Loading(aValue->GetEnumValue()) == Loading::Lazy) { 172 SetLazyLoading(); 173 } else if (aOldValue && 174 Loading(aOldValue->GetEnumValue()) == Loading::Lazy) { 175 StopLazyLoading(); 176 } 177 } 178 179 // If lazy loading and src set, set lazy loading again as we are doing a new 180 // load (lazy loading is unset after a load is complete). 181 if ((aName == nsGkAtoms::src || aName == nsGkAtoms::srcdoc) && 182 LoadingState() == Loading::Lazy) { 183 SetLazyLoading(); 184 } 185 186 if (aName == nsGkAtoms::sandbox) { 187 if (mFrameLoader) { 188 // If we have an nsFrameLoader, apply the new sandbox flags. 189 // Since this is called after the setter, the sandbox flags have 190 // alreay been updated. 191 mFrameLoader->ApplySandboxFlags(GetSandboxFlags()); 192 } 193 } 194 195 if (aName == nsGkAtoms::allow || aName == nsGkAtoms::src || 196 aName == nsGkAtoms::srcdoc || aName == nsGkAtoms::sandbox) { 197 RefreshFeaturePolicy(true /* parse the feature policy attribute */); 198 } else if (aName == nsGkAtoms::allowfullscreen) { 199 RefreshFeaturePolicy(false /* parse the feature policy attribute */); 200 } 201 } 202 203 if (aName == nsGkAtoms::referrerpolicy) { 204 const auto newValue = ReferrerPolicyFromAttr(aValue); 205 if (newValue != ReferrerPolicyFromAttr(aOldValue)) { 206 RefreshEmbedderReferrerPolicy(newValue); 207 } 208 } 209 210 return nsGenericHTMLFrameElement::AfterSetAttr( 211 aNameSpaceID, aName, aValue, aOldValue, aMaybeScriptedPrincipal, aNotify); 212 } 213 214 void HTMLIFrameElement::OnAttrSetButNotChanged( 215 int32_t aNamespaceID, nsAtom* aName, const nsAttrValueOrString& aValue, 216 bool aNotify) { 217 AfterMaybeChangeAttr(aNamespaceID, aName, aNotify); 218 219 return nsGenericHTMLFrameElement::OnAttrSetButNotChanged(aNamespaceID, aName, 220 aValue, aNotify); 221 } 222 223 void HTMLIFrameElement::AfterMaybeChangeAttr(int32_t aNamespaceID, 224 nsAtom* aName, bool aNotify) { 225 if (aNamespaceID == kNameSpaceID_None) { 226 if (aName == nsGkAtoms::srcdoc) { 227 // Don't propagate errors from LoadSrc. The attribute was successfully 228 // set/unset, that's what we should reflect. 229 LoadSrc(); 230 } 231 } 232 } 233 234 uint32_t HTMLIFrameElement::GetSandboxFlags() const { 235 const nsAttrValue* sandboxAttr = GetParsedAttr(nsGkAtoms::sandbox); 236 // No sandbox attribute, no sandbox flags. 237 if (!sandboxAttr) { 238 return SANDBOXED_NONE; 239 } 240 return nsContentUtils::ParseSandboxAttributeToFlags(sandboxAttr); 241 } 242 243 JSObject* HTMLIFrameElement::WrapNode(JSContext* aCx, 244 JS::Handle<JSObject*> aGivenProto) { 245 return HTMLIFrameElement_Binding::Wrap(aCx, this, aGivenProto); 246 } 247 248 mozilla::dom::FeaturePolicy* HTMLIFrameElement::FeaturePolicy() const { 249 return mFeaturePolicy; 250 } 251 252 void HTMLIFrameElement::MaybeStoreCrossOriginFeaturePolicy() { 253 if (!mFrameLoader) { 254 return; 255 } 256 257 // If the browsingContext is not ready (because docshell is dead), don't try 258 // to create one. 259 if (!mFrameLoader->IsRemoteFrame() && !mFrameLoader->GetExistingDocShell()) { 260 return; 261 } 262 263 RefPtr<BrowsingContext> browsingContext = mFrameLoader->GetBrowsingContext(); 264 265 if (!browsingContext || !browsingContext->IsContentSubframe()) { 266 return; 267 } 268 269 if (ContentChild* cc = ContentChild::GetSingleton()) { 270 (void)cc->SendSetContainerFeaturePolicy( 271 browsingContext, Some(mFeaturePolicy->ToFeaturePolicyInfo())); 272 } 273 } 274 275 already_AddRefed<nsIPrincipal> 276 HTMLIFrameElement::GetFeaturePolicyDefaultOrigin() const { 277 nsCOMPtr<nsIPrincipal> principal; 278 279 if (HasAttr(nsGkAtoms::srcdoc)) { 280 principal = NodePrincipal(); 281 return principal.forget(); 282 } 283 284 nsCOMPtr<nsIURI> nodeURI; 285 if (GetURIAttr(nsGkAtoms::src, nullptr, getter_AddRefs(nodeURI)) && nodeURI) { 286 principal = BasePrincipal::CreateContentPrincipal( 287 nodeURI, BasePrincipal::Cast(NodePrincipal())->OriginAttributesRef()); 288 } 289 290 if (!principal) { 291 principal = NodePrincipal(); 292 } 293 294 return principal.forget(); 295 } 296 297 void HTMLIFrameElement::RefreshFeaturePolicy(bool aParseAllowAttribute) { 298 if (aParseAllowAttribute) { 299 mFeaturePolicy->ResetDeclaredPolicy(); 300 301 // The origin can change if 'src' and 'srcdoc' attributes change. 302 nsCOMPtr<nsIPrincipal> origin = GetFeaturePolicyDefaultOrigin(); 303 MOZ_ASSERT(origin); 304 mFeaturePolicy->SetDefaultOrigin(origin); 305 306 nsAutoString allow; 307 GetAttr(nsGkAtoms::allow, allow); 308 309 if (!allow.IsEmpty()) { 310 // Set or reset the FeaturePolicy directives. 311 mFeaturePolicy->SetDeclaredPolicy(OwnerDoc(), allow, NodePrincipal(), 312 origin); 313 } 314 } 315 316 if (AllowFullscreen()) { 317 mFeaturePolicy->MaybeSetAllowedPolicy(u"fullscreen"_ns); 318 } 319 320 mFeaturePolicy->InheritPolicy(OwnerDoc()->FeaturePolicy()); 321 MaybeStoreCrossOriginFeaturePolicy(); 322 } 323 324 void HTMLIFrameElement::RefreshEmbedderReferrerPolicy(ReferrerPolicy aPolicy) { 325 auto* browsingContext = GetExtantBrowsingContext(); 326 if (!browsingContext || !browsingContext->IsContentSubframe()) { 327 return; 328 } 329 330 if (ContentChild* cc = ContentChild::GetSingleton()) { 331 (void)cc->SendSetReferrerPolicyForEmbedderFrame(browsingContext, aPolicy); 332 } 333 } 334 335 void HTMLIFrameElement::UpdateLazyLoadState() { 336 // Store current base URI and referrer policy in the lazy load state. 337 mLazyLoadState.mBaseURI = GetBaseURI(); 338 mLazyLoadState.mReferrerPolicy = GetReferrerPolicyAsEnum(); 339 } 340 341 nsresult HTMLIFrameElement::BindToTree(BindContext& aContext, 342 nsINode& aParent) { 343 // Update lazy load state on bind to tree again if lazy loading, as the 344 // loading attribute could be set before others. 345 if (mLazyLoading) { 346 UpdateLazyLoadState(); 347 } 348 349 return nsGenericHTMLFrameElement::BindToTree(aContext, aParent); 350 } 351 352 void HTMLIFrameElement::SetLazyLoading() { 353 if (mLazyLoading) { 354 return; 355 } 356 357 // https://html.spec.whatwg.org/multipage/urls-and-fetching.html#will-lazy-load-element-steps 358 // "If scripting is disabled for element, then return false." 359 Document* doc = OwnerDoc(); 360 if (!doc->IsScriptEnabled() || doc->IsStaticDocument()) { 361 return; 362 } 363 364 doc->EnsureLazyLoadObserver().Observe(*this); 365 mLazyLoading = true; 366 367 UpdateLazyLoadState(); 368 } 369 370 void HTMLIFrameElement::StopLazyLoading() { 371 CancelLazyLoading(false /* aClearLazyLoadState */); 372 373 LoadSrc(); 374 375 mLazyLoadState.Clear(); 376 if (nsSubDocumentFrame* ourFrame = do_QueryFrame(GetPrimaryFrame())) { 377 ourFrame->ResetFrameLoader(nsSubDocumentFrame::RetainPaintData::No); 378 } 379 } 380 381 void HTMLIFrameElement::NodeInfoChanged(Document* aOldDoc) { 382 nsGenericHTMLElement::NodeInfoChanged(aOldDoc); 383 384 if (mLazyLoading) { 385 aOldDoc->GetLazyLoadObserver()->Unobserve(*this); 386 mLazyLoading = false; 387 } 388 389 if (LoadingState() == Loading::Lazy) { 390 SetLazyLoading(); 391 } 392 } 393 394 void HTMLIFrameElement::CancelLazyLoading(bool aClearLazyLoadState) { 395 if (!mLazyLoading) { 396 return; 397 } 398 399 Document* doc = OwnerDoc(); 400 if (auto* obs = doc->GetLazyLoadObserver()) { 401 obs->Unobserve(*this); 402 } 403 404 mLazyLoading = false; 405 406 if (aClearLazyLoadState) { 407 mLazyLoadState.Clear(); 408 } 409 } 410 411 void HTMLIFrameElement::GetSrcdoc(OwningTrustedHTMLOrString& aSrcdoc) { 412 GetHTMLAttr(nsGkAtoms::srcdoc, aSrcdoc.SetAsString()); 413 } 414 415 void HTMLIFrameElement::SetSrcdoc(const TrustedHTMLOrString& aSrcdoc, 416 nsIPrincipal* aSubjectPrincipal, 417 ErrorResult& aError) { 418 constexpr nsLiteralString sink = u"HTMLIFrameElement srcdoc"_ns; 419 420 Maybe<nsAutoString> compliantStringHolder; 421 const nsAString* compliantString = 422 TrustedTypeUtils::GetTrustedTypesCompliantString( 423 aSrcdoc, sink, kTrustedTypesOnlySinkGroup, *this, aSubjectPrincipal, 424 compliantStringHolder, aError); 425 if (aError.Failed()) { 426 return; 427 } 428 429 SetHTMLAttr(nsGkAtoms::srcdoc, *compliantString, aError); 430 } 431 432 } // namespace mozilla::dom