Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "content/browser/loader/loader_io_thread_notifier.h" | 5 #include "content/browser/loader/loader_io_thread_notifier.h" |
| 6 | 6 |
| 7 #include "content/browser/frame_host/render_frame_host_impl.h" | 7 #include "content/browser/frame_host/render_frame_host_impl.h" |
| 8 #include "content/browser/loader/global_routing_id.h" | 8 #include "content/browser/loader/content_size_resource_handler.h" |
| 9 #include "content/browser/loader/resource_dispatcher_host_impl.h" | 9 #include "content/browser/loader/resource_dispatcher_host_impl.h" |
| 10 #include "content/browser/web_contents/web_contents_impl.h" | |
| 10 #include "content/public/browser/browser_thread.h" | 11 #include "content/public/browser/browser_thread.h" |
| 12 #include "content/public/browser/navigation_handle.h" | |
| 13 #include "content/public/common/content_switches.h" | |
| 14 #include "url/origin.h" | |
| 11 | 15 |
| 12 namespace content { | 16 namespace content { |
| 13 | 17 |
| 14 namespace { | 18 namespace { |
| 15 | 19 |
| 16 void NotifyRenderFrameDeletedOnIO(const GlobalFrameRoutingId& id) { | 20 void NotifyRenderFrameDeletedOnIO(const GlobalFrameRoutingId& id) { |
| 17 ResourceDispatcherHostImpl* rdhi = ResourceDispatcherHostImpl::Get(); | 21 ResourceDispatcherHostImpl* rdhi = ResourceDispatcherHostImpl::Get(); |
| 18 if (rdhi) | 22 if (rdhi) |
| 19 rdhi->OnRenderFrameDeleted(id); | 23 rdhi->OnRenderFrameDeleted(id); |
| 20 } | 24 } |
| 21 | 25 |
| 26 class ContentSizeNavigationThrottle : public NavigationThrottle { | |
| 27 public: | |
| 28 ContentSizeNavigationThrottle(NavigationHandle* handle, | |
| 29 LoaderIOThreadNotifier* notifier) | |
| 30 : NavigationThrottle(handle), notifier_(notifier) {} | |
| 31 ~ContentSizeNavigationThrottle() override {} | |
| 32 | |
| 33 // NavigationThrottle: | |
| 34 NavigationThrottle::ThrottleCheckResult WillProcessResponse() override { | |
| 35 return notifier_->WillProcessNavigationResponse(navigation_handle()); | |
| 36 } | |
| 37 | |
| 38 private: | |
| 39 LoaderIOThreadNotifier* notifier_; | |
| 40 | |
| 41 DISALLOW_COPY_AND_ASSIGN(ContentSizeNavigationThrottle); | |
| 42 }; | |
| 43 | |
| 22 } // namespace | 44 } // namespace |
| 23 | 45 |
| 24 LoaderIOThreadNotifier::LoaderIOThreadNotifier(WebContents* web_contents) | 46 LoaderIOThreadNotifier::LoaderIOThreadNotifier(WebContents* web_contents) |
| 25 : WebContentsObserver(web_contents) {} | 47 : WebContentsObserver(web_contents) {} |
| 26 | 48 |
| 27 LoaderIOThreadNotifier::~LoaderIOThreadNotifier() {} | 49 LoaderIOThreadNotifier::~LoaderIOThreadNotifier() {} |
| 28 | 50 |
| 29 void LoaderIOThreadNotifier::RenderFrameDeleted( | 51 void LoaderIOThreadNotifier::RenderFrameDeleted( |
| 30 RenderFrameHost* render_frame_host) { | 52 RenderFrameHost* render_frame_host) { |
| 31 BrowserThread::PostTask( | 53 BrowserThread::PostTask( |
| 32 BrowserThread::IO, FROM_HERE, | 54 BrowserThread::IO, FROM_HERE, |
| 33 base::Bind(&NotifyRenderFrameDeletedOnIO, | 55 base::Bind(&NotifyRenderFrameDeletedOnIO, |
| 34 static_cast<RenderFrameHostImpl*>(render_frame_host) | 56 static_cast<RenderFrameHostImpl*>(render_frame_host) |
| 35 ->GetGlobalFrameRoutingId())); | 57 ->GetGlobalFrameRoutingId())); |
| 36 } | 58 } |
| 37 | 59 |
| 60 // static | |
| 61 NavigationThrottle* LoaderIOThreadNotifier::MaybeCreateThrottle( | |
| 62 NavigationHandle* handle) { | |
| 63 if (handle->IsInMainFrame()) | |
|
mmenke
2016/08/02 18:02:11
A main frame navigation is a navigation "in" a mai
Charlie Harrison
2016/08/02 18:35:36
Right. Now that you mention it, this is confusingl
| |
| 64 return nullptr; | |
| 65 if (!base::CommandLine::ForCurrentProcess()->HasSwitch( | |
| 66 switches::kContentSizePolicy)) { | |
| 67 return nullptr; | |
| 68 } | |
| 69 | |
| 70 LoaderIOThreadNotifier* notifier = | |
| 71 static_cast<WebContentsImpl*>(handle->GetWebContents()) | |
| 72 ->loader_io_thread_notifier(); | |
| 73 return new ContentSizeNavigationThrottle(handle, notifier); | |
| 74 } | |
| 75 | |
| 76 // TODO(csharrison): This should be moved to ReadyToCommitNavigation once that | |
| 77 // is available. This callback is incorrect for transfer navigations. | |
| 78 NavigationThrottle::ThrottleCheckResult | |
| 79 LoaderIOThreadNotifier::WillProcessNavigationResponse( | |
| 80 NavigationHandle* navigation_handle) { | |
|
mmenke
2016/08/02 18:02:11
Do we really need a NavigationThrottle for this?
Charlie Harrison
2016/08/02 18:35:36
So, actually I think I will remove the throttle. I
| |
| 81 const GURL& url = navigation_handle->GetURL(); | |
| 82 // Only consider http/s or about:blank urls for blocking. | |
|
mmenke
2016/08/02 18:02:11
Why do we care about about:blank? Is there a spec
Charlie Harrison
2016/08/02 18:35:36
No spec yet :(
The only reason I added about:blan
| |
| 83 if (!url.SchemeIsHTTPOrHTTPS() && | |
| 84 url.spec().compare(url::kAboutBlankURL) != 0) { | |
| 85 return NavigationThrottle::PROCEED; | |
| 86 } | |
| 87 | |
| 88 if (web_contents()->GetLastCommittedURL().SchemeIs("chrome")) | |
| 89 return NavigationThrottle::PROCEED; | |
| 90 | |
| 91 // TODO(csharrison): Navigations to about:blank are not counted as | |
| 92 // same-origin. | |
| 93 if (url::Origin(web_contents()->GetLastCommittedURL()) | |
| 94 .IsSameOriginWith(url::Origin(url))) { | |
| 95 return NavigationThrottle::PROCEED; | |
| 96 } | |
| 97 | |
| 98 std::string policy_string = | |
| 99 base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII( | |
| 100 switches::kContentSizePolicy); | |
|
mmenke
2016/08/05 17:18:17
Why are we getting this as a command line string..
Charlie Harrison
2016/08/08 14:04:38
Yes. We wanted to land an experiment that just app
| |
| 101 int content_size = 0; | |
| 102 base::StringToInt(policy_string, &content_size); | |
|
mmenke
2016/08/05 17:16:27
Should we be using int64_t here, or even uint64_t?
mmenke
2016/08/05 17:18:17
Oh, and I don't think anyone will be setting a lim
Charlie Harrison
2016/08/08 14:04:38
Yeah why not. Changing it to uint64_t sgtm. I had
| |
| 103 | |
| 104 RenderFrameHostImpl* rfh = static_cast<RenderFrameHostImpl*>( | |
| 105 navigation_handle->GetRenderFrameHost()); | |
| 106 FrameSize* frame_size = AddNewFrame(rfh->frame_tree_node(), content_size); | |
| 107 if (frame_size->content_left < INT_MAX) { | |
| 108 BrowserThread::PostTask( | |
| 109 BrowserThread::IO, FROM_HERE, | |
| 110 base::Bind(&LoaderIOThreadNotifier::NotifyFrameHasLimit, | |
| 111 rfh->GetGlobalFrameRoutingId(), frame_size->content_left)); | |
| 112 } | |
| 113 return frame_size->content_left <= 0 ? NavigationThrottle::CANCEL | |
| 114 : NavigationThrottle::PROCEED; | |
| 115 } | |
| 116 | |
| 117 void LoaderIOThreadNotifier::RenderFrameCreated( | |
| 118 RenderFrameHost* render_frame_host) { | |
|
mmenke
2016/08/05 17:16:27
This doesn't do anything.
Charlie Harrison
2016/08/08 14:04:38
Sorry about that. Removed.
| |
| 119 } | |
| 120 | |
| 121 // static | |
| 122 // This method iterates through all LoaderIOThreadNotifiers, and calls | |
| 123 // UpdateDataAccounting on them with updates from the IO thread. | |
| 124 void LoaderIOThreadNotifier::UpdateGlobalDataAccounting( | |
| 125 std::unique_ptr<std::map<GlobalFrameRoutingId, int>> updates) { | |
| 126 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
| 127 DCHECK(updates); | |
| 128 std::map<LoaderIOThreadNotifier*, std::map<GlobalFrameRoutingId, int>> | |
| 129 per_notifier_map; | |
| 130 for (const auto& it : *updates) { | |
| 131 RenderFrameHostImpl* rfh = RenderFrameHostImpl::FromID( | |
| 132 it.first.child_id, it.first.frame_routing_id); | |
| 133 WebContents* contents = WebContents::FromRenderFrameHost(rfh); | |
|
mmenke
2016/08/02 18:02:11
By creating and killing iframes, and then re-reque
Charlie Harrison
2016/08/02 18:35:37
No, because we count bytes for resources loaded fr
| |
| 134 if (!contents) | |
| 135 continue; | |
| 136 LoaderIOThreadNotifier* notifier = | |
| 137 static_cast<WebContentsImpl*>(contents)->loader_io_thread_notifier(); | |
| 138 per_notifier_map[notifier][it.first] = it.second; | |
|
mmenke
2016/08/02 18:02:11
+=? We can have multiple notifications for the sa
Charlie Harrison
2016/08/02 18:35:37
Acknowledged. Think you're right.
Charlie Harrison
2016/08/08 14:04:39
Done.
| |
| 139 } | |
| 140 for (const auto& it : per_notifier_map) { | |
| 141 it.first->UpdateDataAccounting(it.second); | |
| 142 } | |
| 143 } | |
| 144 | |
| 145 LoaderIOThreadNotifier::FrameSize* LoaderIOThreadNotifier::AddNewFrame( | |
| 146 FrameTreeNode* node, | |
| 147 int limit) { | |
| 148 FrameSize* frame_size = GetEffectiveLimit(node); | |
| 149 // No need to add this to the map if a parent frame has a lower limit. | |
| 150 if (!frame_size || frame_size->content_left > limit) { | |
| 151 FrameSize* size = &frame_tree_node_sizes_[node->frame_tree_node_id()]; | |
|
mmenke
2016/08/02 18:02:11
Half this code uses frame_tree_node_ids, half uses
Charlie Harrison
2016/08/02 18:35:37
*sigh* that's one of the complications here.
fram
| |
| 152 size->content_limit = limit; | |
| 153 size->content_left = limit; | |
| 154 return size; | |
| 155 } | |
| 156 return frame_size; | |
| 157 } | |
| 158 | |
| 159 // This method walks the frame tree and returns the FrameSize of the frame with | |
| 160 // the lowest limit. This is the effective bytes limit of |node|. | |
| 161 LoaderIOThreadNotifier::FrameSize* | |
| 162 LoaderIOThreadNotifier::GetEffectiveLimit(FrameTreeNode* node) { | |
| 163 FrameSize* frame_size = nullptr; | |
| 164 FrameTreeNode* current_node = node; | |
| 165 while (current_node) { | |
| 166 std::map<int, FrameSize>::iterator size_it = | |
| 167 frame_tree_node_sizes_.find(current_node->frame_tree_node_id()); | |
| 168 if (size_it != frame_tree_node_sizes_.end() && | |
| 169 (!frame_size || | |
| 170 size_it->second.content_left < frame_size->content_left)) { | |
| 171 frame_size = &size_it->second; | |
| 172 } | |
| 173 current_node = current_node->parent(); | |
| 174 } | |
| 175 return frame_size; | |
| 176 } | |
| 177 | |
| 178 void LoaderIOThreadNotifier::UpdateDataAccounting( | |
| 179 std::map<GlobalFrameRoutingId, int> updates) { | |
| 180 std::unique_ptr<std::set<GlobalFrameRoutingId>> frames_over_limit( | |
| 181 new std::set<GlobalFrameRoutingId>); | |
| 182 for (const auto& it : updates) { | |
| 183 RenderFrameHostImpl* rfh = static_cast<RenderFrameHostImpl*>( | |
| 184 RenderFrameHost::FromID(it.first.child_id, it.first.frame_routing_id)); | |
| 185 FrameSize* frame_size = GetEffectiveLimit(rfh->frame_tree_node()); | |
| 186 DCHECK(frame_size); | |
| 187 frame_size->content_left -= it.second; | |
|
mmenke
2016/08/02 18:02:12
This should update the content_left value of all f
Charlie Harrison
2016/08/02 18:35:36
I think you mixed up B and C in your example, but
Charlie Harrison
2016/08/08 14:04:39
Done.
| |
| 188 if (frame_size->content_left <= 0) | |
| 189 frames_over_limit->insert(it.first); | |
| 190 } | |
| 191 if (!frames_over_limit->empty()) { | |
| 192 BrowserThread::PostTask( | |
| 193 BrowserThread::IO, FROM_HERE, | |
| 194 base::Bind(&LoaderIOThreadNotifier::NotifyFramesWentOverSizeLimits, | |
| 195 base::Passed(std::move(frames_over_limit)))); | |
| 196 } | |
| 197 } | |
| 198 | |
| 199 // static | |
| 200 void LoaderIOThreadNotifier::NotifyFramesWentOverSizeLimits( | |
| 201 std::unique_ptr<std::set<GlobalFrameRoutingId>> frames) { | |
| 202 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
| 203 ResourceDispatcherHostImpl* rdh = ResourceDispatcherHostImpl::Get(); | |
| 204 if (rdh && rdh->content_size_manager()) { | |
| 205 rdh->content_size_manager()->FramesWentOverSizeLimits(std::move(frames)); | |
| 206 } | |
| 207 } | |
| 208 | |
| 209 // static | |
| 210 void LoaderIOThreadNotifier::NotifyFrameHasLimit( | |
| 211 const GlobalFrameRoutingId& frame, | |
| 212 int limit) { | |
| 213 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
| 214 ResourceDispatcherHostImpl* rdh = ResourceDispatcherHostImpl::Get(); | |
| 215 if (rdh && rdh->content_size_manager()) { | |
| 216 rdh->content_size_manager()->FrameHasSizeLimit(frame, limit); | |
| 217 } | |
| 218 } | |
| 219 | |
| 38 } // namespace content | 220 } // namespace content |
| OLD | NEW |