• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2011 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.
4 
5 #include <algorithm>
6 
7 #include "base/format_macros.h"
8 #include "base/stringprintf.h"
9 #include "net/base/net_errors.h"
10 #include "net/http/http_util.h"
11 #include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h"
12 #include "third_party/WebKit/Source/WebKit/chromium/public/WebFrameClient.h"
13 #include "third_party/WebKit/Source/WebKit/chromium/public/WebURLError.h"
14 #include "third_party/WebKit/Source/WebKit/chromium/public/WebURLResponse.h"
15 #include "third_party/WebKit/Source/WebKit/chromium/public/WebView.h"
16 #include "webkit/glue/media/buffered_resource_loader.h"
17 #include "webkit/mocks/mock_webframe.h"
18 #include "webkit/mocks/mock_weburlloader.h"
19 
20 using ::testing::_;
21 using ::testing::Assign;
22 using ::testing::AtLeast;
23 using ::testing::DeleteArg;
24 using ::testing::DoAll;
25 using ::testing::InSequence;
26 using ::testing::Invoke;
27 using ::testing::InvokeWithoutArgs;
28 using ::testing::NotNull;
29 using ::testing::Return;
30 using ::testing::ReturnRef;
31 using ::testing::SetArgumentPointee;
32 using ::testing::StrictMock;
33 using ::testing::NiceMock;
34 using ::testing::WithArgs;
35 
36 using WebKit::WebURLError;
37 using WebKit::WebFrameClient;
38 using WebKit::WebURLResponse;
39 using WebKit::WebView;
40 
41 namespace webkit_glue {
42 
43 static const char* kHttpUrl = "http://test";
44 static const char kHttpRedirectToSameDomainUrl1[] = "http://test/ing";
45 static const char kHttpRedirectToSameDomainUrl2[] = "http://test/ing2";
46 static const char kHttpRedirectToDifferentDomainUrl1[] = "http://test2";
47 static const char kHttpRedirectToDifferentDomainUrl2[] = "http://test2/ing";
48 
49 static const int kDataSize = 1024;
50 static const int kHttpOK = 200;
51 static const int kHttpPartialContent = 206;
52 
53 enum NetworkState {
54   NONE,
55   LOADED,
56   LOADING
57 };
58 
59 // Submit a request completed event to the resource loader due to request
60 // being canceled. Pretending the event is from external.
ACTION_P(RequestCanceled,loader)61 ACTION_P(RequestCanceled, loader) {
62   WebURLError error;
63   error.reason = net::ERR_ABORTED;
64   error.domain = WebString::fromUTF8(net::kErrorDomain);
65   loader->didFail(NULL, error);
66 }
67 
68 class BufferedResourceLoaderTest : public testing::Test {
69  public:
BufferedResourceLoaderTest()70   BufferedResourceLoaderTest() {
71     for (int i = 0; i < kDataSize; ++i)
72       data_[i] = i;
73   }
74 
~BufferedResourceLoaderTest()75   virtual ~BufferedResourceLoaderTest() {
76   }
77 
Initialize(const char * url,int first_position,int last_position)78   void Initialize(const char* url, int first_position, int last_position) {
79     gurl_ = GURL(url);
80     first_position_ = first_position;
81     last_position_ = last_position;
82 
83     frame_.reset(new NiceMock<MockWebFrame>());
84 
85     url_loader_ = new NiceMock<MockWebURLLoader>();
86     loader_ = new BufferedResourceLoader(gurl_,
87                                          first_position_, last_position_);
88     loader_->SetURLLoaderForTest(url_loader_);
89   }
90 
SetLoaderBuffer(size_t forward_capacity,size_t backward_capacity)91   void SetLoaderBuffer(size_t forward_capacity, size_t backward_capacity) {
92     loader_->buffer_.reset(
93         new media::SeekableBuffer(backward_capacity, forward_capacity));
94   }
95 
Start()96   void Start() {
97     InSequence s;
98     EXPECT_CALL(*url_loader_, loadAsynchronously(_, loader_.get()));
99     loader_->Start(
100         NewCallback(this, &BufferedResourceLoaderTest::StartCallback),
101         NewCallback(this, &BufferedResourceLoaderTest::NetworkCallback),
102         frame_.get());
103   }
104 
FullResponse(int64 instance_size)105   void FullResponse(int64 instance_size) {
106     FullResponse(instance_size, net::OK);
107   }
108 
FullResponse(int64 instance_size,int status)109   void FullResponse(int64 instance_size, int status) {
110     EXPECT_CALL(*this, StartCallback(status));
111     if (status != net::OK) {
112       EXPECT_CALL(*url_loader_, cancel())
113           .WillOnce(RequestCanceled(loader_));
114     }
115 
116     WebURLResponse response(gurl_);
117     response.setHTTPHeaderField(WebString::fromUTF8("Content-Length"),
118                                 WebString::fromUTF8(base::StringPrintf("%"
119                                     PRId64, instance_size)));
120     response.setExpectedContentLength(instance_size);
121     response.setHTTPStatusCode(kHttpOK);
122     loader_->didReceiveResponse(url_loader_, response);
123 
124     if (status == net::OK) {
125       EXPECT_EQ(instance_size, loader_->content_length());
126       EXPECT_EQ(instance_size, loader_->instance_size());
127     }
128 
129     EXPECT_FALSE(loader_->range_supported());
130   }
131 
PartialResponse(int64 first_position,int64 last_position,int64 instance_size)132   void PartialResponse(int64 first_position, int64 last_position,
133                        int64 instance_size) {
134     PartialResponse(first_position, last_position, instance_size, false, true);
135   }
136 
PartialResponse(int64 first_position,int64 last_position,int64 instance_size,bool chunked,bool accept_ranges)137   void PartialResponse(int64 first_position, int64 last_position,
138                        int64 instance_size, bool chunked, bool accept_ranges) {
139     EXPECT_CALL(*this, StartCallback(net::OK));
140 
141     WebURLResponse response(gurl_);
142     response.setHTTPHeaderField(WebString::fromUTF8("Content-Range"),
143                                 WebString::fromUTF8(base::StringPrintf("bytes "
144                                             "%" PRId64 "-%" PRId64 "/%" PRId64,
145                                             first_position,
146                                             last_position,
147                                             instance_size)));
148 
149     // HTTP 1.1 doesn't permit Content-Length with Transfer-Encoding: chunked.
150     int64 content_length = -1;
151     if (chunked) {
152       response.setHTTPHeaderField(WebString::fromUTF8("Transfer-Encoding"),
153                                   WebString::fromUTF8("chunked"));
154     } else {
155       content_length = last_position - first_position + 1;
156     }
157     response.setExpectedContentLength(content_length);
158 
159     // A server isn't required to return Accept-Ranges even though it might.
160     if (accept_ranges) {
161       response.setHTTPHeaderField(WebString::fromUTF8("Accept-Ranges"),
162                                   WebString::fromUTF8("bytes"));
163     }
164 
165     response.setHTTPStatusCode(kHttpPartialContent);
166     loader_->didReceiveResponse(url_loader_, response);
167 
168     // XXX: what's the difference between these two? For example in the chunked
169     // range request case, Content-Length is unspecified (because it's chunked)
170     // but Content-Range: a-b/c can be returned, where c == Content-Length
171     //
172     // Can we eliminate one?
173     EXPECT_EQ(content_length, loader_->content_length());
174     EXPECT_EQ(instance_size, loader_->instance_size());
175 
176     // A valid partial response should always result in this being true.
177     EXPECT_TRUE(loader_->range_supported());
178   }
179 
Redirect(const char * url)180   void Redirect(const char* url) {
181     GURL redirectUrl(url);
182     WebKit::WebURLRequest newRequest(redirectUrl);
183     WebKit::WebURLResponse redirectResponse(gurl_);
184 
185     loader_->willSendRequest(url_loader_, newRequest, redirectResponse);
186 
187     MessageLoop::current()->RunAllPending();
188   }
189 
StopWhenLoad()190   void StopWhenLoad() {
191     InSequence s;
192     EXPECT_CALL(*url_loader_, cancel())
193         .WillOnce(RequestCanceled(loader_));
194     loader_->Stop();
195     loader_ = NULL;
196   }
197 
198   // Helper method to write to |loader_| from |data_|.
WriteLoader(int position,int size)199   void WriteLoader(int position, int size) {
200     EXPECT_CALL(*this, NetworkCallback())
201         .RetiresOnSaturation();
202     loader_->didReceiveData(url_loader_,
203                             reinterpret_cast<char*>(data_ + position),
204                             size,
205                             size);
206   }
207 
208   // Helper method to read from |loader_|.
ReadLoader(int64 position,int size,uint8 * buffer)209   void ReadLoader(int64 position, int size, uint8* buffer) {
210     loader_->Read(position, size, buffer,
211                   NewCallback(this, &BufferedResourceLoaderTest::ReadCallback));
212   }
213 
214   // Verifis that data in buffer[0...size] is equal to data_[pos...pos+size].
VerifyBuffer(uint8 * buffer,int pos,int size)215   void VerifyBuffer(uint8* buffer, int pos, int size) {
216     EXPECT_EQ(0, memcmp(buffer, data_ + pos, size));
217   }
218 
ConfirmLoaderDeferredState(bool expectedVal)219   void ConfirmLoaderDeferredState(bool expectedVal) {
220     EXPECT_EQ(loader_->deferred_, expectedVal);
221   }
222 
223   MOCK_METHOD1(StartCallback, void(int error));
224   MOCK_METHOD1(ReadCallback, void(int error));
225   MOCK_METHOD0(NetworkCallback, void());
226 
227  protected:
228   GURL gurl_;
229   int64 first_position_;
230   int64 last_position_;
231 
232   scoped_refptr<BufferedResourceLoader> loader_;
233   NiceMock<MockWebURLLoader>* url_loader_;
234   scoped_ptr<NiceMock<MockWebFrame> > frame_;
235 
236   uint8 data_[kDataSize];
237 
238  private:
239   DISALLOW_COPY_AND_ASSIGN(BufferedResourceLoaderTest);
240 };
241 
TEST_F(BufferedResourceLoaderTest,StartStop)242 TEST_F(BufferedResourceLoaderTest, StartStop) {
243   Initialize(kHttpUrl, -1, -1);
244   Start();
245   StopWhenLoad();
246 }
247 
248 // Tests that a bad HTTP response is recived, e.g. file not found.
TEST_F(BufferedResourceLoaderTest,BadHttpResponse)249 TEST_F(BufferedResourceLoaderTest, BadHttpResponse) {
250   Initialize(kHttpUrl, -1, -1);
251   Start();
252 
253   EXPECT_CALL(*this, StartCallback(net::ERR_FAILED));
254   EXPECT_CALL(*url_loader_, cancel())
255       .WillOnce(RequestCanceled(loader_));
256 
257   WebURLResponse response(gurl_);
258   response.setHTTPStatusCode(404);
259   response.setHTTPStatusText("Not Found\n");
260   loader_->didReceiveResponse(url_loader_, response);
261 }
262 
263 // Tests that partial content is requested but not fulfilled.
TEST_F(BufferedResourceLoaderTest,NotPartialResponse)264 TEST_F(BufferedResourceLoaderTest, NotPartialResponse) {
265   Initialize(kHttpUrl, 100, -1);
266   Start();
267   FullResponse(1024, net::ERR_INVALID_RESPONSE);
268 }
269 
270 // Tests that a 200 response is received.
TEST_F(BufferedResourceLoaderTest,FullResponse)271 TEST_F(BufferedResourceLoaderTest, FullResponse) {
272   Initialize(kHttpUrl, -1, -1);
273   Start();
274   FullResponse(1024);
275   StopWhenLoad();
276 }
277 
278 // Tests that a partial content response is received.
TEST_F(BufferedResourceLoaderTest,PartialResponse)279 TEST_F(BufferedResourceLoaderTest, PartialResponse) {
280   Initialize(kHttpUrl, 100, 200);
281   Start();
282   PartialResponse(100, 200, 1024);
283   StopWhenLoad();
284 }
285 
TEST_F(BufferedResourceLoaderTest,PartialResponse_Chunked)286 TEST_F(BufferedResourceLoaderTest, PartialResponse_Chunked) {
287   Initialize(kHttpUrl, 100, 200);
288   Start();
289   PartialResponse(100, 200, 1024, true, true);
290   StopWhenLoad();
291 }
292 
TEST_F(BufferedResourceLoaderTest,PartialResponse_NoAcceptRanges)293 TEST_F(BufferedResourceLoaderTest, PartialResponse_NoAcceptRanges) {
294   Initialize(kHttpUrl, 100, 200);
295   Start();
296   PartialResponse(100, 200, 1024, false, false);
297   StopWhenLoad();
298 }
299 
TEST_F(BufferedResourceLoaderTest,PartialResponse_ChunkedNoAcceptRanges)300 TEST_F(BufferedResourceLoaderTest, PartialResponse_ChunkedNoAcceptRanges) {
301   Initialize(kHttpUrl, 100, 200);
302   Start();
303   PartialResponse(100, 200, 1024, true, false);
304   StopWhenLoad();
305 }
306 
307 // Tests that an invalid partial response is received.
TEST_F(BufferedResourceLoaderTest,InvalidPartialResponse)308 TEST_F(BufferedResourceLoaderTest, InvalidPartialResponse) {
309   Initialize(kHttpUrl, 0, 10);
310   Start();
311 
312   EXPECT_CALL(*this, StartCallback(net::ERR_INVALID_RESPONSE));
313   EXPECT_CALL(*url_loader_, cancel())
314       .WillOnce(RequestCanceled(loader_));
315 
316   WebURLResponse response(gurl_);
317   response.setHTTPHeaderField(WebString::fromUTF8("Content-Range"),
318                               WebString::fromUTF8(base::StringPrintf("bytes "
319                                   "%d-%d/%d", 1, 10, 1024)));
320   response.setExpectedContentLength(10);
321   response.setHTTPStatusCode(kHttpPartialContent);
322   loader_->didReceiveResponse(url_loader_, response);
323 }
324 
325 // Tests the logic of sliding window for data buffering and reading.
TEST_F(BufferedResourceLoaderTest,BufferAndRead)326 TEST_F(BufferedResourceLoaderTest, BufferAndRead) {
327   Initialize(kHttpUrl, 10, 29);
328   loader_->UpdateDeferStrategy(BufferedResourceLoader::kThresholdDefer);
329   Start();
330   PartialResponse(10, 29, 30);
331 
332   uint8 buffer[10];
333   InSequence s;
334 
335   // Writes 10 bytes and read them back.
336   WriteLoader(10, 10);
337   EXPECT_CALL(*this, ReadCallback(10));
338   ReadLoader(10, 10, buffer);
339   VerifyBuffer(buffer, 10, 10);
340 
341   // Writes 10 bytes and read 2 times.
342   WriteLoader(20, 10);
343   EXPECT_CALL(*this, ReadCallback(5));
344   ReadLoader(20, 5, buffer);
345   VerifyBuffer(buffer, 20, 5);
346   EXPECT_CALL(*this, ReadCallback(5));
347   ReadLoader(25, 5, buffer);
348   VerifyBuffer(buffer, 25, 5);
349 
350   // Read backward within buffer.
351   EXPECT_CALL(*this, ReadCallback(10));
352   ReadLoader(10, 10, buffer);
353   VerifyBuffer(buffer, 10, 10);
354 
355   // Read backward outside buffer.
356   EXPECT_CALL(*this, ReadCallback(net::ERR_CACHE_MISS));
357   ReadLoader(9, 10, buffer);
358 
359   // Response has completed.
360   EXPECT_CALL(*this, NetworkCallback());
361   loader_->didFinishLoading(url_loader_, 0);
362 
363   // Try to read 10 from position 25 will just return with 5 bytes.
364   EXPECT_CALL(*this, ReadCallback(5));
365   ReadLoader(25, 10, buffer);
366   VerifyBuffer(buffer, 25, 5);
367 
368   // Try to read outside buffered range after request has completed.
369   EXPECT_CALL(*this, ReadCallback(net::ERR_CACHE_MISS));
370   ReadLoader(5, 10, buffer);
371 
372   // Try to read beyond the instance size.
373   EXPECT_CALL(*this, ReadCallback(0));
374   ReadLoader(30, 10, buffer);
375 }
376 
TEST_F(BufferedResourceLoaderTest,ReadOutsideBuffer)377 TEST_F(BufferedResourceLoaderTest, ReadOutsideBuffer) {
378   Initialize(kHttpUrl, 10, 0x00FFFFFF);
379   loader_->UpdateDeferStrategy(BufferedResourceLoader::kThresholdDefer);
380   Start();
381   PartialResponse(10, 0x00FFFFFF, 0x01000000);
382 
383   uint8 buffer[10];
384   InSequence s;
385 
386   // Read very far aheard will get a cache miss.
387   EXPECT_CALL(*this, ReadCallback(net::ERR_CACHE_MISS));
388   ReadLoader(0x00FFFFFF, 1, buffer);
389 
390   // The following call will not call ReadCallback() because it is waiting for
391   // data to arrive.
392   ReadLoader(10, 10, buffer);
393 
394   // Writing to loader will fulfill the read request.
395   EXPECT_CALL(*this, ReadCallback(10));
396   WriteLoader(10, 20);
397   VerifyBuffer(buffer, 10, 10);
398 
399   // The following call cannot be fulfilled now.
400   ReadLoader(25, 10, buffer);
401 
402   EXPECT_CALL(*this, ReadCallback(5));
403   EXPECT_CALL(*this, NetworkCallback());
404   loader_->didFinishLoading(url_loader_, 0);
405 }
406 
TEST_F(BufferedResourceLoaderTest,RequestFailedWhenRead)407 TEST_F(BufferedResourceLoaderTest, RequestFailedWhenRead) {
408   Initialize(kHttpUrl, 10, 29);
409   Start();
410   PartialResponse(10, 29, 30);
411 
412   uint8 buffer[10];
413   InSequence s;
414 
415   ReadLoader(10, 10, buffer);
416   EXPECT_CALL(*this, ReadCallback(net::ERR_FAILED));
417   EXPECT_CALL(*this, NetworkCallback());
418   WebURLError error;
419   error.reason = net::ERR_FAILED;
420   loader_->didFail(url_loader_, error);
421 }
422 
423 // Tests the data buffering logic of NeverDefer strategy.
TEST_F(BufferedResourceLoaderTest,NeverDeferStrategy)424 TEST_F(BufferedResourceLoaderTest, NeverDeferStrategy) {
425   Initialize(kHttpUrl, 10, 99);
426   SetLoaderBuffer(10, 20);
427   loader_->UpdateDeferStrategy(BufferedResourceLoader::kNeverDefer);
428   Start();
429   PartialResponse(10, 99, 100);
430 
431   uint8 buffer[10];
432 
433   // Read past the buffer size; should not defer regardless.
434   WriteLoader(10, 10);
435   WriteLoader(20, 50);
436   ConfirmLoaderDeferredState(false);
437 
438   // Should move past window.
439   EXPECT_CALL(*this, ReadCallback(net::ERR_CACHE_MISS));
440   ReadLoader(10, 10, buffer);
441 
442   StopWhenLoad();
443 }
444 
445 // Tests the data buffering logic of ReadThenDefer strategy.
TEST_F(BufferedResourceLoaderTest,ReadThenDeferStrategy)446 TEST_F(BufferedResourceLoaderTest, ReadThenDeferStrategy) {
447   Initialize(kHttpUrl, 10, 99);
448   SetLoaderBuffer(10, 20);
449   loader_->UpdateDeferStrategy(BufferedResourceLoader::kReadThenDefer);
450   Start();
451   PartialResponse(10, 99, 100);
452 
453   uint8 buffer[10];
454 
455   // Make an outstanding read request.
456   // We should disable deferring after the read request, so expect
457   // a network event.
458   EXPECT_CALL(*this, NetworkCallback());
459   ReadLoader(10, 10, buffer);
460 
461   // Receive almost enough data to cover, shouldn't defer.
462   WriteLoader(10, 9);
463   ConfirmLoaderDeferredState(false);
464 
465   // As soon as we have received enough data to fulfill the read, defer.
466   EXPECT_CALL(*this, NetworkCallback());
467   EXPECT_CALL(*this, ReadCallback(10));
468   WriteLoader(19, 1);
469 
470   ConfirmLoaderDeferredState(true);
471   VerifyBuffer(buffer, 10, 10);
472 
473   StopWhenLoad();
474 }
475 
476 // Tests the data buffering logic of ThresholdDefer strategy.
TEST_F(BufferedResourceLoaderTest,ThresholdDeferStrategy)477 TEST_F(BufferedResourceLoaderTest, ThresholdDeferStrategy) {
478   Initialize(kHttpUrl, 10, 99);
479   SetLoaderBuffer(10, 20);
480   loader_->UpdateDeferStrategy(BufferedResourceLoader::kThresholdDefer);
481   Start();
482   PartialResponse(10, 99, 100);
483 
484   uint8 buffer[10];
485 
486   WriteLoader(10, 5);
487   // Haven't reached threshold, don't defer.
488   ConfirmLoaderDeferredState(false);
489 
490   // We're at the threshold now, let's defer.
491   EXPECT_CALL(*this, NetworkCallback());
492   WriteLoader(15, 5);
493   ConfirmLoaderDeferredState(true);
494 
495   // Now we've read over half of the buffer, disable deferring.
496   EXPECT_CALL(*this, ReadCallback(6));
497   EXPECT_CALL(*this, NetworkCallback());
498   ReadLoader(10, 6, buffer);
499 
500   ConfirmLoaderDeferredState(false);
501   VerifyBuffer(buffer, 10, 6);
502 
503   StopWhenLoad();
504 }
505 
506 // NOTE: This test will need to be reworked a little once
507 // http://code.google.com/p/chromium/issues/detail?id=72578
508 // is fixed.
TEST_F(BufferedResourceLoaderTest,HasSingleOrigin)509 TEST_F(BufferedResourceLoaderTest, HasSingleOrigin) {
510   // Make sure no redirect case works as expected.
511   Initialize(kHttpUrl, -1, -1);
512   Start();
513   FullResponse(1024);
514   EXPECT_TRUE(loader_->HasSingleOrigin());
515   StopWhenLoad();
516 
517   // Test redirect to the same domain.
518   Initialize(kHttpUrl, -1, -1);
519   Start();
520   Redirect(kHttpRedirectToSameDomainUrl1);
521   FullResponse(1024);
522   EXPECT_TRUE(loader_->HasSingleOrigin());
523   StopWhenLoad();
524 
525   // Test redirect twice to the same domain.
526   Initialize(kHttpUrl, -1, -1);
527   Start();
528   Redirect(kHttpRedirectToSameDomainUrl1);
529   Redirect(kHttpRedirectToSameDomainUrl2);
530   FullResponse(1024);
531   EXPECT_TRUE(loader_->HasSingleOrigin());
532   StopWhenLoad();
533 
534   // Test redirect to a different domain.
535   Initialize(kHttpUrl, -1, -1);
536   Start();
537   Redirect(kHttpRedirectToDifferentDomainUrl1);
538   EXPECT_FALSE(loader_->HasSingleOrigin());
539   StopWhenLoad();
540 
541   // Test redirect to the same domain and then to a different domain.
542   Initialize(kHttpUrl, -1, -1);
543   Start();
544   Redirect(kHttpRedirectToSameDomainUrl1);
545   Redirect(kHttpRedirectToDifferentDomainUrl1);
546   EXPECT_FALSE(loader_->HasSingleOrigin());
547   StopWhenLoad();
548 }
549 
550 // TODO(hclam): add unit test for defer loading.
551 
552 }  // namespace webkit_glue
553