blob: 491f16e1e2c13548102f7bc329dd41e88cad77dd [file] [log] [blame]
// Copyright 2016 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 "chrome/browser/permissions/delegation_tracker.h"
#include <unordered_set>
#include "base/memory/ptr_util.h"
#include "chrome/browser/permissions/permission_util.h"
#include "content/public/browser/permission_type.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_contents_observer.h"
class DelegationTracker::DelegatedForChild : content::WebContentsObserver {
public:
DelegatedForChild(content::RenderFrameHost* child_rfh,
const std::vector<content::PermissionType>& permissions,
const base::Callback<void(content::RenderFrameHost*)>&
rfh_destroyed_callback)
: content::WebContentsObserver(
content::WebContents::FromRenderFrameHost(child_rfh)),
child_rfh_(child_rfh),
permissions_(permissions.begin(), permissions.end()),
rfh_destroyed_callback_(rfh_destroyed_callback) {}
~DelegatedForChild() override {}
bool HasPermission(const content::PermissionType& permission) {
return permissions_.count(permission) == 1;
}
private:
DISALLOW_COPY_AND_ASSIGN(DelegatedForChild);
void ClearPermissions(content::RenderFrameHost* render_frame_host) {
if (render_frame_host == child_rfh_)
rfh_destroyed_callback_.Run(render_frame_host); // Will delete |this|.
}
// WebContentsObserver
void RenderFrameHostChanged(content::RenderFrameHost* old_host,
content::RenderFrameHost* new_host) override {
ClearPermissions(old_host);
}
void FrameDeleted(content::RenderFrameHost* render_frame_host) override {
ClearPermissions(render_frame_host);
}
void DidNavigateAnyFrame(
content::RenderFrameHost* render_frame_host,
const content::LoadCommittedDetails& details,
const content::FrameNavigateParams& params) override {
ClearPermissions(render_frame_host);
}
content::RenderFrameHost* child_rfh_;
std::unordered_set<content::PermissionType, PermissionTypeHash> permissions_;
base::Callback<void(content::RenderFrameHost*)> rfh_destroyed_callback_;
};
DelegationTracker::DelegationTracker() {}
DelegationTracker::~DelegationTracker() {}
void DelegationTracker::SetDelegatedPermissions(
content::RenderFrameHost* child_rfh,
const std::vector<content::PermissionType>& permissions) {
DCHECK(child_rfh && child_rfh->GetParent());
delegated_permissions_[child_rfh] = base::MakeUnique<DelegatedForChild>(
child_rfh, permissions,
base::Bind(&DelegationTracker::RenderFrameHostChanged,
base::Unretained(this)));
}
bool DelegationTracker::IsGranted(content::RenderFrameHost* requesting_rfh,
const content::PermissionType& permission) {
content::RenderFrameHost* child_rfh = requesting_rfh;
while (child_rfh->GetParent()) {
// Parents with unique origins can't delegate permission.
url::Origin parent_origin =
child_rfh->GetParent()->GetLastCommittedOrigin();
if (parent_origin.unique())
return false;
if (!child_rfh->GetLastCommittedOrigin().IsSameOriginWith(parent_origin)) {
const auto& it = delegated_permissions_.find(child_rfh);
if (it == delegated_permissions_.end())
return false;
if (!it->second->HasPermission(permission))
return false;
}
child_rfh = child_rfh->GetParent();
}
return true;
}
void DelegationTracker::RenderFrameHostChanged(content::RenderFrameHost* rfh) {
delegated_permissions_.erase(rfh);
}