| /* |
| * Copyright (C) 1999 Lars Knoll (knoll@kde.org) |
| * (C) 1999 Antti Koivisto (koivisto@kde.org) |
| * (C) 2001 Dirk Mueller (mueller@kde.org) |
| * (C) 2006 Alexey Proskuryakov (ap@webkit.org) |
| * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2012 Apple Inc. All |
| * rights reserved. |
| * Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. |
| * (http://www.torchmobile.com/) |
| * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies) |
| * Copyright (C) 2013 Google Inc. All rights reserved. |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Library General Public |
| * License as published by the Free Software Foundation; either |
| * version 2 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Library General Public License for more details. |
| * |
| * You should have received a copy of the GNU Library General Public License |
| * along with this library; see the file COPYING.LIB. If not, write to |
| * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
| * Boston, MA 02110-1301, USA. |
| * |
| */ |
| |
| #include "third_party/blink/renderer/core/dom/document_init.h" |
| |
| #include "services/network/public/cpp/web_sandbox_flags.h" |
| #include "services/network/public/mojom/web_sandbox_flags.mojom-blink.h" |
| #include "third_party/blink/public/mojom/security_context/insecure_request_policy.mojom-blink.h" |
| #include "third_party/blink/renderer/core/dom/document.h" |
| #include "third_party/blink/renderer/core/dom/dom_implementation.h" |
| #include "third_party/blink/renderer/core/frame/csp/content_security_policy.h" |
| #include "third_party/blink/renderer/core/frame/local_frame.h" |
| #include "third_party/blink/renderer/core/html/custom/v0_custom_element_registration_context.h" |
| #include "third_party/blink/renderer/core/html/html_document.h" |
| #include "third_party/blink/renderer/core/html/html_frame_owner_element.h" |
| #include "third_party/blink/renderer/core/html/imports/html_imports_controller.h" |
| #include "third_party/blink/renderer/core/html/media/html_media_element.h" |
| #include "third_party/blink/renderer/core/loader/document_loader.h" |
| #include "third_party/blink/renderer/core/page/chrome_client.h" |
| #include "third_party/blink/renderer/core/page/page.h" |
| #include "third_party/blink/renderer/core/page/plugin_data.h" |
| #include "third_party/blink/renderer/platform/network/mime/content_type.h" |
| #include "third_party/blink/renderer/platform/network/mime/mime_type_registry.h" |
| #include "third_party/blink/renderer/platform/network/network_utils.h" |
| #include "third_party/blink/renderer/platform/web_test_support.h" |
| |
| namespace blink { |
| |
| // FIXME: Broken with OOPI. |
| static Document* ParentDocument(DocumentLoader* loader) { |
| DCHECK(loader); |
| DCHECK(loader->GetFrame()); |
| |
| Element* owner_element = loader->GetFrame()->DeprecatedLocalOwner(); |
| if (!owner_element) |
| return nullptr; |
| return &owner_element->GetDocument(); |
| } |
| |
| // static |
| DocumentInit DocumentInit::Create() { |
| return DocumentInit(); |
| } |
| |
| DocumentInit::DocumentInit(const DocumentInit&) = default; |
| |
| DocumentInit::~DocumentInit() = default; |
| |
| DocumentInit& DocumentInit::WithImportsController( |
| HTMLImportsController* controller) { |
| imports_controller_ = controller; |
| return *this; |
| } |
| |
| bool DocumentInit::ShouldSetURL() const { |
| DocumentLoader* loader = MasterDocumentLoader(); |
| return (loader && loader->GetFrame()->Tree().Parent()) || !url_.IsEmpty(); |
| } |
| |
| bool DocumentInit::IsSrcdocDocument() const { |
| // TODO(dgozman): why do we check |parent_document_| here? |
| return parent_document_ && is_srcdoc_document_; |
| } |
| |
| DocumentLoader* DocumentInit::MasterDocumentLoader() const { |
| if (document_loader_) |
| return document_loader_; |
| if (imports_controller_) { |
| return imports_controller_->Master() |
| ->GetFrame() |
| ->Loader() |
| .GetDocumentLoader(); |
| } |
| return nullptr; |
| } |
| |
| network::mojom::blink::WebSandboxFlags DocumentInit::GetSandboxFlags() const { |
| network::mojom::blink::WebSandboxFlags flags = sandbox_flags_; |
| if (DocumentLoader* loader = MasterDocumentLoader()) |
| flags |= loader->GetFrame()->Loader().EffectiveSandboxFlags(); |
| // If the load was blocked by CSP, force the Document's origin to be unique, |
| // so that the blocked document appears to be a normal cross-origin |
| // document's load per CSP spec: |
| // https://www.w3.org/TR/CSP3/#directive-frame-ancestors. |
| if (blocked_by_csp_) |
| flags |= network::mojom::blink::WebSandboxFlags::kOrigin; |
| return flags; |
| } |
| |
| mojom::blink::InsecureRequestPolicy DocumentInit::GetInsecureRequestPolicy() |
| const { |
| DCHECK(MasterDocumentLoader()); |
| Frame* parent_frame = MasterDocumentLoader()->GetFrame()->Tree().Parent(); |
| if (!parent_frame) |
| return mojom::blink::InsecureRequestPolicy::kLeaveInsecureRequestsAlone; |
| return parent_frame->GetSecurityContext()->GetInsecureRequestPolicy(); |
| } |
| |
| const SecurityContext::InsecureNavigationsSet* |
| DocumentInit::InsecureNavigationsToUpgrade() const { |
| DCHECK(MasterDocumentLoader()); |
| Frame* parent_frame = MasterDocumentLoader()->GetFrame()->Tree().Parent(); |
| if (!parent_frame) |
| return nullptr; |
| return &parent_frame->GetSecurityContext()->InsecureNavigationsToUpgrade(); |
| } |
| |
| network::mojom::IPAddressSpace DocumentInit::GetIPAddressSpace() const { |
| return ip_address_space_; |
| } |
| |
| Settings* DocumentInit::GetSettings() const { |
| DCHECK(MasterDocumentLoader()); |
| return MasterDocumentLoader()->GetFrame()->GetSettings(); |
| } |
| |
| DocumentInit& DocumentInit::WithDocumentLoader(DocumentLoader* loader) { |
| DCHECK(!document_loader_); |
| DCHECK(!imports_controller_); |
| document_loader_ = loader; |
| if (document_loader_) |
| parent_document_ = ParentDocument(document_loader_); |
| return *this; |
| } |
| |
| LocalFrame* DocumentInit::GetFrame() const { |
| return document_loader_ ? document_loader_->GetFrame() : nullptr; |
| } |
| |
| UseCounter* DocumentInit::GetUseCounter() const { |
| return document_loader_; |
| } |
| |
| // static |
| DocumentInit::Type DocumentInit::ComputeDocumentType( |
| LocalFrame* frame, |
| const KURL& url, |
| const String& mime_type, |
| bool* is_for_external_handler) { |
| if (frame && frame->InViewSourceMode()) |
| return Type::kViewSource; |
| |
| // Plugins cannot take HTML and XHTML from us, and we don't even need to |
| // initialize the plugin database for those. |
| if (mime_type == "text/html") |
| return Type::kHTML; |
| |
| if (mime_type == "application/xhtml+xml") |
| return Type::kXHTML; |
| |
| // multipart/x-mixed-replace is only supported for images. |
| if (MIMETypeRegistry::IsSupportedImageResourceMIMEType(mime_type) || |
| mime_type == "multipart/x-mixed-replace") { |
| return Type::kImage; |
| } |
| |
| if (HTMLMediaElement::GetSupportsType(ContentType(mime_type))) |
| return Type::kMedia; |
| |
| if (frame && frame->GetPage() && |
| frame->Loader().AllowPlugins(kNotAboutToInstantiatePlugin)) { |
| PluginData* plugin_data = GetPluginData(frame, url); |
| |
| // Everything else except text/plain can be overridden by plugins. |
| // Disallowing plugins to use text/plain prevents plugins from hijacking a |
| // fundamental type that the browser is expected to handle, and also serves |
| // as an optimization to prevent loading the plugin database in the common |
| // case. |
| if (mime_type != "text/plain" && plugin_data && |
| plugin_data->SupportsMimeType(mime_type)) { |
| // Plugins handled by MimeHandlerView do not create a PluginDocument. They |
| // are rendered inside cross-process frames and the notion of a PluginView |
| // (which is associated with PluginDocument) is irrelevant here. |
| if (plugin_data->IsExternalPluginMimeType(mime_type)) { |
| if (is_for_external_handler) |
| *is_for_external_handler = true; |
| return Type::kHTML; |
| } |
| |
| return Type::kPlugin; |
| } |
| } |
| |
| if (DOMImplementation::IsTextMIMEType(mime_type)) |
| return Type::kText; |
| |
| if (mime_type == "image/svg+xml") |
| return Type::kSVG; |
| |
| if (DOMImplementation::IsXMLMIMEType(mime_type)) |
| return Type::kXML; |
| |
| return Type::kHTML; |
| } |
| |
| // static |
| PluginData* DocumentInit::GetPluginData(LocalFrame* frame, const KURL& url) { |
| // If the document is being created for the main frame, |
| // frame()->tree().top()->securityContext() returns nullptr. |
| // For that reason, the origin must be retrieved directly from |url|. |
| if (frame->IsMainFrame()) |
| return frame->GetPage()->GetPluginData(SecurityOrigin::Create(url).get()); |
| |
| const SecurityOrigin* main_frame_origin = |
| frame->Tree().Top().GetSecurityContext()->GetSecurityOrigin(); |
| return frame->GetPage()->GetPluginData(main_frame_origin); |
| } |
| |
| DocumentInit& DocumentInit::WithTypeFrom(const String& mime_type) { |
| mime_type_ = mime_type; |
| type_ = ComputeDocumentType(GetFrame(), Url(), mime_type_, |
| &is_for_external_handler_); |
| if (type_ == Type::kPlugin) { |
| plugin_background_color_ = |
| GetPluginData(GetFrame(), Url()) |
| ->PluginBackgroundColorForMimeType(mime_type_); |
| } |
| return *this; |
| } |
| |
| DocumentInit& DocumentInit::WithContextDocument(Document* context_document) { |
| DCHECK(!context_document_); |
| context_document_ = context_document; |
| return *this; |
| } |
| |
| DocumentInit& DocumentInit::WithURL(const KURL& url) { |
| DCHECK(url_.IsNull()); |
| url_ = url; |
| return *this; |
| } |
| |
| scoped_refptr<SecurityOrigin> DocumentInit::GetDocumentOrigin() const { |
| // Origin to commit is specified by the browser process, it must be taken |
| // and used directly. It is currently supplied only for session history |
| // navigations, where the origin was already calcuated previously and |
| // stored on the session history entry. |
| if (origin_to_commit_) |
| return origin_to_commit_; |
| |
| if (owner_document_) |
| return owner_document_->GetMutableSecurityOrigin(); |
| |
| // Otherwise, create an origin that propagates precursor information |
| // as needed. For non-opaque origins, this creates a standard tuple |
| // origin, but for opaque origins, it creates an origin with the |
| // initiator origin as the precursor. |
| return SecurityOrigin::CreateWithReferenceOrigin(url_, |
| initiator_origin_.get()); |
| } |
| |
| DocumentInit& DocumentInit::WithOwnerDocument(Document* owner_document) { |
| DCHECK(!owner_document_); |
| DCHECK(!initiator_origin_ || !owner_document || |
| owner_document->GetSecurityOrigin() == initiator_origin_); |
| owner_document_ = owner_document; |
| return *this; |
| } |
| |
| DocumentInit& DocumentInit::WithInitiatorOrigin( |
| scoped_refptr<const SecurityOrigin> initiator_origin) { |
| DCHECK(!initiator_origin_); |
| DCHECK(!initiator_origin || !owner_document_ || |
| owner_document_->GetSecurityOrigin() == initiator_origin); |
| initiator_origin_ = std::move(initiator_origin); |
| return *this; |
| } |
| |
| DocumentInit& DocumentInit::WithOriginToCommit( |
| scoped_refptr<SecurityOrigin> origin_to_commit) { |
| origin_to_commit_ = std::move(origin_to_commit); |
| return *this; |
| } |
| |
| DocumentInit& DocumentInit::WithIPAddressSpace( |
| network::mojom::IPAddressSpace ip_address_space) { |
| ip_address_space_ = ip_address_space; |
| return *this; |
| } |
| |
| DocumentInit& DocumentInit::WithSrcdocDocument(bool is_srcdoc_document) { |
| is_srcdoc_document_ = is_srcdoc_document; |
| return *this; |
| } |
| |
| DocumentInit& DocumentInit::WithBlockedByCSP(bool blocked_by_csp) { |
| blocked_by_csp_ = blocked_by_csp; |
| return *this; |
| } |
| |
| DocumentInit& DocumentInit::WithGrantLoadLocalResources( |
| bool grant_load_local_resources) { |
| grant_load_local_resources_ = grant_load_local_resources; |
| return *this; |
| } |
| |
| DocumentInit& DocumentInit::WithRegistrationContext( |
| V0CustomElementRegistrationContext* registration_context) { |
| DCHECK(!create_new_registration_context_); |
| DCHECK(!registration_context_); |
| registration_context_ = registration_context; |
| return *this; |
| } |
| |
| DocumentInit& DocumentInit::WithNewRegistrationContext() { |
| DCHECK(!create_new_registration_context_); |
| DCHECK(!registration_context_); |
| create_new_registration_context_ = true; |
| return *this; |
| } |
| |
| V0CustomElementRegistrationContext* DocumentInit::RegistrationContext( |
| Document* document) const { |
| if (!IsA<HTMLDocument>(document) && !document->IsXHTMLDocument()) |
| return nullptr; |
| |
| if (create_new_registration_context_) |
| return MakeGarbageCollected<V0CustomElementRegistrationContext>(); |
| |
| return registration_context_; |
| } |
| |
| Document* DocumentInit::ContextDocument() const { |
| return context_document_; |
| } |
| |
| DocumentInit& DocumentInit::WithFeaturePolicyHeader(const String& header) { |
| DCHECK(feature_policy_header_.IsEmpty()); |
| feature_policy_header_ = header; |
| return *this; |
| } |
| |
| DocumentInit& DocumentInit::WithReportOnlyFeaturePolicyHeader( |
| const String& header) { |
| DCHECK(report_only_feature_policy_header_.IsEmpty()); |
| report_only_feature_policy_header_ = header; |
| return *this; |
| } |
| |
| DocumentInit& DocumentInit::WithOriginTrialsHeader(const String& header) { |
| DCHECK(origin_trials_header_.IsEmpty()); |
| origin_trials_header_ = header; |
| return *this; |
| } |
| |
| DocumentInit& DocumentInit::WithSandboxFlags( |
| network::mojom::blink::WebSandboxFlags flags) { |
| // Only allow adding more sandbox flags. |
| sandbox_flags_ |= flags; |
| return *this; |
| } |
| |
| DocumentInit& DocumentInit::WithContentSecurityPolicy( |
| ContentSecurityPolicy* policy) { |
| content_security_policy_ = policy; |
| return *this; |
| } |
| |
| DocumentInit& DocumentInit::WithContentSecurityPolicyFromContextDoc() { |
| content_security_policy_from_context_doc_ = true; |
| return *this; |
| } |
| |
| ContentSecurityPolicy* DocumentInit::GetContentSecurityPolicy() const { |
| DCHECK( |
| !(content_security_policy_ && content_security_policy_from_context_doc_)); |
| if (context_document_ && content_security_policy_from_context_doc_) { |
| // Return a copy of the context documents' CSP. The return value will be |
| // modified, so this must be a copy. |
| ContentSecurityPolicy* csp = MakeGarbageCollected<ContentSecurityPolicy>(); |
| csp->CopyStateFrom(context_document_->GetContentSecurityPolicy()); |
| return csp; |
| } |
| return content_security_policy_; |
| } |
| |
| DocumentInit& DocumentInit::WithFramePolicy( |
| const base::Optional<FramePolicy>& frame_policy) { |
| frame_policy_ = frame_policy; |
| if (frame_policy_.has_value()) { |
| DCHECK(document_loader_); |
| // Make the snapshot value of sandbox flags from the beginning of navigation |
| // available in frame loader, so that the value could be further used to |
| // initialize sandbox flags in security context. crbug.com/1026627 |
| document_loader_->GetFrame()->Loader().SetFrameOwnerSandboxFlags( |
| frame_policy_.value().sandbox_flags); |
| } |
| return *this; |
| } |
| |
| DocumentInit& DocumentInit::WithDocumentPolicy( |
| const DocumentPolicy::ParsedDocumentPolicy& document_policy) { |
| document_policy_ = document_policy; |
| return *this; |
| } |
| |
| DocumentInit& DocumentInit::WithReportOnlyDocumentPolicyHeader( |
| const String& header) { |
| DCHECK(report_only_document_policy_header_.IsEmpty()); |
| report_only_document_policy_header_ = header; |
| return *this; |
| } |
| |
| DocumentInit& DocumentInit::WithWebBundleClaimedUrl( |
| const KURL& web_bundle_claimed_url) { |
| web_bundle_claimed_url_ = web_bundle_claimed_url; |
| return *this; |
| } |
| |
| bool IsPagePopupRunningInWebTest(LocalFrame* frame) { |
| return frame && frame->GetPage()->GetChromeClient().IsPopup() && |
| WebTestSupport::IsRunningWebTest(); |
| } |
| |
| WindowAgentFactory* DocumentInit::GetWindowAgentFactory() const { |
| // If we are a page popup in LayoutTests ensure we use the popup |
| // owner's frame for looking up the Agent so the tests can possibly |
| // access the document via internals API. |
| LocalFrame* frame = nullptr; |
| if (IsPagePopupRunningInWebTest(GetFrame())) |
| frame = GetFrame()->PagePopupOwner()->GetDocument().GetFrame(); |
| else if (GetFrame()) |
| frame = GetFrame(); |
| else if (Document* context_document = ContextDocument()) |
| return context_document->GetWindowAgentFactory(); |
| return frame ? &frame->window_agent_factory() : nullptr; |
| } |
| |
| Settings* DocumentInit::GetSettingsForWindowAgentFactory() const { |
| LocalFrame* frame = nullptr; |
| if (IsPagePopupRunningInWebTest(GetFrame())) |
| frame = GetFrame()->PagePopupOwner()->GetDocument().GetFrame(); |
| else if (GetFrame()) |
| frame = GetFrame(); |
| else if (Document* context_document = ContextDocument()) |
| frame = context_document->GetFrame(); |
| return frame ? frame->GetSettings() : nullptr; |
| } |
| |
| } // namespace blink |