summaryrefslogtreecommitdiffstats
path: root/chromium/chrome/browser/extensions/api/processes/processes_api.cc
blob: 30d45fe2144782307eb3e667f6fc3c08156ad80a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
// Copyright (c) 2012 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 "chrome/browser/extensions/api/processes/processes_api.h"

#include <stdint.h>

#include <algorithm>
#include <memory>
#include <set>
#include <utility>

#include "base/bind.h"
#include "base/lazy_instance.h"
#include "base/metrics/histogram_macros.h"
#include "base/process/process.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "base/task/post_task.h"
#include "chrome/browser/extensions/api/tabs/tabs_constants.h"
#include "chrome/browser/extensions/extension_tab_util.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/task_manager/task_manager_interface.h"
#include "chrome/common/extensions/api/processes.h"
#include "content/public/browser/browser_child_process_host.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/child_process_data.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/child_process_host.h"
#include "content/public/common/result_codes.h"
#include "extensions/common/error_utils.h"

namespace extensions {

namespace errors {
const char kNotAllowedToTerminate[] = "Not allowed to terminate process: *.";
const char kProcessNotFound[] = "Process not found: *.";
const char kInvalidArgument[] = "Invalid argument: *.";
}  // namespace errors

namespace {

base::LazyInstance<BrowserContextKeyedAPIFactory<ProcessesAPI>>::
    DestructorAtExit g_processes_api_factory = LAZY_INSTANCE_INITIALIZER;

int64_t GetRefreshTypesFlagOnlyEssentialData() {
  // This is the only non-optional data in the Process as defined by the API in
  // processes.idl.
  return task_manager::REFRESH_TYPE_NACL;
}

// This does not include memory. The memory refresh flag will only be added once
// a listener to OnUpdatedWithMemory event is added.
int64_t GetRefreshTypesForProcessOptionalData() {
  return task_manager::REFRESH_TYPE_CPU |
      task_manager::REFRESH_TYPE_NETWORK_USAGE |
      task_manager::REFRESH_TYPE_SQLITE_MEMORY |
      task_manager::REFRESH_TYPE_V8_MEMORY |
      task_manager::REFRESH_TYPE_WEBCACHE_STATS;
}

std::unique_ptr<api::processes::Cache> CreateCacheData(
    const blink::WebCacheResourceTypeStat& stat) {
  std::unique_ptr<api::processes::Cache> cache(new api::processes::Cache());
  cache->size = static_cast<double>(stat.size);
  cache->live_size = static_cast<double>(stat.size);
  return cache;
}

api::processes::ProcessType GetProcessType(
    task_manager::Task::Type task_type) {
  switch (task_type) {
    case task_manager::Task::BROWSER:
      return api::processes::PROCESS_TYPE_BROWSER;

    case task_manager::Task::RENDERER:
      return api::processes::PROCESS_TYPE_RENDERER;

    case task_manager::Task::EXTENSION:
    case task_manager::Task::GUEST:
      return api::processes::PROCESS_TYPE_EXTENSION;

    case task_manager::Task::PLUGIN:
      return api::processes::PROCESS_TYPE_PLUGIN;

    case task_manager::Task::WORKER:
      return api::processes::PROCESS_TYPE_WORKER;

    case task_manager::Task::NACL:
      return api::processes::PROCESS_TYPE_NACL;

    case task_manager::Task::SERVICE_WORKER:
      return api::processes::PROCESS_TYPE_SERVICE_WORKER;

    case task_manager::Task::UTILITY:
      return api::processes::PROCESS_TYPE_UTILITY;

    case task_manager::Task::GPU:
      return api::processes::PROCESS_TYPE_GPU;

    case task_manager::Task::UNKNOWN:
    case task_manager::Task::ARC:
    case task_manager::Task::CROSTINI:
    case task_manager::Task::SANDBOX_HELPER:
    case task_manager::Task::ZYGOTE:
      return api::processes::PROCESS_TYPE_OTHER;
  }

  NOTREACHED() << "Unknown task type.";
  return api::processes::PROCESS_TYPE_NONE;
}

// Fills |out_process| with the data of the process in which the task with |id|
// is running. If |include_optional| is true, this function will fill the
// optional fields in |api::processes::Process| except for |private_memory|,
// which should be filled later if needed.
void FillProcessData(
    task_manager::TaskId id,
    task_manager::TaskManagerInterface* task_manager,
    bool include_optional,
    api::processes::Process* out_process) {
  DCHECK(out_process);

  out_process->id = task_manager->GetChildProcessUniqueId(id);
  out_process->os_process_id = task_manager->GetProcessId(id);
  out_process->type = GetProcessType(task_manager->GetType(id));
  out_process->profile = base::UTF16ToUTF8(task_manager->GetProfileName(id));
  out_process->nacl_debug_port = task_manager->GetNaClDebugStubPort(id);

  // Collect the tab IDs of all the tasks sharing this renderer if any.
  const task_manager::TaskIdList tasks_on_process =
      task_manager->GetIdsOfTasksSharingSameProcess(id);
  for (const auto& task_id : tasks_on_process) {
    api::processes::TaskInfo task_info;
    task_info.title = base::UTF16ToUTF8(task_manager->GetTitle(task_id));
    const SessionID tab_id = task_manager->GetTabId(task_id);
    if (tab_id.is_valid())
      task_info.tab_id.reset(new int(tab_id.id()));

    out_process->tasks.push_back(std::move(task_info));
  }

  // If we don't need to include the optional properties, just return now.
  if (!include_optional)
    return;

  out_process->cpu.reset(
      new double(task_manager->GetPlatformIndependentCPUUsage(id)));
  out_process->network.reset(new double(static_cast<double>(
      task_manager->GetProcessTotalNetworkUsage(id))));

  int64_t v8_allocated = 0;
  int64_t v8_used = 0;
  if (task_manager->GetV8Memory(id, &v8_allocated, &v8_used)) {
    out_process->js_memory_allocated.reset(new double(static_cast<double>(
        v8_allocated)));
    out_process->js_memory_used.reset(new double(static_cast<double>(v8_used)));
  }

  const int64_t sqlite_bytes = task_manager->GetSqliteMemoryUsed(id);
  if (sqlite_bytes != -1) {
    out_process->sqlite_memory.reset(new double(static_cast<double>(
        sqlite_bytes)));
  }

  blink::WebCacheResourceTypeStats cache_stats;
  if (task_manager->GetWebCacheStats(id, &cache_stats)) {
    out_process->image_cache = CreateCacheData(cache_stats.images);
    out_process->script_cache = CreateCacheData(cache_stats.scripts);
    out_process->css_cache = CreateCacheData(cache_stats.css_style_sheets);
  }
}

}  // namespace

////////////////////////////////////////////////////////////////////////////////
// ProcessesEventRouter:
////////////////////////////////////////////////////////////////////////////////

ProcessesEventRouter::ProcessesEventRouter(content::BrowserContext* context)
    : task_manager::TaskManagerObserver(base::TimeDelta::FromSeconds(1),
                                        task_manager::REFRESH_TYPE_NONE),
      browser_context_(context),
      listeners_(0) {
}

ProcessesEventRouter::~ProcessesEventRouter() {
}

void ProcessesEventRouter::ListenerAdded() {
  UpdateRefreshTypesFlagsBasedOnListeners();

  if (listeners_++ == 0) {
    // The first listener to be added.
    task_manager::TaskManagerInterface::GetTaskManager()->AddObserver(this);
  }
}

void ProcessesEventRouter::ListenerRemoved() {
  UpdateRefreshTypesFlagsBasedOnListeners();

  if (--listeners_ == 0) {
    // Last listener to be removed.
    task_manager::TaskManagerInterface::GetTaskManager()->RemoveObserver(
        this);
  }
}

void ProcessesEventRouter::OnTaskAdded(task_manager::TaskId id) {
  if (!HasEventListeners(api::processes::OnCreated::kEventName))
    return;

  int child_process_host_id = 0;
  if (!ShouldReportOnCreatedOrOnExited(id, &child_process_host_id))
    return;

  api::processes::Process process;
  FillProcessData(id,
                  observed_task_manager(),
                  false,  // include_optional
                  &process);
  DispatchEvent(events::PROCESSES_ON_CREATED,
                api::processes::OnCreated::kEventName,
                api::processes::OnCreated::Create(process));
}

void ProcessesEventRouter::OnTaskToBeRemoved(task_manager::TaskId id) {
  if (!HasEventListeners(api::processes::OnExited::kEventName))
    return;

  int child_process_host_id = 0;
  if (!ShouldReportOnCreatedOrOnExited(id, &child_process_host_id))
    return;

  int exit_code = 0;
  base::TerminationStatus status = base::TERMINATION_STATUS_STILL_RUNNING;
  observed_task_manager()->GetTerminationStatus(id, &status, &exit_code);

  DispatchEvent(events::PROCESSES_ON_EXITED,
                api::processes::OnExited::kEventName,
                api::processes::OnExited::Create(child_process_host_id,
                                                 status,
                                                 exit_code));
}

void ProcessesEventRouter::OnTasksRefreshedWithBackgroundCalculations(
    const task_manager::TaskIdList& task_ids) {
  const bool has_on_updated_listeners =
      HasEventListeners(api::processes::OnUpdated::kEventName);
  const bool has_on_updated_with_memory_listeners =
      HasEventListeners(api::processes::OnUpdatedWithMemory::kEventName);

  if (!has_on_updated_listeners && !has_on_updated_with_memory_listeners)
    return;

  // Get the data of tasks sharing the same process only once.
  std::set<base::ProcessId> seen_processes;
  base::DictionaryValue processes_dictionary;
  for (const auto& task_id : task_ids) {
    // We are not interested in tasks, but rather the processes on which they
    // run.
    const base::ProcessId proc_id =
        observed_task_manager()->GetProcessId(task_id);
    if (seen_processes.count(proc_id))
      continue;

    const int child_process_host_id =
        observed_task_manager()->GetChildProcessUniqueId(task_id);
    // Ignore tasks that don't have a valid child process host ID like ARC
    // processes. We report the browser process info here though.
    if (child_process_host_id == content::ChildProcessHost::kInvalidUniqueID)
      continue;

    seen_processes.insert(proc_id);
    api::processes::Process process;
    FillProcessData(task_id,
                    observed_task_manager(),
                    true,  // include_optional
                    &process);

    if (has_on_updated_with_memory_listeners) {
      // Append the memory footprint to the process data.
      const int64_t memory_footprint =
          observed_task_manager()->GetMemoryFootprintUsage(task_id);
      process.private_memory =
          std::make_unique<double>(static_cast<double>(memory_footprint));
    }

    // Store each process indexed by the string version of its ChildProcessHost
    // ID.
    processes_dictionary.Set(base::NumberToString(child_process_host_id),
                             process.ToValue());
  }

  // Done with data collection. Now dispatch the appropriate events according to
  // the present listeners.
  DCHECK(has_on_updated_listeners || has_on_updated_with_memory_listeners);
  if (has_on_updated_listeners) {
    api::processes::OnUpdated::Processes processes;
    processes.additional_properties.MergeDictionary(&processes_dictionary);
    // NOTE: If there are listeners to the updates with memory as well,
    // listeners to onUpdated (without memory) will also get the memory info
    // of processes as an added bonus.
    DispatchEvent(events::PROCESSES_ON_UPDATED,
                  api::processes::OnUpdated::kEventName,
                  api::processes::OnUpdated::Create(processes));
  }

  if (has_on_updated_with_memory_listeners) {
    api::processes::OnUpdatedWithMemory::Processes processes;
    processes.additional_properties.MergeDictionary(&processes_dictionary);
    DispatchEvent(events::PROCESSES_ON_UPDATED_WITH_MEMORY,
                  api::processes::OnUpdatedWithMemory::kEventName,
                  api::processes::OnUpdatedWithMemory::Create(processes));
  }
}

void ProcessesEventRouter::OnTaskUnresponsive(task_manager::TaskId id) {
  if (!HasEventListeners(api::processes::OnUnresponsive::kEventName))
    return;

  api::processes::Process process;
  FillProcessData(id,
                  observed_task_manager(),
                  false,  // include_optional
                  &process);
  DispatchEvent(events::PROCESSES_ON_UNRESPONSIVE,
                api::processes::OnUnresponsive::kEventName,
                api::processes::OnUnresponsive::Create(process));
}

void ProcessesEventRouter::DispatchEvent(
    events::HistogramValue histogram_value,
    const std::string& event_name,
    std::unique_ptr<base::ListValue> event_args) const {
  EventRouter* event_router = EventRouter::Get(browser_context_);
  if (event_router) {
    std::unique_ptr<Event> event(
        new Event(histogram_value, event_name, std::move(event_args)));
    event_router->BroadcastEvent(std::move(event));
  }
}

bool ProcessesEventRouter::HasEventListeners(
    const std::string& event_name) const {
  EventRouter* event_router = EventRouter::Get(browser_context_);
  return event_router && event_router->HasEventListener(event_name);
}

bool ProcessesEventRouter::ShouldReportOnCreatedOrOnExited(
    task_manager::TaskId id,
    int* out_child_process_host_id) const {
  // Is it the first task to be created or the last one to be removed?
  if (observed_task_manager()->GetNumberOfTasksOnSameProcess(id) != 1)
    return false;

  // Ignore tasks that don't have a valid child process host ID like ARC
  // processes, as well as the browser process (neither onCreated() nor
  // onExited() shouldn't report the browser process).
  *out_child_process_host_id =
      observed_task_manager()->GetChildProcessUniqueId(id);
  if (*out_child_process_host_id ==
          content::ChildProcessHost::kInvalidUniqueID ||
      *out_child_process_host_id == 0) {
    return false;
  }

  return true;
}

void ProcessesEventRouter::UpdateRefreshTypesFlagsBasedOnListeners() {
  int64_t refresh_types = task_manager::REFRESH_TYPE_NONE;
  if (HasEventListeners(api::processes::OnCreated::kEventName) ||
      HasEventListeners(api::processes::OnUnresponsive::kEventName)) {
    refresh_types |= GetRefreshTypesFlagOnlyEssentialData();
  }

  const int64_t on_updated_types = GetRefreshTypesForProcessOptionalData();
  if (HasEventListeners(api::processes::OnUpdated::kEventName))
    refresh_types |= on_updated_types;

  if (HasEventListeners(api::processes::OnUpdatedWithMemory::kEventName)) {
    refresh_types |=
        (on_updated_types | task_manager::REFRESH_TYPE_MEMORY_FOOTPRINT);
  }

  SetRefreshTypesFlags(refresh_types);
}

////////////////////////////////////////////////////////////////////////////////
// ProcessesAPI:
////////////////////////////////////////////////////////////////////////////////

ProcessesAPI::ProcessesAPI(content::BrowserContext* context)
    : browser_context_(context) {
  EventRouter* event_router = EventRouter::Get(browser_context_);
  // Monitor when the following events are being listened to in order to know
  // when to start the task manager.
  event_router->RegisterObserver(this, api::processes::OnUpdated::kEventName);
  event_router->RegisterObserver(
      this, api::processes::OnUpdatedWithMemory::kEventName);
  event_router->RegisterObserver(this, api::processes::OnCreated::kEventName);
  event_router->RegisterObserver(this,
                                 api::processes::OnUnresponsive::kEventName);
  event_router->RegisterObserver(this, api::processes::OnExited::kEventName);
}

ProcessesAPI::~ProcessesAPI() {
  // This object has already been unregistered as an observer in Shutdown().
}

// static
BrowserContextKeyedAPIFactory<ProcessesAPI>*
ProcessesAPI::GetFactoryInstance() {
  return g_processes_api_factory.Pointer();
}

// static
ProcessesAPI* ProcessesAPI::Get(content::BrowserContext* context) {
  return BrowserContextKeyedAPIFactory<ProcessesAPI>::Get(context);
}

void ProcessesAPI::Shutdown() {
  EventRouter::Get(browser_context_)->UnregisterObserver(this);
}

void ProcessesAPI::OnListenerAdded(const EventListenerInfo& details) {
  // The ProcessesEventRouter will observe the TaskManager as long as there are
  // listeners for the processes.onUpdated/.onUpdatedWithMemory/.onCreated ...
  // etc. events.
  processes_event_router()->ListenerAdded();
}

void ProcessesAPI::OnListenerRemoved(const EventListenerInfo& details) {
  // If a processes.onUpdated/.onUpdatedWithMemory/.onCreated ... etc. event
  // listener is removed (or a process with one exits), then we let the
  // extension API know that it has one fewer listener.
  processes_event_router()->ListenerRemoved();
}

ProcessesEventRouter* ProcessesAPI::processes_event_router() {
  if (!processes_event_router_.get())
    processes_event_router_.reset(new ProcessesEventRouter(browser_context_));
  return processes_event_router_.get();
}

////////////////////////////////////////////////////////////////////////////////
// ProcessesGetProcessIdForTabFunction:
////////////////////////////////////////////////////////////////////////////////

ExtensionFunction::ResponseAction ProcessesGetProcessIdForTabFunction::Run() {
  // For this function, the task manager doesn't even need to be running.
  std::unique_ptr<api::processes::GetProcessIdForTab::Params> params(
      api::processes::GetProcessIdForTab::Params::Create(*args_));
  EXTENSION_FUNCTION_VALIDATE(params.get());

  const int tab_id = params->tab_id;
  content::WebContents* contents = nullptr;
  int tab_index = -1;
  if (!ExtensionTabUtil::GetTabById(
          tab_id, Profile::FromBrowserContext(browser_context()),
          include_incognito_information(), nullptr, nullptr, &contents,
          &tab_index)) {
    return RespondNow(
        Error(tabs_constants::kTabNotFoundError, base::NumberToString(tab_id)));
  }

  // TODO(https://crbug.com/767563): chrome.processes.getProcessIdForTab API
  // incorrectly assumes a *single* renderer process per tab.
  const int process_id = contents->GetMainFrame()->GetProcess()->GetID();
  return RespondNow(ArgumentList(
      api::processes::GetProcessIdForTab::Results::Create(process_id)));
}

////////////////////////////////////////////////////////////////////////////////
// ProcessesTerminateFunction:
////////////////////////////////////////////////////////////////////////////////

ExtensionFunction::ResponseAction ProcessesTerminateFunction::Run() {
  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);

  // For this function, the task manager doesn't even need to be running.
  std::unique_ptr<api::processes::Terminate::Params> params(
      api::processes::Terminate::Params::Create(*args_));
  EXTENSION_FUNCTION_VALIDATE(params.get());

  child_process_host_id_ = params->process_id;
  if (child_process_host_id_ < 0) {
    return RespondNow(Error(errors::kInvalidArgument,
                            base::NumberToString(child_process_host_id_)));
  } else if (child_process_host_id_ == 0) {
    // Cannot kill the browser process.
    return RespondNow(Error(errors::kNotAllowedToTerminate,
                            base::NumberToString(child_process_host_id_)));
  }

  // Check if it's a renderer.
  auto* render_process_host =
      content::RenderProcessHost::FromID(child_process_host_id_);
  if (render_process_host)
    return RespondNow(
        TerminateIfAllowed(render_process_host->GetProcess().Handle()));

  // This could be a non-renderer child process like a plugin or a nacl
  // process. Try to get its handle from the BrowserChildProcessHost on the
  // IO thread.
  base::PostTaskAndReplyWithResult(
      FROM_HERE, {content::BrowserThread::IO},
      base::Bind(&ProcessesTerminateFunction::GetProcessHandleOnIO, this,
                 child_process_host_id_),
      base::Bind(&ProcessesTerminateFunction::OnProcessHandleOnUI, this));

  // Promise to respond later.
  return RespondLater();
}

base::ProcessHandle ProcessesTerminateFunction::GetProcessHandleOnIO(
    int child_process_host_id) const {
  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);

  auto* host = content::BrowserChildProcessHost::FromID(child_process_host_id);
  if (host)
    return host->GetData().GetProcess().Handle();

  return base::kNullProcessHandle;
}

void ProcessesTerminateFunction::OnProcessHandleOnUI(
    base::ProcessHandle handle) {
  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);

  Respond(TerminateIfAllowed(handle));
}

ExtensionFunction::ResponseValue
ProcessesTerminateFunction::TerminateIfAllowed(base::ProcessHandle handle) {
  if (handle == base::kNullProcessHandle) {
    return Error(errors::kProcessNotFound,
                 base::NumberToString(child_process_host_id_));
  }

  if (handle == base::GetCurrentProcessHandle()) {
    // Cannot kill the browser process.
    return Error(errors::kNotAllowedToTerminate,
                 base::NumberToString(child_process_host_id_));
  }

  base::Process process = base::Process::Open(base::GetProcId(handle));
  if (!process.IsValid()) {
    return Error(errors::kProcessNotFound,
                 base::NumberToString(child_process_host_id_));
  }

  const bool did_terminate =
      process.Terminate(content::RESULT_CODE_KILLED, true /* wait */);
  if (did_terminate)
    UMA_HISTOGRAM_COUNTS_1M("ChildProcess.KilledByExtensionAPI", 1);

  return ArgumentList(
      api::processes::Terminate::Results::Create(did_terminate));
}

////////////////////////////////////////////////////////////////////////////////
// ProcessesGetProcessInfoFunction:
////////////////////////////////////////////////////////////////////////////////

ProcessesGetProcessInfoFunction::ProcessesGetProcessInfoFunction()
    : task_manager::TaskManagerObserver(
          base::TimeDelta::FromSeconds(1),
          GetRefreshTypesFlagOnlyEssentialData()) {
}

ExtensionFunction::ResponseAction ProcessesGetProcessInfoFunction::Run() {
  std::unique_ptr<api::processes::GetProcessInfo::Params> params(
      api::processes::GetProcessInfo::Params::Create(*args_));
  EXTENSION_FUNCTION_VALIDATE(params.get());
  if (params->process_ids.as_integer)
    process_host_ids_.push_back(*params->process_ids.as_integer);
  else
    process_host_ids_.swap(*params->process_ids.as_integers);

  include_memory_ = params->include_memory;
  if (include_memory_)
    AddRefreshType(task_manager::REFRESH_TYPE_MEMORY_FOOTPRINT);

  // Keep this object alive until the first of either OnTasksRefreshed() or
  // OnTasksRefreshedWithBackgroundCalculations() is received depending on
  // |include_memory_|.
  AddRef();

  // The task manager needs to be enabled for this function.
  // Start observing the task manager and wait for the next refresh event.
  task_manager::TaskManagerInterface::GetTaskManager()->AddObserver(this);

  return RespondLater();
}

void ProcessesGetProcessInfoFunction::OnTasksRefreshed(
    const task_manager::TaskIdList& task_ids) {
  // Memory is background calculated and will be ready when
  // OnTasksRefreshedWithBackgroundCalculations() is invoked.
  if (include_memory_)
    return;

  GatherDataAndRespond(task_ids);
}

void
ProcessesGetProcessInfoFunction::OnTasksRefreshedWithBackgroundCalculations(
    const task_manager::TaskIdList& task_ids) {
  if (!include_memory_)
    return;

  GatherDataAndRespond(task_ids);
}

ProcessesGetProcessInfoFunction::~ProcessesGetProcessInfoFunction() {}

void ProcessesGetProcessInfoFunction::GatherDataAndRespond(
    const task_manager::TaskIdList& task_ids) {
  // If there are no process IDs specified, it means we need to return all of
  // the ones we know of.
  const bool specific_processes_requested = !process_host_ids_.empty();
  std::set<base::ProcessId> seen_processes;
  // Create the results object as defined in the generated API from process.idl
  // and fill it with the processes info.
  api::processes::GetProcessInfo::Results::Processes processes;
  for (const auto& task_id : task_ids) {
    const base::ProcessId proc_id =
        observed_task_manager()->GetProcessId(task_id);
    if (seen_processes.count(proc_id))
      continue;

    const int child_process_host_id =
        observed_task_manager()->GetChildProcessUniqueId(task_id);
    // Ignore tasks that don't have a valid child process host ID like ARC
    // processes. We report the browser process info here though.
    if (child_process_host_id == content::ChildProcessHost::kInvalidUniqueID)
      continue;

    if (specific_processes_requested) {
      // Note: we can't use |!process_host_ids_.empty()| directly in the above
      // condition as we will erase from |process_host_ids_| below.
      auto itr = std::find(process_host_ids_.begin(),
                           process_host_ids_.end(),
                           child_process_host_id);
      if (itr == process_host_ids_.end())
        continue;

      // If found, we remove it from |process_host_ids|, so that at the end if
      // anything remains in |process_host_ids|, those were invalid arguments
      // that will be reported on the console.
      process_host_ids_.erase(itr);
    }

    seen_processes.insert(proc_id);

    // We do not include the optional data in this function results.
    api::processes::Process process;
    FillProcessData(task_id,
                    observed_task_manager(),
                    false,  // include_optional
                    &process);

    if (include_memory_) {
      // Append the memory footprint to the process data.
      const int64_t memory_footprint =
          observed_task_manager()->GetMemoryFootprintUsage(task_id);
      process.private_memory =
          std::make_unique<double>(static_cast<double>(memory_footprint));
    }

    // Store each process indexed by the string version of its
    // ChildProcessHost ID.
    processes.additional_properties.Set(
        base::NumberToString(child_process_host_id), process.ToValue());
  }

  // Report the invalid host ids sent in the arguments.
  for (const auto& host_id : process_host_ids_) {
    WriteToConsole(
        blink::mojom::ConsoleMessageLevel::kError,
        ErrorUtils::FormatErrorMessage(errors::kProcessNotFound,
                                       base::NumberToString(host_id)));
  }

  // Send the response.
  Respond(ArgumentList(
      api::processes::GetProcessInfo::Results::Create(processes)));

  // Stop observing the task manager, and balance the AddRef() in Run().
  task_manager::TaskManagerInterface::GetTaskManager()->RemoveObserver(this);
  Release();
}

}  // namespace extensions