blob: a7cf10cf3b4084eb3a36382c4ea990199456abd1 [file]
// Copyright 2021 The Chromium Authors
// 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/permissions_policy/permissions_policy_devtools_support.h"
#include "services/network/public/cpp/permissions_policy/permissions_policy.h"
#include "third_party/blink/renderer/core/execution_context/security_context.h"
#include "third_party/blink/renderer/core/frame/frame.h"
#include "third_party/blink/renderer/core/inspector/identifiers_factory.h"
#include "third_party/blink/renderer/platform/weborigin/security_origin.h"
namespace blink {
std::optional<PermissionsPolicyBlockLocator> TracePermissionsPolicyBlockSource(
Frame* frame,
network::mojom::PermissionsPolicyFeature feature) {
const network::PermissionsPolicy* current_policy =
frame->GetSecurityContext()->GetPermissionsPolicy();
DCHECK(current_policy);
if (current_policy->IsFeatureEnabled(feature))
return std::nullopt;
// All permissions are disabled by default for fenced frames, irrespective of
// headers (see PermissionsPolicy::CreateFixedForFencedFrame).
if (frame->IsInFencedFrameTree()) {
return PermissionsPolicyBlockLocator{
IdentifiersFactory::FrameId(frame),
PermissionsPolicyBlockReason::kInFencedFrameTree,
};
}
Frame* current_frame = frame;
Frame* child_frame = nullptr;
// Trace up the frame tree until feature is not disabled by inherited policy
// in |current_frame| or until reaching the top of the frame tree for
// isolated apps.
// After the trace up, the only 3 possibilities for a feature to be disabled
// become
// - The HTTP header of |current_frame|.
// - The iframe attribute on |child_frame|'s html frame owner element.
// - The frame tree belongs to an isolated app, which must not have have the
// feature enabled at the top level frame.
while (true) {
DCHECK(current_frame);
current_policy =
current_frame->GetSecurityContext()->GetPermissionsPolicy();
DCHECK(current_policy);
if (current_policy->IsFeatureEnabledByInheritedPolicy(feature))
break;
// For isolated apps, the top level frame might not have the feature
// enabled.
if (!current_frame->Tree().Parent()) {
return PermissionsPolicyBlockLocator{
IdentifiersFactory::FrameId(current_frame),
PermissionsPolicyBlockReason::kInIsolatedApp,
};
}
child_frame = current_frame;
current_frame = current_frame->Tree().Parent();
}
const network::PermissionsPolicy::Allowlist allowlist =
current_policy->GetAllowlistForDevTools(feature);
bool allowed_by_current_frame = allowlist.Contains(
current_frame->GetSecurityContext()->GetSecurityOrigin()->ToUrlOrigin());
bool allowed_by_child_frame =
!child_frame || allowlist.Contains(child_frame->GetSecurityContext()
->GetSecurityOrigin()
->ToUrlOrigin());
if (!allowed_by_current_frame || !allowed_by_child_frame) {
// Feature disabled by allowlist, i.e. value in HTTP header.
return PermissionsPolicyBlockLocator{
IdentifiersFactory::FrameId(current_frame),
PermissionsPolicyBlockReason::kHeader,
};
} else {
// Otherwise, feature must be disabled by iframe attribute.
// |child_frame| is nullptr iff
// - feature is disabled in the starting frame (1)
// - feature is enabled by inherited policy in the starting frame (2)
// Container policy (iframe attribute) is part of inherited policy.
// Along with (2), we can conclude feature is enabled by container policy
// (iframe attribute) which contradicts with the else branch condition.
DCHECK(child_frame);
return PermissionsPolicyBlockLocator{
IdentifiersFactory::FrameId(child_frame),
PermissionsPolicyBlockReason::kIframeAttribute,
};
}
}
} // namespace blink