| OLD | NEW |
| (Empty) | |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "content/browser/loader/content_size_frame_counter.h" |
| 6 |
| 7 #include "content/browser/frame_host/frame_tree.h" |
| 8 #include "content/browser/frame_host/render_frame_host_impl.h" |
| 9 #include "content/browser/loader/content_size_resource_handler_manager.h" |
| 10 #include "content/browser/loader/resource_dispatcher_host_impl.h" |
| 11 #include "content/browser/web_contents/web_contents_impl.h" |
| 12 #include "content/public/browser/browser_thread.h" |
| 13 #include "content/public/browser/navigation_handle.h" |
| 14 #include "content/public/common/content_switches.h" |
| 15 #include "url/origin.h" |
| 16 |
| 17 namespace content { |
| 18 |
| 19 ContentSizeFrameCounter::ContentSizeFrameCounter(WebContents* web_contents) |
| 20 : WebContentsObserver(web_contents) {} |
| 21 |
| 22 ContentSizeFrameCounter::~ContentSizeFrameCounter() {} |
| 23 |
| 24 // TODO(csharrison): This code really wants to be in |
| 25 // WebContentsObserver::ReadyToCommitNavigation, which is not available yet. |
| 26 // Note: this code is all experimental. In the final version, the render process |
| 27 // will notify this component that a given frame should have its data counted. |
| 28 // This will likely be in a mojo interface callback. For now, count all |
| 29 // cross-origin frames as having a data limit set by a command line flag. |
| 30 void ContentSizeFrameCounter::DidFinishNavigation( |
| 31 NavigationHandle* navigation_handle) { |
| 32 if (!navigation_handle->HasCommitted() || navigation_handle->IsErrorPage()) |
| 33 return; |
| 34 const GURL& url = navigation_handle->GetURL(); |
| 35 // Only consider http/s or about:blank urls for blocking. |
| 36 if (!url.SchemeIsHTTPOrHTTPS() && |
| 37 url.spec().compare(url::kAboutBlankURL) != 0) { |
| 38 return; |
| 39 } |
| 40 |
| 41 if (web_contents()->GetLastCommittedURL().SchemeIs("chrome")) |
| 42 return; |
| 43 |
| 44 // TODO(csharrison): Navigations to about:blank are not counted as |
| 45 // same-origin. |
| 46 if (url::Origin(web_contents()->GetLastCommittedURL()) |
| 47 .IsSameOriginWith(url::Origin(url))) { |
| 48 return; |
| 49 } |
| 50 |
| 51 std::string policy_string = |
| 52 base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII( |
| 53 switches::kContentSizePolicy); |
| 54 uint64_t content_size = 0; |
| 55 base::StringToUint64(policy_string, &content_size); |
| 56 |
| 57 RenderFrameHostImpl* rfh = static_cast<RenderFrameHostImpl*>( |
| 58 navigation_handle->GetRenderFrameHost()); |
| 59 FrameSize* frame_size = AddNewFrame(rfh->frame_tree_node(), content_size); |
| 60 if (frame_size->content_left < INT_MAX) { |
| 61 BrowserThread::PostTask( |
| 62 BrowserThread::IO, FROM_HERE, |
| 63 base::Bind(&ContentSizeFrameCounter::NotifyFrameHasLimit, |
| 64 rfh->GetGlobalFrameRoutingId(), frame_size->content_left)); |
| 65 } |
| 66 } |
| 67 |
| 68 // static |
| 69 // This method iterates through all ContentSizeFrameCounters, and calls |
| 70 // UpdateDataAccounting on them with updates from the IO thread. |
| 71 void ContentSizeFrameCounter::UpdateGlobalDataAccounting( |
| 72 std::unique_ptr<std::map<GlobalFrameRoutingId, uint64_t>> updates) { |
| 73 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| 74 DCHECK(updates); |
| 75 std::map<ContentSizeFrameCounter*, std::map<GlobalFrameRoutingId, uint64_t>> |
| 76 per_notifier_map; |
| 77 for (const auto& it : *updates) { |
| 78 RenderFrameHostImpl* rfh = RenderFrameHostImpl::FromID( |
| 79 it.first.child_id, it.first.frame_routing_id); |
| 80 WebContents* contents = WebContents::FromRenderFrameHost(rfh); |
| 81 if (!contents) |
| 82 continue; |
| 83 ContentSizeFrameCounter* notifier = |
| 84 static_cast<WebContentsImpl*>(contents)->content_size_frame_counter(); |
| 85 per_notifier_map[notifier][it.first] += it.second; |
| 86 } |
| 87 for (const auto& it : per_notifier_map) { |
| 88 it.first->UpdateDataAccounting(it.second); |
| 89 } |
| 90 } |
| 91 |
| 92 ContentSizeFrameCounter::FrameSize* ContentSizeFrameCounter::AddNewFrame( |
| 93 FrameTreeNode* node, |
| 94 uint64_t limit) { |
| 95 FrameSize* min_parent_frame_size = nullptr; |
| 96 for (FrameTreeNode* current_node = node->parent(); current_node != nullptr; |
| 97 current_node = current_node->parent()) { |
| 98 std::map<int, FrameSize>::iterator size_it = |
| 99 frame_tree_node_sizes_.find(current_node->frame_tree_node_id()); |
| 100 if (size_it != frame_tree_node_sizes_.end() && |
| 101 (!min_parent_frame_size || |
| 102 size_it->second.content_left < min_parent_frame_size->content_left)) { |
| 103 min_parent_frame_size = &size_it->second; |
| 104 } |
| 105 } |
| 106 |
| 107 // No need to add this to the map if a parent frame has a lower limit. |
| 108 if (min_parent_frame_size && min_parent_frame_size->content_left <= limit) |
| 109 return min_parent_frame_size; |
| 110 FrameSize* size = &frame_tree_node_sizes_[node->frame_tree_node_id()]; |
| 111 size->content_left = limit; |
| 112 return size; |
| 113 } |
| 114 |
| 115 void ContentSizeFrameCounter::ApplyUpdateToTree( |
| 116 FrameTreeNode* node, |
| 117 uint64_t bytes_downloaded, |
| 118 std::set<GlobalFrameRoutingId>* routing_ids_to_block) { |
| 119 FrameTreeNode* highest_node_to_block = nullptr; |
| 120 for (FrameTreeNode* current_node = node; current_node != nullptr; |
| 121 current_node = current_node->parent()) { |
| 122 std::map<int, FrameSize>::iterator size_it = |
| 123 frame_tree_node_sizes_.find(current_node->frame_tree_node_id()); |
| 124 if (size_it == frame_tree_node_sizes_.end()) |
| 125 continue; |
| 126 |
| 127 if (size_it->second.content_left < bytes_downloaded) { |
| 128 size_it->second.content_left = 0; |
| 129 highest_node_to_block = node; |
| 130 } else { |
| 131 size_it->second.content_left -= bytes_downloaded; |
| 132 } |
| 133 } |
| 134 |
| 135 // No frame went over the size limit. |
| 136 if (!highest_node_to_block) |
| 137 return; |
| 138 |
| 139 // Collect all the routing ids to block in the subtree defined by |
| 140 // |highest_node_to_block|. |
| 141 for (FrameTreeNode* ftn : highest_node_to_block->frame_tree()->SubtreeNodes( |
| 142 highest_node_to_block)) { |
| 143 routing_ids_to_block->insert( |
| 144 ftn->current_frame_host()->GetGlobalFrameRoutingId()); |
| 145 } |
| 146 } |
| 147 |
| 148 void ContentSizeFrameCounter::UpdateDataAccounting( |
| 149 std::map<GlobalFrameRoutingId, uint64_t> updates) { |
| 150 std::unique_ptr<std::set<GlobalFrameRoutingId>> frames_over_limit( |
| 151 new std::set<GlobalFrameRoutingId>); |
| 152 for (const auto& it : updates) { |
| 153 RenderFrameHostImpl* rfh = static_cast<RenderFrameHostImpl*>( |
| 154 RenderFrameHost::FromID(it.first.child_id, it.first.frame_routing_id)); |
| 155 if (!rfh) |
| 156 continue; |
| 157 ApplyUpdateToTree(rfh->frame_tree_node(), it.second, |
| 158 frames_over_limit.get()); |
| 159 } |
| 160 if (!frames_over_limit->empty()) { |
| 161 BrowserThread::PostTask( |
| 162 BrowserThread::IO, FROM_HERE, |
| 163 base::Bind(&ContentSizeFrameCounter::NotifyFramesWentOverSizeLimits, |
| 164 base::Passed(std::move(frames_over_limit)))); |
| 165 } |
| 166 } |
| 167 |
| 168 // static |
| 169 void ContentSizeFrameCounter::NotifyFramesWentOverSizeLimits( |
| 170 std::unique_ptr<std::set<GlobalFrameRoutingId>> frames) { |
| 171 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| 172 ResourceDispatcherHostImpl* rdh = ResourceDispatcherHostImpl::Get(); |
| 173 if (rdh && rdh->content_size_manager()) { |
| 174 rdh->content_size_manager()->FramesWentOverSizeLimits(std::move(frames)); |
| 175 } |
| 176 } |
| 177 |
| 178 // static |
| 179 void ContentSizeFrameCounter::NotifyFrameHasLimit( |
| 180 const GlobalFrameRoutingId& frame, |
| 181 uint64_t limit) { |
| 182 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| 183 ResourceDispatcherHostImpl* rdh = ResourceDispatcherHostImpl::Get(); |
| 184 if (rdh && rdh->content_size_manager()) { |
| 185 rdh->content_size_manager()->FrameHasSizeLimit(frame, limit); |
| 186 } |
| 187 } |
| 188 |
| 189 } // namespace content |
| OLD | NEW |