1 // Copyright (c) 2012 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 "content/browser/loader/resource_scheduler.h"
7 #include "base/memory/scoped_vector.h"
8 #include "base/message_loop/message_loop.h"
9 #include "base/strings/string_number_conversions.h"
10 #include "content/browser/browser_thread_impl.h"
11 #include "content/browser/loader/resource_dispatcher_host_impl.h"
12 #include "content/browser/loader/resource_message_filter.h"
13 #include "content/browser/loader/resource_request_info_impl.h"
14 #include "content/common/resource_messages.h"
15 #include "content/public/browser/resource_context.h"
16 #include "content/public/browser/resource_controller.h"
17 #include "content/public/browser/resource_throttle.h"
18 #include "content/public/common/process_type.h"
19 #include "net/base/host_port_pair.h"
20 #include "net/base/request_priority.h"
21 #include "net/http/http_server_properties_impl.h"
22 #include "net/url_request/url_request.h"
23 #include "net/url_request/url_request_test_util.h"
24 #include "testing/gtest/include/gtest/gtest.h"
25 #include "webkit/common/resource_type.h"
31 class TestRequestFactory
;
33 const int kChildId
= 30;
34 const int kRouteId
= 75;
36 class TestRequest
: public ResourceController
{
38 TestRequest(scoped_ptr
<ResourceThrottle
> throttle
,
39 scoped_ptr
<net::URLRequest
> url_request
)
41 throttle_(throttle
.Pass()),
42 url_request_(url_request
.Pass()) {
43 throttle_
->set_controller_for_testing(this);
46 bool started() const { return started_
; }
49 bool deferred
= false;
50 throttle_
->WillStartRequest(&deferred
);
54 const net::URLRequest
* url_request() const { return url_request_
.get(); }
57 // ResourceController interface:
58 virtual void Cancel() OVERRIDE
{}
59 virtual void CancelAndIgnore() OVERRIDE
{}
60 virtual void CancelWithError(int error_code
) OVERRIDE
{}
61 virtual void Resume() OVERRIDE
{ started_
= true; }
65 scoped_ptr
<ResourceThrottle
> throttle_
;
66 scoped_ptr
<net::URLRequest
> url_request_
;
69 class CancelingTestRequest
: public TestRequest
{
71 CancelingTestRequest(scoped_ptr
<ResourceThrottle
> throttle
,
72 scoped_ptr
<net::URLRequest
> url_request
)
73 : TestRequest(throttle
.Pass(), url_request
.Pass()) {
76 void set_request_to_cancel(scoped_ptr
<TestRequest
> request_to_cancel
) {
77 request_to_cancel_
= request_to_cancel
.Pass();
81 virtual void Resume() OVERRIDE
{
82 TestRequest::Resume();
83 request_to_cancel_
.reset();
86 scoped_ptr
<TestRequest
> request_to_cancel_
;
89 class FakeResourceContext
: public ResourceContext
{
91 virtual net::HostResolver
* GetHostResolver() OVERRIDE
{ return NULL
; }
92 virtual net::URLRequestContext
* GetRequestContext() OVERRIDE
{ return NULL
; }
93 virtual bool AllowMicAccess(const GURL
& origin
) OVERRIDE
{ return false; }
94 virtual bool AllowCameraAccess(const GURL
& origin
) OVERRIDE
{ return false; }
97 class FakeResourceMessageFilter
: public ResourceMessageFilter
{
99 FakeResourceMessageFilter(int child_id
)
100 : ResourceMessageFilter(
102 PROCESS_TYPE_RENDERER
,
103 NULL
/* appcache_service */,
104 NULL
/* blob_storage_context */,
105 NULL
/* file_system_context */,
106 NULL
/* service_worker_context */,
107 base::Bind(&FakeResourceMessageFilter::GetContexts
,
108 base::Unretained(this))) {
112 virtual ~FakeResourceMessageFilter() {}
114 void GetContexts(const ResourceHostMsg_Request
& request
,
115 ResourceContext
** resource_context
,
116 net::URLRequestContext
** request_context
) {
117 *resource_context
= &context_
;
118 *request_context
= NULL
;
121 FakeResourceContext context_
;
124 class ResourceSchedulerTest
: public testing::Test
{
126 ResourceSchedulerTest()
127 : next_request_id_(0),
128 ui_thread_(BrowserThread::UI
, &message_loop_
),
129 io_thread_(BrowserThread::IO
, &message_loop_
) {
130 scheduler_
.OnClientCreated(kChildId
, kRouteId
);
131 context_
.set_http_server_properties(http_server_properties_
.GetWeakPtr());
134 virtual ~ResourceSchedulerTest() {
135 scheduler_
.OnClientDeleted(kChildId
, kRouteId
);
138 scoped_ptr
<net::URLRequest
> NewURLRequestWithRoute(
140 net::RequestPriority priority
,
142 scoped_ptr
<net::URLRequest
> url_request(
143 context_
.CreateRequest(GURL(url
), priority
, NULL
, NULL
));
144 ResourceRequestInfoImpl
* info
= new ResourceRequestInfoImpl(
145 PROCESS_TYPE_RENDERER
, // process_type
146 kChildId
, // child_id
147 route_id
, // route_id
149 ++next_request_id_
, // request_id
150 MSG_ROUTING_NONE
, // render_frame_id
151 false, // is_main_frame
152 false, // parent_is_main_frame
153 0, // parent_render_frame_id
154 ResourceType::SUB_RESOURCE
, // resource_type
155 PAGE_TRANSITION_LINK
, // transition_type
156 false, // should_replace_current_entry
157 false, // is_download
159 true, // allow_download
160 false, // has_user_gesture
161 blink::WebReferrerPolicyDefault
, // referrer_policy
162 blink::WebPageVisibilityStateVisible
, // visibility_state
164 base::WeakPtr
<ResourceMessageFilter
>(), // filter
166 info
->AssociateWithRequest(url_request
.get());
167 return url_request
.Pass();
170 scoped_ptr
<net::URLRequest
> NewURLRequest(const char* url
,
171 net::RequestPriority priority
) {
172 return NewURLRequestWithRoute(url
, priority
, kRouteId
);
175 TestRequest
* NewRequestWithRoute(const char* url
,
176 net::RequestPriority priority
,
178 scoped_ptr
<net::URLRequest
> url_request(
179 NewURLRequestWithRoute(url
, priority
, route_id
));
180 scoped_ptr
<ResourceThrottle
> throttle(scheduler_
.ScheduleRequest(
181 kChildId
, route_id
, url_request
.get()));
182 TestRequest
* request
= new TestRequest(throttle
.Pass(), url_request
.Pass());
187 TestRequest
* NewRequest(const char* url
, net::RequestPriority priority
) {
188 return NewRequestWithRoute(url
, priority
, kRouteId
);
191 void ChangeRequestPriority(TestRequest
* request
,
192 net::RequestPriority new_priority
,
193 int intra_priority
= 0) {
194 scoped_refptr
<FakeResourceMessageFilter
> filter(
195 new FakeResourceMessageFilter(kChildId
));
196 const ResourceRequestInfoImpl
* info
= ResourceRequestInfoImpl::ForRequest(
197 request
->url_request());
198 const GlobalRequestID
& id
= info
->GetGlobalRequestID();
199 ResourceHostMsg_DidChangePriority
msg(id
.request_id
, new_priority
,
201 rdh_
.OnMessageReceived(msg
, filter
.get());
204 int next_request_id_
;
205 base::MessageLoopForIO message_loop_
;
206 BrowserThreadImpl ui_thread_
;
207 BrowserThreadImpl io_thread_
;
208 ResourceDispatcherHostImpl rdh_
;
209 ResourceScheduler scheduler_
;
210 net::HttpServerPropertiesImpl http_server_properties_
;
211 net::TestURLRequestContext context_
;
214 TEST_F(ResourceSchedulerTest
, OneIsolatedLowRequest
) {
215 scoped_ptr
<TestRequest
> request(NewRequest("http://host/1", net::LOWEST
));
216 EXPECT_TRUE(request
->started());
219 TEST_F(ResourceSchedulerTest
, OneLowLoadsUntilIdle
) {
220 scoped_ptr
<TestRequest
> high(NewRequest("http://host/high", net::HIGHEST
));
221 scoped_ptr
<TestRequest
> low(NewRequest("http://host/low", net::LOWEST
));
222 scoped_ptr
<TestRequest
> low2(NewRequest("http://host/low", net::LOWEST
));
223 EXPECT_TRUE(high
->started());
224 EXPECT_TRUE(low
->started());
225 EXPECT_FALSE(low2
->started());
227 EXPECT_TRUE(low2
->started());
230 TEST_F(ResourceSchedulerTest
, OneLowLoadsUntilBodyInserted
) {
231 scoped_ptr
<TestRequest
> high(NewRequest("http://host/high", net::HIGHEST
));
232 scoped_ptr
<TestRequest
> low(NewRequest("http://host/low", net::LOWEST
));
233 scoped_ptr
<TestRequest
> low2(NewRequest("http://host/low", net::LOWEST
));
234 EXPECT_TRUE(high
->started());
235 EXPECT_TRUE(low
->started());
236 EXPECT_FALSE(low2
->started());
237 scheduler_
.OnWillInsertBody(kChildId
, kRouteId
);
238 EXPECT_TRUE(low2
->started());
241 TEST_F(ResourceSchedulerTest
, OneLowLoadsUntilBodyInsertedExceptSpdy
) {
242 http_server_properties_
.SetSupportsSpdy(
243 net::HostPortPair("spdyhost", 443), true);
244 scoped_ptr
<TestRequest
> high(NewRequest("http://host/high", net::HIGHEST
));
245 scoped_ptr
<TestRequest
> low_spdy(
246 NewRequest("https://spdyhost/high", net::LOWEST
));
247 scoped_ptr
<TestRequest
> low(NewRequest("http://host/low", net::LOWEST
));
248 scoped_ptr
<TestRequest
> low2(NewRequest("http://host/low", net::LOWEST
));
249 EXPECT_TRUE(high
->started());
250 EXPECT_TRUE(low_spdy
->started());
251 EXPECT_TRUE(low
->started());
252 EXPECT_FALSE(low2
->started());
253 scheduler_
.OnWillInsertBody(kChildId
, kRouteId
);
254 EXPECT_TRUE(low2
->started());
257 TEST_F(ResourceSchedulerTest
, NavigationResetsState
) {
258 scheduler_
.OnWillInsertBody(kChildId
, kRouteId
);
259 scheduler_
.OnNavigate(kChildId
, kRouteId
);
260 scoped_ptr
<TestRequest
> high(NewRequest("http://host/high", net::HIGHEST
));
261 scoped_ptr
<TestRequest
> low(NewRequest("http://host/low", net::LOWEST
));
262 scoped_ptr
<TestRequest
> low2(NewRequest("http://host/low", net::LOWEST
));
263 EXPECT_TRUE(high
->started());
264 EXPECT_TRUE(low
->started());
265 EXPECT_FALSE(low2
->started());
268 TEST_F(ResourceSchedulerTest
, BackgroundRequestStartsImmediately
) {
269 const int route_id
= 0; // Indicates a background request.
270 scoped_ptr
<TestRequest
> request(NewRequestWithRoute("http://host/1",
271 net::LOWEST
, route_id
));
272 EXPECT_TRUE(request
->started());
275 TEST_F(ResourceSchedulerTest
, StartMultipleLowRequestsWhenIdle
) {
276 scoped_ptr
<TestRequest
> high1(NewRequest("http://host/high1", net::HIGHEST
));
277 scoped_ptr
<TestRequest
> high2(NewRequest("http://host/high2", net::HIGHEST
));
278 scoped_ptr
<TestRequest
> low(NewRequest("http://host/low", net::LOWEST
));
279 scoped_ptr
<TestRequest
> low2(NewRequest("http://host/low", net::LOWEST
));
280 EXPECT_TRUE(high1
->started());
281 EXPECT_TRUE(high2
->started());
282 EXPECT_TRUE(low
->started());
283 EXPECT_FALSE(low2
->started());
285 EXPECT_FALSE(low2
->started());
287 EXPECT_TRUE(low2
->started());
290 TEST_F(ResourceSchedulerTest
, CancelOtherRequestsWhileResuming
) {
291 scoped_ptr
<TestRequest
> high(NewRequest("http://host/high", net::HIGHEST
));
292 scoped_ptr
<TestRequest
> low1(NewRequest("http://host/low1", net::LOWEST
));
294 scoped_ptr
<net::URLRequest
> url_request(
295 NewURLRequest("http://host/low2", net::LOWEST
));
296 scoped_ptr
<ResourceThrottle
> throttle(scheduler_
.ScheduleRequest(
297 kChildId
, kRouteId
, url_request
.get()));
298 scoped_ptr
<CancelingTestRequest
> low2(new CancelingTestRequest(
299 throttle
.Pass(), url_request
.Pass()));
302 scoped_ptr
<TestRequest
> low3(NewRequest("http://host/low3", net::LOWEST
));
303 low2
->set_request_to_cancel(low3
.Pass());
304 scoped_ptr
<TestRequest
> low4(NewRequest("http://host/low4", net::LOWEST
));
306 EXPECT_TRUE(high
->started());
307 EXPECT_FALSE(low2
->started());
309 EXPECT_TRUE(low1
->started());
310 EXPECT_TRUE(low2
->started());
311 EXPECT_TRUE(low4
->started());
314 TEST_F(ResourceSchedulerTest
, LimitedNumberOfDelayableRequestsInFlight
) {
315 // We only load low priority resources if there's a body.
316 scheduler_
.OnWillInsertBody(kChildId
, kRouteId
);
318 // Throw in one high priority request to make sure that's not a factor.
319 scoped_ptr
<TestRequest
> high(NewRequest("http://host/high", net::HIGHEST
));
320 EXPECT_TRUE(high
->started());
322 const int kMaxNumDelayableRequestsPerClient
= 10; // Should match the .cc.
323 const int kMaxNumDelayableRequestsPerHost
= 6;
324 ScopedVector
<TestRequest
> lows_singlehost
;
325 // Queue up to the per-host limit (we subtract the current high-pri request).
326 for (int i
= 0; i
< kMaxNumDelayableRequestsPerHost
- 1; ++i
) {
327 string url
= "http://host/low" + base::IntToString(i
);
328 lows_singlehost
.push_back(NewRequest(url
.c_str(), net::LOWEST
));
329 EXPECT_TRUE(lows_singlehost
[i
]->started());
332 scoped_ptr
<TestRequest
> second_last_singlehost(NewRequest("http://host/last",
334 scoped_ptr
<TestRequest
> last_singlehost(NewRequest("http://host/s_last",
337 EXPECT_FALSE(second_last_singlehost
->started());
339 EXPECT_TRUE(second_last_singlehost
->started());
340 EXPECT_FALSE(last_singlehost
->started());
341 lows_singlehost
.erase(lows_singlehost
.begin());
342 EXPECT_TRUE(last_singlehost
->started());
344 // Queue more requests from different hosts until we reach the total limit.
345 int expected_slots_left
=
346 kMaxNumDelayableRequestsPerClient
- kMaxNumDelayableRequestsPerHost
;
347 EXPECT_GT(expected_slots_left
, 0);
348 ScopedVector
<TestRequest
> lows_differenthosts
;
349 for (int i
= 0; i
< expected_slots_left
; ++i
) {
350 string url
= "http://host" + base::IntToString(i
) + "/low";
351 lows_differenthosts
.push_back(NewRequest(url
.c_str(), net::LOWEST
));
352 EXPECT_TRUE(lows_differenthosts
[i
]->started());
355 scoped_ptr
<TestRequest
> last_differenthost(NewRequest("http://host_new/last",
357 EXPECT_FALSE(last_differenthost
->started());
360 TEST_F(ResourceSchedulerTest
, RaisePriorityAndStart
) {
361 // Dummies to enforce scheduling.
362 scoped_ptr
<TestRequest
> high(NewRequest("http://host/high", net::HIGHEST
));
363 scoped_ptr
<TestRequest
> low(NewRequest("http://host/req", net::LOWEST
));
365 scoped_ptr
<TestRequest
> request(NewRequest("http://host/req", net::LOWEST
));
366 EXPECT_FALSE(request
->started());
368 ChangeRequestPriority(request
.get(), net::HIGHEST
);
369 EXPECT_TRUE(request
->started());
372 TEST_F(ResourceSchedulerTest
, RaisePriorityInQueue
) {
373 // Dummies to enforce scheduling.
374 scoped_ptr
<TestRequest
> high(NewRequest("http://host/high", net::HIGHEST
));
375 scoped_ptr
<TestRequest
> low(NewRequest("http://host/low", net::LOWEST
));
377 scoped_ptr
<TestRequest
> request(NewRequest("http://host/req", net::IDLE
));
378 scoped_ptr
<TestRequest
> idle(NewRequest("http://host/idle", net::IDLE
));
379 EXPECT_FALSE(request
->started());
380 EXPECT_FALSE(idle
->started());
382 ChangeRequestPriority(request
.get(), net::LOWEST
);
383 EXPECT_FALSE(request
->started());
384 EXPECT_FALSE(idle
->started());
386 const int kMaxNumDelayableRequestsPerClient
= 10; // Should match the .cc.
387 ScopedVector
<TestRequest
> lows
;
388 for (int i
= 0; i
< kMaxNumDelayableRequestsPerClient
- 1; ++i
) {
389 string url
= "http://host/low" + base::IntToString(i
);
390 lows
.push_back(NewRequest(url
.c_str(), net::LOWEST
));
393 scheduler_
.OnWillInsertBody(kChildId
, kRouteId
);
394 EXPECT_TRUE(request
->started());
395 EXPECT_FALSE(idle
->started());
398 TEST_F(ResourceSchedulerTest
, LowerPriority
) {
399 // Dummies to enforce scheduling.
400 scoped_ptr
<TestRequest
> high(NewRequest("http://host/high", net::HIGHEST
));
401 scoped_ptr
<TestRequest
> low(NewRequest("http://host/low", net::LOWEST
));
403 scoped_ptr
<TestRequest
> request(NewRequest("http://host/req", net::LOWEST
));
404 scoped_ptr
<TestRequest
> idle(NewRequest("http://host/idle", net::IDLE
));
405 EXPECT_FALSE(request
->started());
406 EXPECT_FALSE(idle
->started());
408 ChangeRequestPriority(request
.get(), net::IDLE
);
409 EXPECT_FALSE(request
->started());
410 EXPECT_FALSE(idle
->started());
412 const int kMaxNumDelayableRequestsPerClient
= 10; // Should match the .cc.
413 // 2 fewer filler requests: 1 for the "low" dummy at the start, and 1 for the
414 // one at the end, which will be tested.
415 const int kNumFillerRequests
= kMaxNumDelayableRequestsPerClient
- 2;
416 ScopedVector
<TestRequest
> lows
;
417 for (int i
= 0; i
< kNumFillerRequests
; ++i
) {
418 string url
= "http://host" + base::IntToString(i
) + "/low";
419 lows
.push_back(NewRequest(url
.c_str(), net::LOWEST
));
422 scheduler_
.OnWillInsertBody(kChildId
, kRouteId
);
423 EXPECT_FALSE(request
->started());
424 EXPECT_TRUE(idle
->started());
427 TEST_F(ResourceSchedulerTest
, ReprioritizedRequestGoesToBackOfQueue
) {
428 // Dummies to enforce scheduling.
429 scoped_ptr
<TestRequest
> high(NewRequest("http://host/high", net::HIGHEST
));
430 scoped_ptr
<TestRequest
> low(NewRequest("http://host/high", net::LOWEST
));
432 scoped_ptr
<TestRequest
> request(NewRequest("http://host/req", net::LOWEST
));
433 scoped_ptr
<TestRequest
> idle(NewRequest("http://host/idle", net::IDLE
));
434 EXPECT_FALSE(request
->started());
435 EXPECT_FALSE(idle
->started());
437 const int kMaxNumDelayableRequestsPerClient
= 10; // Should match the .cc.
438 ScopedVector
<TestRequest
> lows
;
439 for (int i
= 0; i
< kMaxNumDelayableRequestsPerClient
; ++i
) {
440 string url
= "http://host/low" + base::IntToString(i
);
441 lows
.push_back(NewRequest(url
.c_str(), net::LOWEST
));
444 ChangeRequestPriority(request
.get(), net::IDLE
);
445 EXPECT_FALSE(request
->started());
446 EXPECT_FALSE(idle
->started());
448 ChangeRequestPriority(request
.get(), net::LOWEST
);
449 EXPECT_FALSE(request
->started());
450 EXPECT_FALSE(idle
->started());
452 scheduler_
.OnWillInsertBody(kChildId
, kRouteId
);
453 EXPECT_FALSE(request
->started());
454 EXPECT_FALSE(idle
->started());
457 TEST_F(ResourceSchedulerTest
, HigherIntraPriorityGoesToFrontOfQueue
) {
458 // Dummies to enforce scheduling.
459 scoped_ptr
<TestRequest
> high(NewRequest("http://host/high", net::HIGHEST
));
460 scoped_ptr
<TestRequest
> low(NewRequest("http://host/high", net::LOWEST
));
462 const int kMaxNumDelayableRequestsPerClient
= 10; // Should match the .cc.
463 ScopedVector
<TestRequest
> lows
;
464 for (int i
= 0; i
< kMaxNumDelayableRequestsPerClient
; ++i
) {
465 string url
= "http://host/low" + base::IntToString(i
);
466 lows
.push_back(NewRequest(url
.c_str(), net::IDLE
));
469 scoped_ptr
<TestRequest
> request(NewRequest("http://host/req", net::IDLE
));
470 EXPECT_FALSE(request
->started());
472 ChangeRequestPriority(request
.get(), net::IDLE
, 1);
473 EXPECT_FALSE(request
->started());
475 scheduler_
.OnWillInsertBody(kChildId
, kRouteId
);
476 EXPECT_TRUE(request
->started());
479 TEST_F(ResourceSchedulerTest
, NonHTTPSchedulesImmediately
) {
480 // Dummies to enforce scheduling.
481 scoped_ptr
<TestRequest
> high(NewRequest("http://host/high", net::HIGHEST
));
482 scoped_ptr
<TestRequest
> low(NewRequest("http://host/high", net::LOWEST
));
484 scoped_ptr
<TestRequest
> request(
485 NewRequest("chrome-extension://req", net::LOWEST
));
486 EXPECT_TRUE(request
->started());
489 TEST_F(ResourceSchedulerTest
, SpdyProxySchedulesImmediately
) {
490 scoped_ptr
<TestRequest
> high(NewRequest("http://host/high", net::HIGHEST
));
491 scoped_ptr
<TestRequest
> low(NewRequest("http://host/low", net::LOWEST
));
493 scoped_ptr
<TestRequest
> request(NewRequest("http://host/req", net::IDLE
));
494 EXPECT_FALSE(request
->started());
496 scheduler_
.OnReceivedSpdyProxiedHttpResponse(kChildId
, kRouteId
);
497 EXPECT_TRUE(request
->started());
499 scoped_ptr
<TestRequest
> after(NewRequest("http://host/after", net::IDLE
));
500 EXPECT_TRUE(after
->started());
503 TEST_F(ResourceSchedulerTest
, NewSpdyHostInDelayableRequests
) {
504 scheduler_
.OnWillInsertBody(kChildId
, kRouteId
);
505 const int kMaxNumDelayableRequestsPerClient
= 10; // Should match the .cc.
507 scoped_ptr
<TestRequest
> low1_spdy(
508 NewRequest("http://spdyhost1:8080/low", net::LOWEST
));
509 // Cancel a request after we learn the server supports SPDY.
510 ScopedVector
<TestRequest
> lows
;
511 for (int i
= 0; i
< kMaxNumDelayableRequestsPerClient
- 1; ++i
) {
512 string url
= "http://host" + base::IntToString(i
) + "/low";
513 lows
.push_back(NewRequest(url
.c_str(), net::LOWEST
));
515 scoped_ptr
<TestRequest
> low1(NewRequest("http://host/low", net::LOWEST
));
516 EXPECT_FALSE(low1
->started());
517 http_server_properties_
.SetSupportsSpdy(
518 net::HostPortPair("spdyhost1", 8080), true);
520 EXPECT_TRUE(low1
->started());
523 scoped_ptr
<TestRequest
> low2_spdy(
524 NewRequest("http://spdyhost2:8080/low", net::IDLE
));
525 // Reprioritize a request after we learn the server supports SPDY.
526 EXPECT_TRUE(low2_spdy
->started());
527 http_server_properties_
.SetSupportsSpdy(
528 net::HostPortPair("spdyhost2", 8080), true);
529 ChangeRequestPriority(low2_spdy
.get(), net::LOWEST
);
530 scoped_ptr
<TestRequest
> low2(NewRequest("http://host/low", net::LOWEST
));
531 EXPECT_TRUE(low2
->started());
534 } // unnamed namespace
536 } // namespace content