Add ICU message format support
[chromium-blink-merge.git] / chrome / browser / task_manager / web_contents_resource_provider.cc
blob3e2c1ac4ae0eabd1065528730b81f7663b9af84a
1 // Copyright 2014 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.
5 #include "chrome/browser/task_manager/web_contents_resource_provider.h"
7 #include "base/bind.h"
8 #include "base/bind_helpers.h"
9 #include "base/strings/utf_string_conversions.h"
10 #include "chrome/browser/browser_process.h"
11 #include "chrome/browser/prerender/prerender_manager.h"
12 #include "chrome/browser/prerender/prerender_manager_factory.h"
13 #include "chrome/browser/profiles/profile.h"
14 #include "chrome/browser/profiles/profile_manager.h"
15 #include "chrome/browser/task_manager/renderer_resource.h"
16 #include "chrome/browser/task_manager/task_manager.h"
17 #include "chrome/browser/task_manager/task_manager_util.h"
18 #include "chrome/browser/task_manager/web_contents_information.h"
19 #include "chrome/grit/generated_resources.h"
20 #include "content/public/browser/render_frame_host.h"
21 #include "content/public/browser/render_process_host.h"
22 #include "content/public/browser/render_process_host_observer.h"
23 #include "content/public/browser/render_view_host.h"
24 #include "content/public/browser/render_widget_host_iterator.h"
25 #include "content/public/browser/site_instance.h"
26 #include "content/public/browser/web_contents.h"
27 #include "content/public/browser/web_contents_observer.h"
28 #include "ui/base/l10n/l10n_util.h"
29 #include "ui/gfx/image/image_skia.h"
31 using content::RenderFrameHost;
32 using content::RenderProcessHost;
33 using content::RenderViewHost;
34 using content::SiteInstance;
35 using content::WebContents;
37 namespace task_manager {
39 // A resource for a process hosting out-of-process iframes.
40 class SubframeResource : public RendererResource {
41 public:
42 explicit SubframeResource(WebContents* web_contents,
43 SiteInstance* site_instance,
44 RenderFrameHost* example_rfh);
45 ~SubframeResource() override {}
47 // Resource methods:
48 Type GetType() const override;
49 base::string16 GetTitle() const override;
50 gfx::ImageSkia GetIcon() const override;
51 WebContents* GetWebContents() const override;
53 private:
54 WebContents* web_contents_;
55 base::string16 title_;
56 DISALLOW_COPY_AND_ASSIGN(SubframeResource);
59 SubframeResource::SubframeResource(WebContents* web_contents,
60 SiteInstance* subframe_site_instance,
61 RenderFrameHost* example_rfh)
62 : RendererResource(subframe_site_instance->GetProcess()->GetHandle(),
63 example_rfh->GetRenderViewHost()),
64 web_contents_(web_contents) {
65 int message_id = subframe_site_instance->GetBrowserContext()->IsOffTheRecord()
66 ? IDS_TASK_MANAGER_SUBFRAME_INCOGNITO_PREFIX
67 : IDS_TASK_MANAGER_SUBFRAME_PREFIX;
68 title_ = l10n_util::GetStringFUTF16(
69 message_id,
70 base::UTF8ToUTF16(subframe_site_instance->GetSiteURL().spec()));
73 Resource::Type SubframeResource::GetType() const {
74 return RENDERER;
77 base::string16 SubframeResource::GetTitle() const {
78 return title_;
81 gfx::ImageSkia SubframeResource::GetIcon() const {
82 return gfx::ImageSkia();
85 WebContents* SubframeResource::GetWebContents() const {
86 return web_contents_;
89 // Tracks changes to one WebContents, and manages task manager resources for
90 // that WebContents, on behalf of a WebContentsResourceProvider.
91 class TaskManagerWebContentsEntry : public content::WebContentsObserver,
92 public content::RenderProcessHostObserver {
93 public:
94 typedef std::multimap<SiteInstance*, RendererResource*> ResourceMap;
95 typedef std::pair<ResourceMap::iterator, ResourceMap::iterator> ResourceRange;
97 TaskManagerWebContentsEntry(WebContents* web_contents,
98 WebContentsResourceProvider* provider)
99 : content::WebContentsObserver(web_contents),
100 provider_(provider),
101 main_frame_site_instance_(NULL) {}
103 ~TaskManagerWebContentsEntry() override { ClearAllResources(false); }
105 // content::WebContentsObserver implementation.
106 void RenderFrameDeleted(RenderFrameHost* render_frame_host) override {
107 ClearResourceForFrame(render_frame_host);
110 void RenderFrameHostChanged(RenderFrameHost* old_host,
111 RenderFrameHost* new_host) override {
112 if (old_host)
113 ClearResourceForFrame(old_host);
114 CreateResourceForFrame(new_host);
117 void RenderViewReady() override {
118 ClearAllResources(true);
119 CreateAllResources();
122 void WebContentsDestroyed() override {
123 ClearAllResources(true);
124 provider_->DeleteEntry(web_contents(), this); // Deletes |this|.
127 // content::RenderProcessHostObserver implementation.
128 void RenderProcessExited(RenderProcessHost* process_host,
129 base::TerminationStatus status,
130 int exit_code) override {
131 ClearResourcesForProcess(process_host);
134 void RenderProcessHostDestroyed(RenderProcessHost* process_host) override {
135 tracked_process_hosts_.erase(process_host);
138 // Called by WebContentsResourceProvider.
139 RendererResource* GetResourceForSiteInstance(SiteInstance* site_instance) {
140 ResourceMap::iterator i = resources_by_site_instance_.find(site_instance);
141 if (i == resources_by_site_instance_.end())
142 return NULL;
143 return i->second;
146 void CreateAllResources() {
147 // We'll show one row per SiteInstance in the task manager.
148 DCHECK(web_contents()->GetMainFrame() != NULL);
149 web_contents()->ForEachFrame(
150 base::Bind(&TaskManagerWebContentsEntry::CreateResourceForFrame,
151 base::Unretained(this)));
154 void ClearAllResources(bool update_task_manager) {
155 RendererResource* last_resource = NULL;
156 for (auto& x : resources_by_site_instance_) {
157 RendererResource* resource = x.second;
158 if (resource == last_resource)
159 continue; // Skip multiset duplicates.
160 if (update_task_manager)
161 task_manager()->RemoveResource(resource);
162 delete resource;
163 last_resource = resource;
165 resources_by_site_instance_.clear();
167 RenderProcessHost* last_process_host = NULL;
168 for (RenderProcessHost* process_host : tracked_process_hosts_) {
169 if (last_process_host == process_host)
170 continue; // Skip multiset duplicates.
171 process_host->RemoveObserver(this);
172 last_process_host = process_host;
174 tracked_process_hosts_.clear();
175 tracked_frame_hosts_.clear();
178 void ClearResourceForFrame(RenderFrameHost* render_frame_host) {
179 SiteInstance* site_instance = render_frame_host->GetSiteInstance();
180 std::set<RenderFrameHost*>::iterator frame_set_iterator =
181 tracked_frame_hosts_.find(render_frame_host);
182 if (frame_set_iterator == tracked_frame_hosts_.end()) {
183 // We weren't tracking this RenderFrameHost.
184 return;
186 tracked_frame_hosts_.erase(frame_set_iterator);
187 ResourceRange resource_range =
188 resources_by_site_instance_.equal_range(site_instance);
189 if (resource_range.first == resource_range.second) {
190 NOTREACHED();
191 return;
193 RendererResource* resource = resource_range.first->second;
194 resources_by_site_instance_.erase(resource_range.first++);
195 if (resource_range.first == resource_range.second) {
196 // The removed entry was the sole remaining reference to that resource, so
197 // actually destroy it.
198 task_manager()->RemoveResource(resource);
199 delete resource;
200 DecrementProcessWatch(site_instance->GetProcess());
201 if (site_instance == main_frame_site_instance_) {
202 main_frame_site_instance_ = NULL;
207 void ClearResourcesForProcess(RenderProcessHost* crashed_process) {
208 std::vector<RenderFrameHost*> frame_hosts_to_delete;
209 for (RenderFrameHost* frame_host : tracked_frame_hosts_) {
210 if (frame_host->GetProcess() == crashed_process) {
211 frame_hosts_to_delete.push_back(frame_host);
214 for (RenderFrameHost* frame_host : frame_hosts_to_delete) {
215 ClearResourceForFrame(frame_host);
219 void CreateResourceForFrame(RenderFrameHost* render_frame_host) {
220 SiteInstance* site_instance = render_frame_host->GetSiteInstance();
222 DCHECK_EQ(0u, tracked_frame_hosts_.count(render_frame_host));
224 if (!site_instance->GetProcess()->HasConnection())
225 return;
227 tracked_frame_hosts_.insert(render_frame_host);
229 ResourceRange existing_resource_range =
230 resources_by_site_instance_.equal_range(site_instance);
231 bool existing_resource =
232 (existing_resource_range.first != existing_resource_range.second);
233 bool is_main_frame = (render_frame_host == web_contents()->GetMainFrame());
234 bool site_instance_is_main = (site_instance == main_frame_site_instance_);
235 scoped_ptr<RendererResource> new_resource;
236 if (!existing_resource || (is_main_frame && !site_instance_is_main)) {
237 if (is_main_frame) {
238 new_resource = info()->MakeResource(web_contents());
239 main_frame_site_instance_ = site_instance;
240 } else {
241 new_resource.reset(new SubframeResource(
242 web_contents(), site_instance, render_frame_host));
246 if (existing_resource) {
247 RendererResource* old_resource = existing_resource_range.first->second;
248 if (!new_resource) {
249 resources_by_site_instance_.insert(
250 std::make_pair(site_instance, old_resource));
251 } else {
252 for (ResourceMap::iterator it = existing_resource_range.first;
253 it != existing_resource_range.second;
254 ++it) {
255 it->second = new_resource.get();
257 task_manager()->RemoveResource(old_resource);
258 delete old_resource;
259 DecrementProcessWatch(site_instance->GetProcess());
263 if (new_resource) {
264 task_manager()->AddResource(new_resource.get());
265 resources_by_site_instance_.insert(
266 std::make_pair(site_instance, new_resource.release()));
267 IncrementProcessWatch(site_instance->GetProcess());
271 // Add ourself as an observer of |process|, if we aren't already. Must be
272 // balanced by a call to DecrementProcessWatch().
273 // TODO(nick): Move away from RenderProcessHostObserver once
274 // WebContentsObserver supports per-frame process death notices.
275 void IncrementProcessWatch(RenderProcessHost* process) {
276 auto range = tracked_process_hosts_.equal_range(process);
277 if (range.first == range.second) {
278 process->AddObserver(this);
280 tracked_process_hosts_.insert(range.first, process);
283 void DecrementProcessWatch(RenderProcessHost* process) {
284 auto range = tracked_process_hosts_.equal_range(process);
285 if (range.first == range.second) {
286 NOTREACHED();
287 return;
290 auto element = range.first++;
291 if (range.first == range.second) {
292 process->RemoveObserver(this);
294 tracked_process_hosts_.erase(element, range.first);
297 private:
298 TaskManager* task_manager() { return provider_->task_manager(); }
300 WebContentsInformation* info() { return provider_->info(); }
302 WebContentsResourceProvider* const provider_;
304 // Every RenderFrameHost that we're watching.
305 std::set<RenderFrameHost*> tracked_frame_hosts_;
307 // The set of processes we're currently observing. There is one entry here per
308 // RendererResource we create. A multimap because we may request observation
309 // more than once, say if two resources happen to share a process.
310 std::multiset<RenderProcessHost*> tracked_process_hosts_;
312 // Maps SiteInstances to the RendererResources. A multimap, this contains one
313 // entry per tracked RenderFrameHost, so we can tell when we're done reusing.
314 ResourceMap resources_by_site_instance_;
316 // The site instance of the main frame.
317 SiteInstance* main_frame_site_instance_;
320 ////////////////////////////////////////////////////////////////////////////////
321 // WebContentsResourceProvider class
322 ////////////////////////////////////////////////////////////////////////////////
324 WebContentsResourceProvider::WebContentsResourceProvider(
325 TaskManager* task_manager,
326 scoped_ptr<WebContentsInformation> info)
327 : task_manager_(task_manager), info_(info.Pass()) {
330 WebContentsResourceProvider::~WebContentsResourceProvider() {}
332 RendererResource* WebContentsResourceProvider::GetResource(int origin_pid,
333 int child_id,
334 int route_id) {
335 RenderFrameHost* rfh = RenderFrameHost::FromID(child_id, route_id);
336 WebContents* web_contents = WebContents::FromRenderFrameHost(rfh);
338 // If an origin PID was specified then the request originated in a plugin
339 // working on the WebContents's behalf, so ignore it.
340 if (origin_pid)
341 return NULL;
343 EntryMap::const_iterator web_contents_it = entries_.find(web_contents);
345 if (web_contents_it == entries_.end()) {
346 // Can happen if the tab was closed while a network request was being
347 // performed.
348 return NULL;
351 return web_contents_it->second->GetResourceForSiteInstance(
352 rfh->GetSiteInstance());
355 void WebContentsResourceProvider::StartUpdating() {
356 WebContentsInformation::NewWebContentsCallback new_web_contents_callback =
357 base::Bind(&WebContentsResourceProvider::OnWebContentsCreated, this);
358 info_->GetAll(new_web_contents_callback);
359 info_->StartObservingCreation(new_web_contents_callback);
362 void WebContentsResourceProvider::StopUpdating() {
363 info_->StopObservingCreation();
365 // Delete all entries; this dissassociates them from the WebContents too.
366 STLDeleteValues(&entries_);
369 void WebContentsResourceProvider::OnWebContentsCreated(
370 WebContents* web_contents) {
371 // Don't add dead tabs or tabs that haven't yet connected.
372 if (!web_contents->GetRenderProcessHost()->GetHandle() ||
373 !web_contents->WillNotifyDisconnection()) {
374 return;
377 DCHECK(info_->CheckOwnership(web_contents));
378 if (entries_.count(web_contents)) {
379 // The case may happen that we have added a WebContents as part of the
380 // iteration performed during StartUpdating() call but the notification that
381 // it has connected was not fired yet. So when the notification happens, we
382 // are already observing this WebContents and just ignore it.
383 return;
385 scoped_ptr<TaskManagerWebContentsEntry> entry(
386 new TaskManagerWebContentsEntry(web_contents, this));
387 entry->CreateAllResources();
388 entries_[web_contents] = entry.release();
391 void WebContentsResourceProvider::DeleteEntry(
392 WebContents* web_contents,
393 TaskManagerWebContentsEntry* entry) {
394 if (!entries_.erase(web_contents)) {
395 NOTREACHED();
396 return;
398 delete entry; // Typically, this is our caller. Deletion is okay.
401 } // namespace task_manager