blob: 59204c4d8db3a09acd79a106c8c89fe8855cc096 [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "third_party/blink/renderer/core/frame/dom_window.h"
#include <algorithm>
#include <memory>
#include "base/metrics/histogram_macros.h"
#include "services/network/public/mojom/web_sandbox_flags.mojom-blink.h"
#include "third_party/blink/public/common/action_after_pagehide.h"
#include "third_party/blink/renderer/bindings/core/v8/serialization/post_message_helper.h"
#include "third_party/blink/renderer/bindings/core/v8/source_location.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_window.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_window_post_message_options.h"
#include "third_party/blink/renderer/bindings/core/v8/window_proxy_manager.h"
#include "third_party/blink/renderer/core/dom/document.h"
#include "third_party/blink/renderer/core/events/message_event.h"
#include "third_party/blink/renderer/core/execution_context/execution_context.h"
#include "third_party/blink/renderer/core/execution_context/security_context.h"
#include "third_party/blink/renderer/core/frame/coop_access_violation_report_body.h"
#include "third_party/blink/renderer/core/frame/csp/content_security_policy.h"
#include "third_party/blink/renderer/core/frame/frame.h"
#include "third_party/blink/renderer/core/frame/frame_client.h"
#include "third_party/blink/renderer/core/frame/frame_console.h"
#include "third_party/blink/renderer/core/frame/local_dom_window.h"
#include "third_party/blink/renderer/core/frame/location.h"
#include "third_party/blink/renderer/core/frame/report.h"
#include "third_party/blink/renderer/core/frame/reporting_context.h"
#include "third_party/blink/renderer/core/frame/settings.h"
#include "third_party/blink/renderer/core/frame/user_activation.h"
#include "third_party/blink/renderer/core/input/input_device_capabilities.h"
#include "third_party/blink/renderer/core/inspector/console_message.h"
#include "third_party/blink/renderer/core/loader/mixed_content_checker.h"
#include "third_party/blink/renderer/core/page/chrome_client.h"
#include "third_party/blink/renderer/core/page/focus_controller.h"
#include "third_party/blink/renderer/core/page/page.h"
#include "third_party/blink/renderer/core/probe/core_probes.h"
#include "third_party/blink/renderer/platform/heap/heap.h"
#include "third_party/blink/renderer/platform/instrumentation/use_counter.h"
#include "third_party/blink/renderer/platform/weborigin/kurl.h"
#include "third_party/blink/renderer/platform/weborigin/security_origin.h"
namespace blink {
DOMWindow::DOMWindow(Frame& frame)
: frame_(frame),
window_proxy_manager_(frame.GetWindowProxyManager()),
window_is_closing_(false) {}
DOMWindow::~DOMWindow() {
// The frame must be disconnected before finalization.
DCHECK(!frame_);
}
v8::Local<v8::Value> DOMWindow::Wrap(v8::Isolate* isolate,
v8::Local<v8::Object> creation_context) {
// TODO(yukishiino): Get understanding of why it's possible to initialize
// the context after the frame is detached. And then, remove the following
// lines. See also https://crbug.com/712638 .
Frame* frame = GetFrame();
if (!frame)
return v8::Null(isolate);
// TODO(yukishiino): Make this function always return the non-empty handle
// even if the frame is detached because the global proxy must always exist
// per spec.
ScriptState* script_state = ScriptState::From(isolate->GetCurrentContext());
return frame->GetWindowProxy(script_state->World())
->GlobalProxyIfNotDetached();
}
v8::MaybeLocal<v8::Value> DOMWindow::WrapV2(ScriptState* script_state) {
// TODO(yukishiino): Get understanding of why it's possible to initialize
// the context after the frame is detached. And then, remove the following
// lines. See also https://crbug.com/712638 .
Frame* frame = GetFrame();
if (!frame)
return v8::Null(script_state->GetIsolate());
// TODO(yukishiino): Make this function always return the non-empty handle
// even if the frame is detached because the global proxy must always exist
// per spec.
return frame->GetWindowProxy(script_state->World())
->GlobalProxyIfNotDetached();
}
v8::Local<v8::Object> DOMWindow::AssociateWithWrapper(
v8::Isolate*,
const WrapperTypeInfo*,
v8::Local<v8::Object> wrapper) {
NOTREACHED();
return v8::Local<v8::Object>();
}
const AtomicString& DOMWindow::InterfaceName() const {
return event_target_names::kWindow;
}
const DOMWindow* DOMWindow::ToDOMWindow() const {
return this;
}
bool DOMWindow::IsWindowOrWorkerGlobalScope() const {
return true;
}
Location* DOMWindow::location() const {
if (!location_)
location_ = MakeGarbageCollected<Location>(const_cast<DOMWindow*>(this));
return location_.Get();
}
bool DOMWindow::closed() const {
return window_is_closing_ || !GetFrame() || !GetFrame()->GetPage();
}
unsigned DOMWindow::length() const {
return GetFrame() ? GetFrame()->Tree().ScopedChildCount() : 0;
}
DOMWindow* DOMWindow::self() const {
if (!GetFrame())
return nullptr;
return GetFrame()->DomWindow();
}
DOMWindow* DOMWindow::opener() const {
// FIXME: Use FrameTree to get opener as well, to simplify logic here.
if (!GetFrame() || !GetFrame()->Client())
return nullptr;
Frame* opener = GetFrame()->Opener();
return opener ? opener->DomWindow() : nullptr;
}
DOMWindow* DOMWindow::parent() const {
if (!GetFrame())
return nullptr;
Frame* parent = GetFrame()->Tree().Parent();
return parent ? parent->DomWindow() : GetFrame()->DomWindow();
}
DOMWindow* DOMWindow::top() const {
if (!GetFrame())
return nullptr;
return GetFrame()->Tree().Top().DomWindow();
}
void DOMWindow::postMessage(v8::Isolate* isolate,
const ScriptValue& message,
const String& target_origin,
HeapVector<ScriptValue>& transfer,
ExceptionState& exception_state) {
WindowPostMessageOptions* options = WindowPostMessageOptions::Create();
options->setTargetOrigin(target_origin);
if (!transfer.IsEmpty())
options->setTransfer(transfer);
postMessage(isolate, message, options, exception_state);
}
void DOMWindow::postMessage(v8::Isolate* isolate,
const ScriptValue& message,
const WindowPostMessageOptions* options,
ExceptionState& exception_state) {
LocalDOMWindow* incumbent_window = IncumbentDOMWindow(isolate);
UseCounter::Count(incumbent_window->document(),
WebFeature::kWindowPostMessage);
Transferables transferables;
scoped_refptr<SerializedScriptValue> serialized_message =
PostMessageHelper::SerializeMessageByMove(isolate, message, options,
transferables, exception_state);
if (exception_state.HadException())
return;
DCHECK(serialized_message);
DoPostMessage(std::move(serialized_message), transferables.message_ports,
options, incumbent_window, exception_state);
}
DOMWindow* DOMWindow::AnonymousIndexedGetter(uint32_t index) {
ReportCoopAccess("indexed");
if (!GetFrame())
return nullptr;
Frame* child = GetFrame()->Tree().ScopedChild(index);
return child ? child->DomWindow() : nullptr;
}
bool DOMWindow::IsCurrentlyDisplayedInFrame() const {
if (GetFrame())
SECURITY_CHECK(GetFrame()->DomWindow() == this);
return GetFrame() && GetFrame()->GetPage();
}
// FIXME: Once we're throwing exceptions for cross-origin access violations, we
// will always sanitize the target frame details, so we can safely combine
// 'crossDomainAccessErrorMessage' with this method after considering exactly
// which details may be exposed to JavaScript.
//
// http://crbug.com/17325
String DOMWindow::SanitizedCrossDomainAccessErrorMessage(
const LocalDOMWindow* accessing_window,
CrossDocumentAccessPolicy cross_document_access) const {
if (!accessing_window || !GetFrame())
return String();
const KURL& accessing_window_url = accessing_window->Url();
if (accessing_window_url.IsNull())
return String();
const SecurityOrigin* active_origin = accessing_window->GetSecurityOrigin();
String message;
if (cross_document_access == CrossDocumentAccessPolicy::kDisallowed) {
message = "Blocked a restricted frame with origin \"" +
active_origin->ToString() + "\" from accessing another frame.";
} else {
message = "Blocked a frame with origin \"" + active_origin->ToString() +
"\" from accessing a cross-origin frame.";
}
// FIXME: Evaluate which details from 'crossDomainAccessErrorMessage' may
// safely be reported to JavaScript.
return message;
}
String DOMWindow::CrossDomainAccessErrorMessage(
const LocalDOMWindow* accessing_window,
CrossDocumentAccessPolicy cross_document_access) const {
if (!accessing_window || !GetFrame())
return String();
const KURL& accessing_window_url = accessing_window->Url();
if (accessing_window_url.IsNull())
return String();
// FIXME: This message, and other console messages, have extra newlines.
// Should remove them.
const SecurityOrigin* active_origin = accessing_window->GetSecurityOrigin();
const SecurityOrigin* target_origin =
GetFrame()->GetSecurityContext()->GetSecurityOrigin();
auto* local_dom_window = DynamicTo<LocalDOMWindow>(this);
// It's possible for a remote frame to be same origin with respect to a
// local frame, but it must still be treated as a disallowed cross-domain
// access. See https://crbug.com/601629.
DCHECK(GetFrame()->IsRemoteFrame() ||
!active_origin->CanAccess(target_origin) ||
(local_dom_window &&
accessing_window->GetAgent() != local_dom_window->GetAgent()));
String message = "Blocked a frame with origin \"" +
active_origin->ToString() +
"\" from accessing a frame with origin \"" +
target_origin->ToString() + "\". ";
// Sandbox errors: Use the origin of the frames' location, rather than their
// actual origin (since we know that at least one will be "null").
KURL active_url = accessing_window->Url();
// TODO(alexmos): RemoteFrames do not have a document, and their URLs
// aren't replicated. For now, construct the URL using the replicated
// origin for RemoteFrames. If the target frame is remote and sandboxed,
// there isn't anything else to show other than "null" for its origin.
KURL target_url = local_dom_window
? local_dom_window->Url()
: KURL(NullURL(), target_origin->ToString());
using SandboxFlags = network::mojom::blink::WebSandboxFlags;
if (GetFrame()->GetSecurityContext()->IsSandboxed(SandboxFlags::kOrigin) ||
accessing_window->IsSandboxed(SandboxFlags::kOrigin)) {
message = "Blocked a frame at \"" +
SecurityOrigin::Create(active_url)->ToString() +
"\" from accessing a frame at \"" +
SecurityOrigin::Create(target_url)->ToString() + "\". ";
if (GetFrame()->GetSecurityContext()->IsSandboxed(SandboxFlags::kOrigin) &&
accessing_window->IsSandboxed(SandboxFlags::kOrigin)) {
return "Sandbox access violation: " + message +
" Both frames are sandboxed and lack the \"allow-same-origin\" "
"flag.";
}
if (GetFrame()->GetSecurityContext()->IsSandboxed(SandboxFlags::kOrigin)) {
return "Sandbox access violation: " + message +
" The frame being accessed is sandboxed and lacks the "
"\"allow-same-origin\" flag.";
}
return "Sandbox access violation: " + message +
" The frame requesting access is sandboxed and lacks the "
"\"allow-same-origin\" flag.";
}
// Protocol errors: Use the URL's protocol rather than the origin's protocol
// so that we get a useful message for non-heirarchal URLs like 'data:'.
if (target_origin->Protocol() != active_origin->Protocol())
return message + " The frame requesting access has a protocol of \"" +
active_url.Protocol() +
"\", the frame being accessed has a protocol of \"" +
target_url.Protocol() + "\". Protocols must match.\n";
// 'document.domain' errors.
if (target_origin->DomainWasSetInDOM() && active_origin->DomainWasSetInDOM())
return message +
"The frame requesting access set \"document.domain\" to \"" +
active_origin->Domain() +
"\", the frame being accessed set it to \"" +
target_origin->Domain() +
"\". Both must set \"document.domain\" to the same value to allow "
"access.";
if (active_origin->DomainWasSetInDOM())
return message +
"The frame requesting access set \"document.domain\" to \"" +
active_origin->Domain() +
"\", but the frame being accessed did not. Both must set "
"\"document.domain\" to the same value to allow access.";
if (target_origin->DomainWasSetInDOM())
return message + "The frame being accessed set \"document.domain\" to \"" +
target_origin->Domain() +
"\", but the frame requesting access did not. Both must set "
"\"document.domain\" to the same value to allow access.";
if (cross_document_access == CrossDocumentAccessPolicy::kDisallowed)
return message + "The document-access policy denied access.";
// Default.
return message + "Protocols, domains, and ports must match.";
}
void DOMWindow::close(v8::Isolate* isolate) {
LocalDOMWindow* incumbent_window = IncumbentDOMWindow(isolate);
Close(incumbent_window);
}
void DOMWindow::Close(LocalDOMWindow* incumbent_window) {
DCHECK(incumbent_window);
if (!GetFrame() || !GetFrame()->IsMainFrame())
return;
Page* page = GetFrame()->GetPage();
if (!page)
return;
if (page->InsidePortal())
return;
Document* active_document = incumbent_window->document();
if (!(active_document && active_document->GetFrame() &&
active_document->GetFrame()->CanNavigate(*GetFrame()))) {
return;
}
Settings* settings = GetFrame()->GetSettings();
bool allow_scripts_to_close_windows =
settings && settings->GetAllowScriptsToCloseWindows();
if (!page->OpenedByDOM() && GetFrame()->Client()->BackForwardLength() > 1 &&
!allow_scripts_to_close_windows) {
active_document->domWindow()->GetFrameConsole()->AddMessage(
MakeGarbageCollected<ConsoleMessage>(
mojom::ConsoleMessageSource::kJavaScript,
mojom::ConsoleMessageLevel::kWarning,
"Scripts may close only the windows that were opened by them."));
return;
}
if (!GetFrame()->ShouldClose())
return;
ExecutionContext* execution_context = nullptr;
if (auto* local_dom_window = DynamicTo<LocalDOMWindow>(this)) {
execution_context = local_dom_window->GetExecutionContext();
}
probe::BreakableLocation(execution_context, "DOMWindow.close");
page->CloseSoon();
// So as to make window.closed return the expected result
// after window.close(), separately record the to-be-closed
// state of this window. Scripts may access window.closed
// before the deferred close operation has gone ahead.
window_is_closing_ = true;
}
void DOMWindow::focus(v8::Isolate* isolate) {
Frame* frame = GetFrame();
if (!frame)
return;
Page* page = frame->GetPage();
if (!page)
return;
// HTML standard doesn't require to check the incumbent realm, but Blink
// historically checks it for some reasons, maybe the same reason as |close|.
// (|close| checks whether the incumbent realm is eligible to close the window
// in order to prevent a (cross origin) window from abusing |close| to close
// pages randomly or with a malicious intent.)
// https://html.spec.whatwg.org/C/#dom-window-focus
// https://html.spec.whatwg.org/C/#focusing-steps
LocalDOMWindow* incumbent_window = IncumbentDOMWindow(isolate);
// TODO(mustaq): Use of |allow_focus| and consuming the activation here seems
// suspicious (https://crbug.com/959815).
bool allow_focus = incumbent_window->IsWindowInteractionAllowed();
if (allow_focus) {
incumbent_window->ConsumeWindowInteraction();
} else {
DCHECK(IsMainThread());
allow_focus = opener() && opener() != this && incumbent_window == opener();
}
// If we're a top level window, bring the window to the front.
if (frame->IsMainFrame() && allow_focus) {
frame->FocusPage(incumbent_window->GetFrame());
} else if (auto* local_frame = DynamicTo<LocalFrame>(frame)) {
// We are depending on user activation twice since IsFocusAllowed() will
// check for activation. This should be addressed in
// https://crbug.com/959815.
if (local_frame->GetDocument() &&
!local_frame->GetDocument()->IsFocusAllowed()) {
return;
}
}
page->GetFocusController().FocusDocumentView(GetFrame(),
true /* notifyEmbedder */);
}
InputDeviceCapabilitiesConstants* DOMWindow::GetInputDeviceCapabilities() {
if (!input_capabilities_) {
input_capabilities_ =
MakeGarbageCollected<InputDeviceCapabilitiesConstants>();
}
return input_capabilities_;
}
void DOMWindow::PostMessageForTesting(
scoped_refptr<SerializedScriptValue> message,
const MessagePortArray& ports,
const String& target_origin,
LocalDOMWindow* source,
ExceptionState& exception_state) {
WindowPostMessageOptions* options = WindowPostMessageOptions::Create();
options->setTargetOrigin(target_origin);
DoPostMessage(std::move(message), ports, options, source, exception_state);
}
void DOMWindow::InstallCoopAccessMonitor(
network::mojom::blink::CoopAccessReportType report_type,
LocalFrame* accessing_frame,
mojo::PendingRemote<network::mojom::blink::CrossOriginOpenerPolicyReporter>
pending_reporter,
bool endpoint_defined,
const WTF::String& reported_window_url) {
CoopAccessMonitor monitor;
DCHECK(accessing_frame->IsMainFrame());
monitor.report_type = report_type;
monitor.accessing_main_frame = accessing_frame->GetLocalFrameToken();
monitor.endpoint_defined = endpoint_defined;
monitor.reported_window_url = std::move(reported_window_url);
monitor.reporter.Bind(std::move(pending_reporter));
// CoopAccessMonitor are cleared when their reporter are gone. This avoids
// accumulation. However it would have been interesting continuing reporting
// accesses past this point, at least for the ReportingObserver and Devtool.
// TODO(arthursonzogni): Consider observing |accessing_main_frame| deletion
// instead.
monitor.reporter.set_disconnect_handler(
WTF::Bind(&DOMWindow::DisconnectCoopAccessMonitor,
WrapWeakPersistent(this), monitor.accessing_main_frame));
// As long as RenderDocument isn't shipped, it can exist a CoopAccessMonitor
// for the same |accessing_main_frame|, because it might now host a different
// Document. Same is true for |this| DOMWindow, it might refer to a window
// hosting a different document.
// The new documents will still be part of a different virtual browsing
// context group, however the new COOPAccessMonitor might now contain updated
// URLs.
//
// There are up to 2 CoopAccessMonitor for the same access, because it can be
// reported to the accessing and the accessed window at the same time.
for (CoopAccessMonitor& old : coop_access_monitor_) {
if (old.accessing_main_frame == monitor.accessing_main_frame &&
network::IsAccessFromCoopPage(old.report_type) ==
network::IsAccessFromCoopPage(monitor.report_type)) {
old = std::move(monitor);
return;
}
}
coop_access_monitor_.push_back(std::move(monitor));
// Any attempts to access |this| window from |accessing_main_frame| will now
// trigger reports (network, ReportingObserver, Devtool).
}
// Check if the accessing context would be able to access this window if COOP
// was enforced. If this isn't a report is sent.
void DOMWindow::ReportCoopAccess(const char* property_name) {
if (coop_access_monitor_.IsEmpty()) // Fast early return. Very likely true.
return;
v8::Isolate* isolate = window_proxy_manager_->GetIsolate();
LocalDOMWindow* accessing_window = IncumbentDOMWindow(isolate);
LocalFrame* accessing_frame = accessing_window->GetFrame();
// A frame might be destroyed, but its context can still be able to execute
// some code. Those accesses are ignored. See https://crbug.com/1108256.
if (!accessing_frame)
return;
// Iframes are allowed to trigger reports, only when they are same-origin with
// their top-level document.
if (accessing_frame->IsCrossOriginToMainFrame())
return;
// See https://crbug.com/1183571
// We assumed accessing_frame->IsCrossOriginToMainFrame() implies
// accessing_frame->Tree().Top() to be a LocalFrame. This might not be the
// case after all, some crashes are reported. This block speculatively returns
// early to avoid crashing.
// TODO(https://crbug.com/1183571): Check if crashes are still happening and
// remove this block.
if (!accessing_frame->Tree().Top().IsLocalFrame()) {
NOTREACHED();
return;
}
LocalFrame& accessing_main_frame =
To<LocalFrame>(accessing_frame->Tree().Top());
const LocalFrameToken accessing_main_frame_token =
accessing_main_frame.GetLocalFrameToken();
auto* it = coop_access_monitor_.begin();
while (it != coop_access_monitor_.end()) {
if (it->accessing_main_frame != accessing_main_frame_token) {
++it;
continue;
}
// TODO(arthursonzogni): Send the blocked-window-url.
auto location = SourceLocation::Capture(
ExecutionContext::From(isolate->GetCurrentContext()));
// TODO(arthursonzogni): Once implemented, use the SourceLocation typemap
// https://chromium-review.googlesource.com/c/chromium/src/+/2041657
auto source_location = network::mojom::blink::SourceLocation::New(
location->Url() ? location->Url() : "", location->LineNumber(),
location->ColumnNumber());
// TODO(https://crbug.com/1124251): Notify Devtool about the access attempt.
// If the reporting document hasn't specified any network report
// endpoint(s), then it is likely not interested in receiving
// ReportingObserver's reports.
//
// TODO(arthursonzogni): Reconsider this decision later, developers might be
// interested.
if (it->endpoint_defined) {
it->reporter->QueueAccessReport(it->report_type, property_name,
std::move(source_location),
std::move(it->reported_window_url));
// Send a coop-access-violation report.
if (network::IsAccessFromCoopPage(it->report_type)) {
ReportingContext::From(accessing_main_frame.DomWindow())
->QueueReport(MakeGarbageCollected<Report>(
ReportType::kCoopAccessViolation,
accessing_main_frame.GetDocument()->Url().GetString(),
MakeGarbageCollected<CoopAccessViolationReportBody>(
std::move(location), it->report_type, String(property_name),
it->reported_window_url)));
}
}
// CoopAccessMonitor are used once and destroyed. This avoids sending
// multiple reports for the same access.
it = coop_access_monitor_.erase(it);
}
}
void DOMWindow::DoPostMessage(scoped_refptr<SerializedScriptValue> message,
const MessagePortArray& ports,
const WindowPostMessageOptions* options,
LocalDOMWindow* source,
ExceptionState& exception_state) {
TRACE_EVENT0("blink", "DOMWindow::DoPostMessage");
auto* source_frame = source->GetFrame();
bool unload_event_in_progress =
source_frame && source_frame->GetDocument() &&
source_frame->GetDocument()->UnloadEventInProgress();
if (!unload_event_in_progress && source_frame && source_frame->GetPage() &&
source_frame->GetPage()->DispatchedPagehideAndStillHidden()) {
// The postMessage call is done after the pagehide event got dispatched
// and the page is still hidden, which is not normally possible (this
// might happen if we're doing a same-site cross-RenderFrame navigation
// where we dispatch pagehide during the new RenderFrame's commit but
// won't unload/freeze the page after the new RenderFrame finished
// committing). We should track this case to measure how often this is
// happening, except for when the unload event is currently in progress,
// which means the page is not actually stored in the back-forward cache and
// this behavior is ok.
UMA_HISTOGRAM_ENUMERATION("BackForwardCache.SameSite.ActionAfterPagehide2",
ActionAfterPagehide::kSentPostMessage);
}
if (!IsCurrentlyDisplayedInFrame())
return;
// Compute the target origin. We need to do this synchronously in order
// to generate the SyntaxError exception correctly.
scoped_refptr<const SecurityOrigin> target =
PostMessageHelper::GetTargetOrigin(options, *source, exception_state);
if (exception_state.HadException())
return;
if (!target) {
UseCounter::Count(source, WebFeature::kUnspecifiedTargetOriginPostMessage);
}
auto channels = MessagePort::DisentanglePorts(GetExecutionContext(), ports,
exception_state);
if (exception_state.HadException())
return;
const SecurityOrigin* target_security_origin =
GetFrame()->GetSecurityContext()->GetSecurityOrigin();
const SecurityOrigin* source_security_origin = source->GetSecurityOrigin();
auto* local_dom_window = DynamicTo<LocalDOMWindow>(this);
KURL target_url = local_dom_window
? local_dom_window->Url()
: KURL(NullURL(), target_security_origin->ToString());
if (MixedContentChecker::IsMixedContent(source_security_origin, target_url)) {
UseCounter::Count(source, WebFeature::kPostMessageFromSecureToInsecure);
} else if (MixedContentChecker::IsMixedContent(target_security_origin,
source->Url())) {
UseCounter::Count(source, WebFeature::kPostMessageFromInsecureToSecure);
if (MixedContentChecker::IsMixedContent(
GetFrame()->Tree().Top().GetSecurityContext()->GetSecurityOrigin(),
source->Url())) {
UseCounter::Count(source,
WebFeature::kPostMessageFromInsecureToSecureToplevel);
}
}
if (source->GetFrame() &&
source->GetFrame()->Tree().Top() != GetFrame()->Tree().Top()) {
if ((!target_security_origin->RegistrableDomain() &&
target_security_origin->Host() == source_security_origin->Host()) ||
(target_security_origin->RegistrableDomain() &&
target_security_origin->RegistrableDomain() ==
source_security_origin->RegistrableDomain())) {
if (target_security_origin->Protocol() ==
source_security_origin->Protocol()) {
UseCounter::Count(source, WebFeature::kSchemefulSameSitePostMessage);
} else {
UseCounter::Count(source, WebFeature::kSchemelesslySameSitePostMessage);
if (MixedContentChecker::IsMixedContent(source_security_origin,
target_url)) {
UseCounter::Count(
source,
WebFeature::kSchemelesslySameSitePostMessageSecureToInsecure);
} else if (MixedContentChecker::IsMixedContent(target_security_origin,
source->Url())) {
UseCounter::Count(
source,
WebFeature::kSchemelesslySameSitePostMessageInsecureToSecure);
}
}
} else {
UseCounter::Count(source, WebFeature::kCrossSitePostMessage);
}
}
if (!source->GetContentSecurityPolicy()->AllowConnectToSource(
target_url, target_url, RedirectStatus::kNoRedirect,
ReportingDisposition::kSuppressReporting)) {
UseCounter::Count(
source, WebFeature::kPostMessageOutgoingWouldBeBlockedByConnectSrc);
}
UserActivation* user_activation = nullptr;
if (options->includeUserActivation())
user_activation = UserActivation::CreateSnapshot(source);
// TODO(mustaq): This is an ad-hoc mechanism to support delegating a single
// capability. We need to add a structure to support passing other
// capabilities. An explainer for the general delegation API is here:
// https://github.com/mustaqahmed/capability-delegation
bool delegate_payment_request = false;
if (RuntimeEnabledFeatures::CapabilityDelegationPaymentRequestEnabled(
GetExecutionContext()) &&
LocalFrame::HasTransientUserActivation(source_frame) &&
options->hasCreateToken()) {
Vector<String> capability_list;
options->createToken().Split(' ', capability_list);
delegate_payment_request = capability_list.Contains("paymentrequest");
}
MessageEvent* event =
MessageEvent::Create(std::move(channels), std::move(message),
source->GetSecurityOrigin()->ToString(), String(),
source, user_activation, delegate_payment_request);
SchedulePostMessage(event, std::move(target), source);
}
void DOMWindow::Trace(Visitor* visitor) const {
visitor->Trace(frame_);
visitor->Trace(window_proxy_manager_);
visitor->Trace(input_capabilities_);
visitor->Trace(location_);
EventTargetWithInlineData::Trace(visitor);
}
void DOMWindow::DisconnectCoopAccessMonitor(
const LocalFrameToken& accessing_main_frame) {
auto* it = coop_access_monitor_.begin();
while (it != coop_access_monitor_.end()) {
if (it->accessing_main_frame == accessing_main_frame) {
it = coop_access_monitor_.erase(it);
} else {
++it;
}
}
}
} // namespace blink