Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(368)

Side by Side Diff: content/browser/service_worker/service_worker_version.cc

Issue 962543005: Service Worker: Add metrics and timeout for starting a Service Worker. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: review Created 5 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2013 The Chromium Authors. All rights reserved. 1 // Copyright 2013 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/service_worker/service_worker_version.h" 5 #include "content/browser/service_worker/service_worker_version.h"
6 6
7 #include "base/command_line.h" 7 #include "base/command_line.h"
8 #include "base/memory/ref_counted.h" 8 #include "base/memory/ref_counted.h"
9 #include "base/metrics/histogram.h"
rkaplow 2015/03/03 15:58:41 histogram_macros.h
falken 2015/03/04 04:16:34 Done.
9 #include "base/stl_util.h" 10 #include "base/stl_util.h"
10 #include "base/strings/string16.h" 11 #include "base/strings/string16.h"
11 #include "base/strings/utf_string_conversions.h" 12 #include "base/strings/utf_string_conversions.h"
12 #include "base/time/time.h" 13 #include "base/time/time.h"
13 #include "content/browser/message_port_message_filter.h" 14 #include "content/browser/message_port_message_filter.h"
14 #include "content/browser/message_port_service.h" 15 #include "content/browser/message_port_service.h"
15 #include "content/browser/service_worker/embedded_worker_instance.h" 16 #include "content/browser/service_worker/embedded_worker_instance.h"
16 #include "content/browser/service_worker/embedded_worker_registry.h" 17 #include "content/browser/service_worker/embedded_worker_registry.h"
17 #include "content/browser/service_worker/service_worker_context_core.h" 18 #include "content/browser/service_worker/service_worker_context_core.h"
18 #include "content/browser/service_worker/service_worker_context_wrapper.h" 19 #include "content/browser/service_worker/service_worker_context_wrapper.h"
(...skipping 10 matching lines...) Expand all
29 #include "content/public/browser/web_contents_observer.h" 30 #include "content/public/browser/web_contents_observer.h"
30 #include "content/public/common/child_process_host.h" 31 #include "content/public/common/child_process_host.h"
31 #include "content/public/common/content_client.h" 32 #include "content/public/common/content_client.h"
32 #include "content/public/common/content_switches.h" 33 #include "content/public/common/content_switches.h"
33 #include "content/public/common/result_codes.h" 34 #include "content/public/common/result_codes.h"
34 #include "net/http/http_response_info.h" 35 #include "net/http/http_response_info.h"
35 36
36 namespace content { 37 namespace content {
37 38
38 typedef ServiceWorkerVersion::StatusCallback StatusCallback; 39 typedef ServiceWorkerVersion::StatusCallback StatusCallback;
39 typedef ServiceWorkerVersion::MessageCallback MessageCallback;
40 40
41 class ServiceWorkerVersion::GetClientDocumentsCallback 41 class ServiceWorkerVersion::GetClientDocumentsCallback
42 : public base::RefCounted<GetClientDocumentsCallback> { 42 : public base::RefCounted<GetClientDocumentsCallback> {
43 public: 43 public:
44 GetClientDocumentsCallback(int request_id, 44 GetClientDocumentsCallback(int request_id,
45 ServiceWorkerVersion* version) 45 ServiceWorkerVersion* version)
46 : request_id_(request_id), 46 : request_id_(request_id),
47 version_(version) { 47 version_(version) {
48 DCHECK(version_); 48 DCHECK(version_);
49 } 49 }
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after
84 84
85 // Default delay for scheduled update. 85 // Default delay for scheduled update.
86 const int kUpdateDelaySeconds = 1; 86 const int kUpdateDelaySeconds = 1;
87 87
88 // Delay between sending pings to the worker. 88 // Delay between sending pings to the worker.
89 const int kPingIntervalTime = 10; // 10 secs. 89 const int kPingIntervalTime = 10; // 10 secs.
90 90
91 // Timeout for waiting for a response to a ping. 91 // Timeout for waiting for a response to a ping.
92 const int kPingTimeoutTime = 30; // 30 secs. 92 const int kPingTimeoutTime = 30; // 30 secs.
93 93
94 // Timeout for the worker to start.
95 const int kStartWorkerTimeoutMinutes = 5;
96
97 // If the SW was destructed while starting up, how many seconds it
98 // had to start up for this to be considered a timeout occurrence.
99 const int kDestructedStartingWorkerTimeoutThreshold = 5; // 5 secs.
rkaplow 2015/03/03 15:58:41 add Sec in const name?
falken 2015/03/04 04:16:34 Done. I feel like there's a more pithy name possib
100
94 const char kClaimClientsStateErrorMesage[] = 101 const char kClaimClientsStateErrorMesage[] =
95 "Only the active worker can claim clients."; 102 "Only the active worker can claim clients.";
96 103
97 const char kClaimClientsShutdownErrorMesage[] = 104 const char kClaimClientsShutdownErrorMesage[] =
98 "Failed to claim clients due to Service Worker system shutdown."; 105 "Failed to claim clients due to Service Worker system shutdown.";
99 106
100 void RunSoon(const base::Closure& callback) { 107 void RunSoon(const base::Closure& callback) {
101 if (!callback.is_null()) 108 if (!callback.is_null())
102 base::MessageLoop::current()->PostTask(FROM_HERE, callback); 109 base::MessageLoop::current()->PostTask(FROM_HERE, callback);
103 } 110 }
(...skipping 196 matching lines...) Expand 10 before | Expand all | Expand 10 after
300 if (registration) { 307 if (registration) {
301 registration_id_ = registration->id(); 308 registration_id_ = registration->id();
302 scope_ = registration->pattern(); 309 scope_ = registration->pattern();
303 } 310 }
304 context_->AddLiveVersion(this); 311 context_->AddLiveVersion(this);
305 embedded_worker_ = context_->embedded_worker_registry()->CreateWorker(); 312 embedded_worker_ = context_->embedded_worker_registry()->CreateWorker();
306 embedded_worker_->AddListener(this); 313 embedded_worker_->AddListener(this);
307 } 314 }
308 315
309 ServiceWorkerVersion::~ServiceWorkerVersion() { 316 ServiceWorkerVersion::~ServiceWorkerVersion() {
317 // The user may have closed the tab waiting for SW to start up.
318 if (start_worker_timeout_timer_.IsRunning() && !start_timing_.is_null()) {
319 if ((base::TimeTicks::Now() - start_timing_) >
320 base::TimeDelta::FromSeconds(
321 kDestructedStartingWorkerTimeoutThreshold)) {
322 RecordStartWorkerResult(SERVICE_WORKER_ERROR_TIMEOUT);
323 }
324 }
325
310 embedded_worker_->RemoveListener(this); 326 embedded_worker_->RemoveListener(this);
311 if (context_) 327 if (context_)
312 context_->RemoveLiveVersion(version_id_); 328 context_->RemoveLiveVersion(version_id_);
313 // EmbeddedWorker's dtor sends StopWorker if it's still running. 329 // EmbeddedWorker's dtor sends StopWorker if it's still running.
314 } 330 }
315 331
316 void ServiceWorkerVersion::SetStatus(Status status) { 332 void ServiceWorkerVersion::SetStatus(Status status) {
317 if (status_ == status) 333 if (status_ == status)
318 return; 334 return;
319 335
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after
361 RunSoon(base::Bind(callback, SERVICE_WORKER_ERROR_START_WORKER_FAILED)); 377 RunSoon(base::Bind(callback, SERVICE_WORKER_ERROR_START_WORKER_FAILED));
362 return; 378 return;
363 } 379 }
364 switch (running_status()) { 380 switch (running_status()) {
365 case RUNNING: 381 case RUNNING:
366 RunSoon(base::Bind(callback, SERVICE_WORKER_OK)); 382 RunSoon(base::Bind(callback, SERVICE_WORKER_OK));
367 return; 383 return;
368 case STOPPING: 384 case STOPPING:
369 case STOPPED: 385 case STOPPED:
370 case STARTING: 386 case STARTING:
387 if (!start_worker_timeout_timer_.IsRunning())
388 ScheduleStartWorkerTimeout();
371 start_callbacks_.push_back(callback); 389 start_callbacks_.push_back(callback);
372 if (running_status() == STOPPED) { 390 if (running_status() == STOPPED) {
373 DCHECK(!cache_listener_.get()); 391 DCHECK(!cache_listener_.get());
374 cache_listener_.reset(new ServiceWorkerCacheListener(this, context_)); 392 cache_listener_.reset(new ServiceWorkerCacheListener(this, context_));
375 embedded_worker_->Start( 393 embedded_worker_->Start(
376 version_id_, 394 version_id_, scope_, script_url_, pause_after_download,
377 scope_, 395 base::Bind(&ServiceWorkerVersion::OnStartSentAndScriptEvaluated,
378 script_url_,
379 pause_after_download,
380 base::Bind(&ServiceWorkerVersion::OnStartMessageSent,
381 weak_factory_.GetWeakPtr())); 396 weak_factory_.GetWeakPtr()));
382 } 397 }
383 return; 398 return;
384 } 399 }
385 } 400 }
386 401
387 void ServiceWorkerVersion::StopWorker(const StatusCallback& callback) { 402 void ServiceWorkerVersion::StopWorker(const StatusCallback& callback) {
388 if (running_status() == STOPPED) { 403 if (running_status() == STOPPED) {
389 RunSoon(base::Bind(callback, SERVICE_WORKER_OK)); 404 RunSoon(base::Bind(callback, SERVICE_WORKER_OK));
390 return; 405 return;
(...skipping 388 matching lines...) Expand 10 before | Expand all | Expand 10 after
779 void ServiceWorkerVersion::Doom() { 794 void ServiceWorkerVersion::Doom() {
780 if (is_doomed_) 795 if (is_doomed_)
781 return; 796 return;
782 is_doomed_ = true; 797 is_doomed_ = true;
783 if (!HasControllee()) 798 if (!HasControllee())
784 DoomInternal(); 799 DoomInternal();
785 } 800 }
786 801
787 void ServiceWorkerVersion::SetDevToolsAttached(bool attached) { 802 void ServiceWorkerVersion::SetDevToolsAttached(bool attached) {
788 embedded_worker()->set_devtools_attached(attached); 803 embedded_worker()->set_devtools_attached(attached);
789 if (attached) 804 if (attached) {
805 // Set to null time so we don't record the startup time metric.
806 start_timing_ = base::TimeTicks();
790 return; 807 return;
808 }
791 // If devtools is detached try scheduling the timers for stopping the worker 809 // If devtools is detached try scheduling the timers for stopping the worker
792 // now. 810 // now.
793 if (!stop_worker_timer_.IsRunning()) 811 if (!stop_worker_timer_.IsRunning())
794 ScheduleStopWorker(); 812 ScheduleStopWorker();
795 if (!ping_worker_timer_.IsRunning()) 813 if (!ping_worker_timer_.IsRunning())
796 StartPingWorker(); 814 StartPingWorker();
815 if (!start_worker_timeout_timer_.IsRunning() && !start_callbacks_.empty())
816 ScheduleStartWorkerTimeout();
797 } 817 }
798 818
799 void ServiceWorkerVersion::SetMainScriptHttpResponseInfo( 819 void ServiceWorkerVersion::SetMainScriptHttpResponseInfo(
800 const net::HttpResponseInfo& http_info) { 820 const net::HttpResponseInfo& http_info) {
801 main_script_http_info_.reset(new net::HttpResponseInfo(http_info)); 821 main_script_http_info_.reset(new net::HttpResponseInfo(http_info));
802 } 822 }
803 823
804 const net::HttpResponseInfo* 824 const net::HttpResponseInfo*
805 ServiceWorkerVersion::GetMainScriptHttpResponseInfo() { 825 ServiceWorkerVersion::GetMainScriptHttpResponseInfo() {
806 return main_script_http_info_.get(); 826 return main_script_http_info_.get();
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after
870 890
871 FOR_EACH_OBSERVER(Listener, listeners_, OnWorkerStopped(this)); 891 FOR_EACH_OBSERVER(Listener, listeners_, OnWorkerStopped(this));
872 892
873 // There should be no more communication from/to a stopped worker. Deleting 893 // There should be no more communication from/to a stopped worker. Deleting
874 // the listener prevents any pending completion callbacks from causing 894 // the listener prevents any pending completion callbacks from causing
875 // messages to be sent to the stopped worker. 895 // messages to be sent to the stopped worker.
876 cache_listener_.reset(); 896 cache_listener_.reset();
877 897
878 // Restart worker if we have any start callbacks and the worker isn't doomed. 898 // Restart worker if we have any start callbacks and the worker isn't doomed.
879 if (should_restart) { 899 if (should_restart) {
900 start_worker_timeout_timer_.Reset();
880 cache_listener_.reset(new ServiceWorkerCacheListener(this, context_)); 901 cache_listener_.reset(new ServiceWorkerCacheListener(this, context_));
881 embedded_worker_->Start( 902 embedded_worker_->Start(
882 version_id_, scope_, script_url_, false /* pause_after_download */, 903 version_id_, scope_, script_url_, false /* pause_after_download */,
883 base::Bind(&ServiceWorkerVersion::OnStartMessageSent, 904 base::Bind(&ServiceWorkerVersion::OnStartSentAndScriptEvaluated,
884 weak_factory_.GetWeakPtr())); 905 weak_factory_.GetWeakPtr()));
885 } 906 }
886 } 907 }
887 908
888 void ServiceWorkerVersion::OnReportException( 909 void ServiceWorkerVersion::OnReportException(
889 const base::string16& error_message, 910 const base::string16& error_message,
890 int line_number, 911 int line_number,
891 int column_number, 912 int column_number,
892 const GURL& source_url) { 913 const GURL& source_url) {
893 FOR_EACH_OBSERVER( 914 FOR_EACH_OBSERVER(
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after
946 IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_SkipWaiting, 967 IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_SkipWaiting,
947 OnSkipWaiting) 968 OnSkipWaiting)
948 IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_ClaimClients, 969 IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_ClaimClients,
949 OnClaimClients) 970 OnClaimClients)
950 IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_Pong, OnPongFromWorker) 971 IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_Pong, OnPongFromWorker)
951 IPC_MESSAGE_UNHANDLED(handled = false) 972 IPC_MESSAGE_UNHANDLED(handled = false)
952 IPC_END_MESSAGE_MAP() 973 IPC_END_MESSAGE_MAP()
953 return handled; 974 return handled;
954 } 975 }
955 976
956 void ServiceWorkerVersion::OnStartMessageSent( 977 void ServiceWorkerVersion::OnStartSentAndScriptEvaluated(
957 ServiceWorkerStatusCode status) { 978 ServiceWorkerStatusCode status) {
958 if (status != SERVICE_WORKER_OK) 979 if (status != SERVICE_WORKER_OK)
959 RunCallbacks(this, &start_callbacks_, status); 980 RunCallbacks(this, &start_callbacks_, status);
960 } 981 }
961 982
962 void ServiceWorkerVersion::DispatchInstallEventAfterStartWorker( 983 void ServiceWorkerVersion::DispatchInstallEventAfterStartWorker(
963 int active_version_id, 984 int active_version_id,
964 const StatusCallback& callback) { 985 const StatusCallback& callback) {
965 DCHECK_EQ(RUNNING, running_status()) 986 DCHECK_EQ(RUNNING, running_status())
966 << "Worker stopped too soon after it was started."; 987 << "Worker stopped too soon after it was started.";
(...skipping 481 matching lines...) Expand 10 before | Expand all | Expand 10 after
1448 base::TimeDelta::FromSeconds(kPingIntervalTime), 1469 base::TimeDelta::FromSeconds(kPingIntervalTime),
1449 base::Bind(&ServiceWorkerVersion::PingWorker, 1470 base::Bind(&ServiceWorkerVersion::PingWorker,
1450 weak_factory_.GetWeakPtr())); 1471 weak_factory_.GetWeakPtr()));
1451 } 1472 }
1452 1473
1453 void ServiceWorkerVersion::OnPingTimeout() { 1474 void ServiceWorkerVersion::OnPingTimeout() {
1454 if (running_status() != STARTING && running_status() != RUNNING) 1475 if (running_status() != STARTING && running_status() != RUNNING)
1455 return; 1476 return;
1456 ping_timed_out_ = true; 1477 ping_timed_out_ = true;
1457 // TODO(falken): Show a message to the developer that the SW was stopped due 1478 // TODO(falken): Show a message to the developer that the SW was stopped due
1458 // to timeout (crbug.com/457968). 1479 // to timeout (crbug.com/457968). Also, change the error code to
1480 // SERVICE_WORKER_ERROR_TIMEOUT.
1459 StopWorkerIfIdle(); 1481 StopWorkerIfIdle();
1460 } 1482 }
1461 1483
1462 void ServiceWorkerVersion::ScheduleStopWorker() { 1484 void ServiceWorkerVersion::ScheduleStopWorker() {
1463 if (running_status() != RUNNING) 1485 if (running_status() != RUNNING)
1464 return; 1486 return;
1465 stop_worker_timer_.Stop(); 1487 stop_worker_timer_.Stop();
1466 stop_worker_timer_.Start( 1488 stop_worker_timer_.Start(
1467 FROM_HERE, base::TimeDelta::FromSeconds( 1489 FROM_HERE, base::TimeDelta::FromSeconds(
1468 is_doomed_ ? kStopDoomedWorkerDelay : kStopWorkerDelay), 1490 is_doomed_ ? kStopDoomedWorkerDelay : kStopWorkerDelay),
(...skipping 23 matching lines...) Expand all
1492 !install_callbacks_.IsEmpty() || 1514 !install_callbacks_.IsEmpty() ||
1493 !fetch_callbacks_.IsEmpty() || 1515 !fetch_callbacks_.IsEmpty() ||
1494 !sync_callbacks_.IsEmpty() || 1516 !sync_callbacks_.IsEmpty() ||
1495 !notification_click_callbacks_.IsEmpty() || 1517 !notification_click_callbacks_.IsEmpty() ||
1496 !push_callbacks_.IsEmpty() || 1518 !push_callbacks_.IsEmpty() ||
1497 !geofencing_callbacks_.IsEmpty() || 1519 !geofencing_callbacks_.IsEmpty() ||
1498 !cross_origin_connect_callbacks_.IsEmpty() || 1520 !cross_origin_connect_callbacks_.IsEmpty() ||
1499 !streaming_url_request_jobs_.empty(); 1521 !streaming_url_request_jobs_.empty();
1500 } 1522 }
1501 1523
1524 void ServiceWorkerVersion::ScheduleStartWorkerTimeout() {
1525 DCHECK(!start_worker_timeout_timer_.IsRunning());
1526 start_timing_ = embedded_worker_->devtools_attached()
1527 ? base::TimeTicks()
1528 : base::TimeTicks::Now();
1529 start_callbacks_.push_back(
1530 base::Bind(&ServiceWorkerVersion::RecordStartWorkerResult,
1531 weak_factory_.GetWeakPtr()));
1532 start_worker_timeout_timer_.Start(
1533 FROM_HERE, base::TimeDelta::FromMinutes(kStartWorkerTimeoutMinutes),
1534 base::Bind(&ServiceWorkerVersion::OnStartWorkerTimeout,
1535 weak_factory_.GetWeakPtr()));
1536 }
1537
1538 void ServiceWorkerVersion::OnStartWorkerTimeout() {
1539 DCHECK(running_status() == STARTING || running_status() == STOPPING)
1540 << running_status();
1541
1542 if (embedded_worker_->devtools_attached()) {
1543 start_worker_timeout_timer_.Stop();
1544 return;
1545 }
1546
1547 scoped_refptr<ServiceWorkerVersion> protect(this);
1548 RunCallbacks(this, &start_callbacks_, SERVICE_WORKER_ERROR_TIMEOUT);
1549 if (running_status() == STARTING)
1550 embedded_worker_->Stop();
1551 }
1552
1553 void ServiceWorkerVersion::RecordStartWorkerResult(
1554 ServiceWorkerStatusCode status) {
1555 start_worker_timeout_timer_.Stop();
1556
1557 UMA_HISTOGRAM_ENUMERATION("ServiceWorkerVersion.StartWorkerStatus", status,
1558 SERVICE_WORKER_ERROR_MAX_VALUE);
1559 if (status == SERVICE_WORKER_OK && !start_timing_.is_null()) {
1560 UMA_HISTOGRAM_MEDIUM_TIMES("ServiceWorkerVersion.StartWorkerTime",
rkaplow 2015/03/03 15:58:41 this histogram will measure up to 3minutes. Consid
falken 2015/03/03 16:23:58 No, I expect it to be very rare that a SW takes 3+
rkaplow 2015/03/03 16:30:15 Then looks good.
1561 base::TimeTicks::Now() - start_timing_);
1562 }
1563
1564 if (status != SERVICE_WORKER_ERROR_TIMEOUT)
1565 return;
1566 EmbeddedWorkerInstance::StartingPhase phase =
1567 EmbeddedWorkerInstance::NOT_STARTING;
1568 EmbeddedWorkerInstance::Status running_status = embedded_worker_->status();
1569 std::string message = "ServiceWorker startup timed out. ";
1570 if (running_status != EmbeddedWorkerInstance::STARTING) {
1571 message.append("The worker had unexpected status: ");
1572 message.append(EmbeddedWorkerInstance::StatusToString(running_status));
1573 } else {
1574 phase = embedded_worker_->starting_phase();
1575 message.append("The worker was in startup phase: ");
1576 message.append(EmbeddedWorkerInstance::StartingPhaseToString(phase));
1577 }
1578 message.append(".");
1579 OnReportException(base::UTF8ToUTF16(message), -1, -1, GURL());
1580 DVLOG(1) << message;
1581 UMA_HISTOGRAM_ENUMERATION("ServiceWorkerVersion.StartWorkerTimeoutPhase",
1582 phase,
1583 EmbeddedWorkerInstance::STARTING_PHASE_MAX_VALUE);
1584 }
1585
1502 void ServiceWorkerVersion::DoomInternal() { 1586 void ServiceWorkerVersion::DoomInternal() {
1503 DCHECK(is_doomed_); 1587 DCHECK(is_doomed_);
1504 DCHECK(!HasControllee()); 1588 DCHECK(!HasControllee());
1505 SetStatus(REDUNDANT); 1589 SetStatus(REDUNDANT);
1506 StopWorkerIfIdle(); 1590 StopWorkerIfIdle();
1507 if (!context_) 1591 if (!context_)
1508 return; 1592 return;
1509 std::vector<ServiceWorkerDatabase::ResourceRecord> resources; 1593 std::vector<ServiceWorkerDatabase::ResourceRecord> resources;
1510 script_cache_map_.GetResources(&resources); 1594 script_cache_map_.GetResources(&resources);
1511 context_->storage()->PurgeResources(resources); 1595 context_->storage()->PurgeResources(resources);
1512 } 1596 }
1513 1597
1514 template <typename IDMAP> 1598 template <typename IDMAP>
1515 void ServiceWorkerVersion::RemoveCallbackAndStopIfDoomed( 1599 void ServiceWorkerVersion::RemoveCallbackAndStopIfDoomed(
1516 IDMAP* callbacks, 1600 IDMAP* callbacks,
1517 int request_id) { 1601 int request_id) {
1518 callbacks->Remove(request_id); 1602 callbacks->Remove(request_id);
1519 if (is_doomed_) { 1603 if (is_doomed_) {
1520 // The stop should be already scheduled, but try to stop immediately, in 1604 // The stop should be already scheduled, but try to stop immediately, in
1521 // order to release worker resources soon. 1605 // order to release worker resources soon.
1522 StopWorkerIfIdle(); 1606 StopWorkerIfIdle();
1523 } 1607 }
1524 } 1608 }
1525 1609
1526 } // namespace content 1610 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698