Chromium Code Reviews| Index: content/browser/loader/loader_io_thread_notifier.cc |
| diff --git a/content/browser/loader/loader_io_thread_notifier.cc b/content/browser/loader/loader_io_thread_notifier.cc |
| index a10537c9d3d342714e9245ce6628915cf3343176..ddebb98da55dc39d82f93c82a6f6dcb845fb2bc8 100644 |
| --- a/content/browser/loader/loader_io_thread_notifier.cc |
| +++ b/content/browser/loader/loader_io_thread_notifier.cc |
| @@ -5,9 +5,13 @@ |
| #include "content/browser/loader/loader_io_thread_notifier.h" |
| #include "content/browser/frame_host/render_frame_host_impl.h" |
| -#include "content/browser/loader/global_routing_id.h" |
| +#include "content/browser/loader/content_size_resource_handler.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 { |
| @@ -19,6 +23,24 @@ void NotifyRenderFrameDeletedOnIO(const GlobalFrameRoutingId& id) { |
| rdhi->OnRenderFrameDeleted(id); |
| } |
| +class ContentSizeNavigationThrottle : public NavigationThrottle { |
| + public: |
| + ContentSizeNavigationThrottle(NavigationHandle* handle, |
| + LoaderIOThreadNotifier* notifier) |
| + : NavigationThrottle(handle), notifier_(notifier) {} |
| + ~ContentSizeNavigationThrottle() override {} |
| + |
| + // NavigationThrottle: |
| + NavigationThrottle::ThrottleCheckResult WillProcessResponse() override { |
| + return notifier_->WillProcessNavigationResponse(navigation_handle()); |
| + } |
| + |
| + private: |
| + LoaderIOThreadNotifier* notifier_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(ContentSizeNavigationThrottle); |
| +}; |
| + |
| } // namespace |
| LoaderIOThreadNotifier::LoaderIOThreadNotifier(WebContents* web_contents) |
| @@ -35,4 +57,164 @@ void LoaderIOThreadNotifier::RenderFrameDeleted( |
| ->GetGlobalFrameRoutingId())); |
| } |
| +// static |
| +NavigationThrottle* LoaderIOThreadNotifier::MaybeCreateThrottle( |
| + NavigationHandle* handle) { |
| + 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
|
| + return nullptr; |
| + if (!base::CommandLine::ForCurrentProcess()->HasSwitch( |
| + switches::kContentSizePolicy)) { |
| + return nullptr; |
| + } |
| + |
| + LoaderIOThreadNotifier* notifier = |
| + static_cast<WebContentsImpl*>(handle->GetWebContents()) |
| + ->loader_io_thread_notifier(); |
| + return new ContentSizeNavigationThrottle(handle, notifier); |
| +} |
| + |
| +// TODO(csharrison): This should be moved to ReadyToCommitNavigation once that |
| +// is available. This callback is incorrect for transfer navigations. |
| +NavigationThrottle::ThrottleCheckResult |
| +LoaderIOThreadNotifier::WillProcessNavigationResponse( |
| + 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
|
| + const GURL& url = navigation_handle->GetURL(); |
| + // 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
|
| + if (!url.SchemeIsHTTPOrHTTPS() && |
| + url.spec().compare(url::kAboutBlankURL) != 0) { |
| + return NavigationThrottle::PROCEED; |
| + } |
| + |
| + if (web_contents()->GetLastCommittedURL().SchemeIs("chrome")) |
| + return NavigationThrottle::PROCEED; |
| + |
| + // TODO(csharrison): Navigations to about:blank are not counted as |
| + // same-origin. |
| + if (url::Origin(web_contents()->GetLastCommittedURL()) |
| + .IsSameOriginWith(url::Origin(url))) { |
| + return NavigationThrottle::PROCEED; |
| + } |
| + |
| + std::string policy_string = |
| + base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII( |
| + 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
|
| + int content_size = 0; |
| + 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
|
| + |
| + 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(&LoaderIOThreadNotifier::NotifyFrameHasLimit, |
| + rfh->GetGlobalFrameRoutingId(), frame_size->content_left)); |
| + } |
| + return frame_size->content_left <= 0 ? NavigationThrottle::CANCEL |
| + : NavigationThrottle::PROCEED; |
| +} |
| + |
| +void LoaderIOThreadNotifier::RenderFrameCreated( |
| + 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.
|
| +} |
| + |
| +// static |
| +// This method iterates through all LoaderIOThreadNotifiers, and calls |
| +// UpdateDataAccounting on them with updates from the IO thread. |
| +void LoaderIOThreadNotifier::UpdateGlobalDataAccounting( |
| + std::unique_ptr<std::map<GlobalFrameRoutingId, int>> updates) { |
| + DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| + DCHECK(updates); |
| + std::map<LoaderIOThreadNotifier*, std::map<GlobalFrameRoutingId, int>> |
| + 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); |
|
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
|
| + if (!contents) |
| + continue; |
| + LoaderIOThreadNotifier* notifier = |
| + static_cast<WebContentsImpl*>(contents)->loader_io_thread_notifier(); |
| + 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.
|
| + } |
| + for (const auto& it : per_notifier_map) { |
| + it.first->UpdateDataAccounting(it.second); |
| + } |
| +} |
| + |
| +LoaderIOThreadNotifier::FrameSize* LoaderIOThreadNotifier::AddNewFrame( |
| + FrameTreeNode* node, |
| + int limit) { |
| + FrameSize* frame_size = GetEffectiveLimit(node); |
| + // No need to add this to the map if a parent frame has a lower limit. |
| + if (!frame_size || frame_size->content_left > limit) { |
| + 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
|
| + size->content_limit = limit; |
| + size->content_left = limit; |
| + return size; |
| + } |
| + return frame_size; |
| +} |
| + |
| +// This method walks the frame tree and returns the FrameSize of the frame with |
| +// the lowest limit. This is the effective bytes limit of |node|. |
| +LoaderIOThreadNotifier::FrameSize* |
| +LoaderIOThreadNotifier::GetEffectiveLimit(FrameTreeNode* node) { |
| + FrameSize* frame_size = nullptr; |
| + FrameTreeNode* current_node = node; |
| + while (current_node) { |
| + 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() && |
| + (!frame_size || |
| + size_it->second.content_left < frame_size->content_left)) { |
| + frame_size = &size_it->second; |
| + } |
| + current_node = current_node->parent(); |
| + } |
| + return frame_size; |
| +} |
| + |
| +void LoaderIOThreadNotifier::UpdateDataAccounting( |
| + std::map<GlobalFrameRoutingId, int> 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)); |
| + FrameSize* frame_size = GetEffectiveLimit(rfh->frame_tree_node()); |
| + DCHECK(frame_size); |
| + 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.
|
| + if (frame_size->content_left <= 0) |
| + frames_over_limit->insert(it.first); |
| + } |
| + if (!frames_over_limit->empty()) { |
| + BrowserThread::PostTask( |
| + BrowserThread::IO, FROM_HERE, |
| + base::Bind(&LoaderIOThreadNotifier::NotifyFramesWentOverSizeLimits, |
| + base::Passed(std::move(frames_over_limit)))); |
| + } |
| +} |
| + |
| +// static |
| +void LoaderIOThreadNotifier::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 LoaderIOThreadNotifier::NotifyFrameHasLimit( |
| + const GlobalFrameRoutingId& frame, |
| + int 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 |