| Index: content/browser/loader/content_size_frame_counter.cc
|
| diff --git a/content/browser/loader/content_size_frame_counter.cc b/content/browser/loader/content_size_frame_counter.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..6c5e0f9512502b5b36ef93a56773ef107f218353
|
| --- /dev/null
|
| +++ b/content/browser/loader/content_size_frame_counter.cc
|
| @@ -0,0 +1,189 @@
|
| +// 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 "content/browser/loader/content_size_frame_counter.h"
|
| +
|
| +#include "content/browser/frame_host/frame_tree.h"
|
| +#include "content/browser/frame_host/render_frame_host_impl.h"
|
| +#include "content/browser/loader/content_size_resource_handler_manager.h"
|
| +#include "content/browser/loader/resource_dispatcher_host_impl.h"
|
| +#include "content/browser/web_contents/web_contents_impl.h"
|
| +#include "content/public/browser/browser_thread.h"
|
| +#include "content/public/browser/navigation_handle.h"
|
| +#include "content/public/common/content_switches.h"
|
| +#include "url/origin.h"
|
| +
|
| +namespace content {
|
| +
|
| +ContentSizeFrameCounter::ContentSizeFrameCounter(WebContents* web_contents)
|
| + : WebContentsObserver(web_contents) {}
|
| +
|
| +ContentSizeFrameCounter::~ContentSizeFrameCounter() {}
|
| +
|
| +// TODO(csharrison): This code really wants to be in
|
| +// WebContentsObserver::ReadyToCommitNavigation, which is not available yet.
|
| +// Note: this code is all experimental. In the final version, the render process
|
| +// will notify this component that a given frame should have its data counted.
|
| +// This will likely be in a mojo interface callback. For now, count all
|
| +// cross-origin frames as having a data limit set by a command line flag.
|
| +void ContentSizeFrameCounter::DidFinishNavigation(
|
| + NavigationHandle* navigation_handle) {
|
| + if (!navigation_handle->HasCommitted() || navigation_handle->IsErrorPage())
|
| + return;
|
| + const GURL& url = navigation_handle->GetURL();
|
| + // Only consider http/s or about:blank urls for blocking.
|
| + if (!url.SchemeIsHTTPOrHTTPS() &&
|
| + url.spec().compare(url::kAboutBlankURL) != 0) {
|
| + return;
|
| + }
|
| +
|
| + if (web_contents()->GetLastCommittedURL().SchemeIs("chrome"))
|
| + return;
|
| +
|
| + // TODO(csharrison): Navigations to about:blank are not counted as
|
| + // same-origin.
|
| + if (url::Origin(web_contents()->GetLastCommittedURL())
|
| + .IsSameOriginWith(url::Origin(url))) {
|
| + return;
|
| + }
|
| +
|
| + std::string policy_string =
|
| + base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
|
| + switches::kContentSizePolicy);
|
| + uint64_t content_size = 0;
|
| + base::StringToUint64(policy_string, &content_size);
|
| +
|
| + RenderFrameHostImpl* rfh = static_cast<RenderFrameHostImpl*>(
|
| + navigation_handle->GetRenderFrameHost());
|
| + FrameSize* frame_size = AddNewFrame(rfh->frame_tree_node(), content_size);
|
| + if (frame_size->content_left < INT_MAX) {
|
| + BrowserThread::PostTask(
|
| + BrowserThread::IO, FROM_HERE,
|
| + base::Bind(&ContentSizeFrameCounter::NotifyFrameHasLimit,
|
| + rfh->GetGlobalFrameRoutingId(), frame_size->content_left));
|
| + }
|
| +}
|
| +
|
| +// static
|
| +// This method iterates through all ContentSizeFrameCounters, and calls
|
| +// UpdateDataAccounting on them with updates from the IO thread.
|
| +void ContentSizeFrameCounter::UpdateGlobalDataAccounting(
|
| + std::unique_ptr<std::map<GlobalFrameRoutingId, uint64_t>> updates) {
|
| + DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
| + DCHECK(updates);
|
| + std::map<ContentSizeFrameCounter*, std::map<GlobalFrameRoutingId, uint64_t>>
|
| + per_notifier_map;
|
| + for (const auto& it : *updates) {
|
| + RenderFrameHostImpl* rfh = RenderFrameHostImpl::FromID(
|
| + it.first.child_id, it.first.frame_routing_id);
|
| + WebContents* contents = WebContents::FromRenderFrameHost(rfh);
|
| + if (!contents)
|
| + continue;
|
| + ContentSizeFrameCounter* notifier =
|
| + static_cast<WebContentsImpl*>(contents)->content_size_frame_counter();
|
| + per_notifier_map[notifier][it.first] += it.second;
|
| + }
|
| + for (const auto& it : per_notifier_map) {
|
| + it.first->UpdateDataAccounting(it.second);
|
| + }
|
| +}
|
| +
|
| +ContentSizeFrameCounter::FrameSize* ContentSizeFrameCounter::AddNewFrame(
|
| + FrameTreeNode* node,
|
| + uint64_t limit) {
|
| + FrameSize* min_parent_frame_size = nullptr;
|
| + for (FrameTreeNode* current_node = node->parent(); current_node != nullptr;
|
| + current_node = current_node->parent()) {
|
| + std::map<int, FrameSize>::iterator size_it =
|
| + frame_tree_node_sizes_.find(current_node->frame_tree_node_id());
|
| + if (size_it != frame_tree_node_sizes_.end() &&
|
| + (!min_parent_frame_size ||
|
| + size_it->second.content_left < min_parent_frame_size->content_left)) {
|
| + min_parent_frame_size = &size_it->second;
|
| + }
|
| + }
|
| +
|
| + // No need to add this to the map if a parent frame has a lower limit.
|
| + if (min_parent_frame_size && min_parent_frame_size->content_left <= limit)
|
| + return min_parent_frame_size;
|
| + FrameSize* size = &frame_tree_node_sizes_[node->frame_tree_node_id()];
|
| + size->content_left = limit;
|
| + return size;
|
| +}
|
| +
|
| +void ContentSizeFrameCounter::ApplyUpdateToTree(
|
| + FrameTreeNode* node,
|
| + uint64_t bytes_downloaded,
|
| + std::set<GlobalFrameRoutingId>* routing_ids_to_block) {
|
| + FrameTreeNode* highest_node_to_block = nullptr;
|
| + for (FrameTreeNode* current_node = node; current_node != nullptr;
|
| + current_node = current_node->parent()) {
|
| + std::map<int, FrameSize>::iterator size_it =
|
| + frame_tree_node_sizes_.find(current_node->frame_tree_node_id());
|
| + if (size_it == frame_tree_node_sizes_.end())
|
| + continue;
|
| +
|
| + if (size_it->second.content_left < bytes_downloaded) {
|
| + size_it->second.content_left = 0;
|
| + highest_node_to_block = node;
|
| + } else {
|
| + size_it->second.content_left -= bytes_downloaded;
|
| + }
|
| + }
|
| +
|
| + // No frame went over the size limit.
|
| + if (!highest_node_to_block)
|
| + return;
|
| +
|
| + // Collect all the routing ids to block in the subtree defined by
|
| + // |highest_node_to_block|.
|
| + for (FrameTreeNode* ftn : highest_node_to_block->frame_tree()->SubtreeNodes(
|
| + highest_node_to_block)) {
|
| + routing_ids_to_block->insert(
|
| + ftn->current_frame_host()->GetGlobalFrameRoutingId());
|
| + }
|
| +}
|
| +
|
| +void ContentSizeFrameCounter::UpdateDataAccounting(
|
| + std::map<GlobalFrameRoutingId, uint64_t> updates) {
|
| + std::unique_ptr<std::set<GlobalFrameRoutingId>> frames_over_limit(
|
| + new std::set<GlobalFrameRoutingId>);
|
| + for (const auto& it : updates) {
|
| + RenderFrameHostImpl* rfh = static_cast<RenderFrameHostImpl*>(
|
| + RenderFrameHost::FromID(it.first.child_id, it.first.frame_routing_id));
|
| + if (!rfh)
|
| + continue;
|
| + ApplyUpdateToTree(rfh->frame_tree_node(), it.second,
|
| + frames_over_limit.get());
|
| + }
|
| + if (!frames_over_limit->empty()) {
|
| + BrowserThread::PostTask(
|
| + BrowserThread::IO, FROM_HERE,
|
| + base::Bind(&ContentSizeFrameCounter::NotifyFramesWentOverSizeLimits,
|
| + base::Passed(std::move(frames_over_limit))));
|
| + }
|
| +}
|
| +
|
| +// static
|
| +void ContentSizeFrameCounter::NotifyFramesWentOverSizeLimits(
|
| + std::unique_ptr<std::set<GlobalFrameRoutingId>> frames) {
|
| + DCHECK_CURRENTLY_ON(BrowserThread::IO);
|
| + ResourceDispatcherHostImpl* rdh = ResourceDispatcherHostImpl::Get();
|
| + if (rdh && rdh->content_size_manager()) {
|
| + rdh->content_size_manager()->FramesWentOverSizeLimits(std::move(frames));
|
| + }
|
| +}
|
| +
|
| +// static
|
| +void ContentSizeFrameCounter::NotifyFrameHasLimit(
|
| + const GlobalFrameRoutingId& frame,
|
| + uint64_t limit) {
|
| + DCHECK_CURRENTLY_ON(BrowserThread::IO);
|
| + ResourceDispatcherHostImpl* rdh = ResourceDispatcherHostImpl::Get();
|
| + if (rdh && rdh->content_size_manager()) {
|
| + rdh->content_size_manager()->FrameHasSizeLimit(frame, limit);
|
| + }
|
| +}
|
| +
|
| +} // namespace content
|
|
|