Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright 2016 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 "chrome/browser/android/webapps/add_to_homescreen_data_fetcher.h" | |
| 6 | |
| 7 #include <memory> | |
| 8 #include <string> | |
| 9 | |
| 10 #include "base/callback_forward.h" | |
| 11 #include "base/files/file_path.h" | |
| 12 #include "base/macros.h" | |
| 13 #include "base/memory/ref_counted.h" | |
| 14 #include "base/message_loop/message_loop.h" | |
| 15 #include "base/run_loop.h" | |
| 16 #include "base/strings/nullable_string16.h" | |
| 17 #include "base/strings/utf_string_conversions.h" | |
| 18 #include "base/time/time.h" | |
| 19 #include "chrome/common/web_application_info.h" | |
| 20 #include "chrome/test/base/chrome_render_view_host_test_harness.h" | |
| 21 #include "chrome/test/base/testing_profile.h" | |
| 22 #include "content/browser/service_worker/embedded_worker_test_helper.h" | |
| 23 #include "content/browser/service_worker/service_worker_context_core.h" | |
| 24 #include "content/common/service_worker/service_worker_status_code.h" | |
| 25 #include "content/public/browser/site_instance.h" | |
| 26 #include "content/public/browser/web_contents.h" | |
| 27 #include "content/public/common/manifest.h" | |
| 28 #include "content/test/test_web_contents.h" | |
| 29 #include "net/http/http_status_code.h" | |
| 30 #include "third_party/WebKit/public/platform/WebDisplayMode.h" | |
| 31 #include "ui/gfx/image/image_unittest_util.h" | |
| 32 #include "url/gurl.h" | |
| 33 | |
| 34 namespace { | |
| 35 | |
| 36 const char* kDefaultManifestUrl = "https://www.example.com/manifest.json"; | |
| 37 const char* kDefaultManifestName = "Default Name"; | |
| 38 const char* kDefaultManifestShortName = "Default Short Name"; | |
| 39 const char* kDefaultStartUrl = "https://www.example.com/index.html"; | |
| 40 const blink::WebDisplayMode kDefaultManifestDisplayMode = | |
| 41 blink::WebDisplayModeStandalone; | |
| 42 | |
| 43 // WebContents subclass which mocks out image and manifest fetching. | |
| 44 class MockWebContents : public content::TestWebContents { | |
| 45 public: | |
| 46 explicit MockWebContents(content::BrowserContext* browser_context) | |
| 47 : content::TestWebContents(browser_context) {} | |
| 48 | |
| 49 ~MockWebContents() override { | |
| 50 } | |
| 51 | |
| 52 // Sets the manifest to be returned by GetManifest(). | |
| 53 // |fetch_delay_ms| is the time in milliseconds that the simulated fetch of | |
| 54 // the web manifest should take. | |
| 55 void SetManifest(const GURL& manifest_url, | |
| 56 const content::Manifest& manifest, | |
| 57 int fetch_delay_ms) { | |
| 58 manifest_url_ = manifest_url; | |
| 59 manifest_ = manifest; | |
| 60 manifest_fetch_delay_ms_ = fetch_delay_ms; | |
| 61 } | |
| 62 | |
| 63 int DownloadImage(const GURL& url, | |
| 64 bool is_favicon, | |
|
dominickn
2016/09/26 07:36:07
Nit: indentation
| |
| 65 uint32_t max_bitmap_size, | |
| 66 bool bypass_cache, | |
| 67 const ImageDownloadCallback& callback) override { | |
| 68 const int kIconSizePx = 144; | |
| 69 SkBitmap icon = gfx::test::CreateBitmap(kIconSizePx, kIconSizePx); | |
| 70 std::vector<SkBitmap> icons(1u, icon); | |
| 71 std::vector<gfx::Size> pixel_sizes(1u, gfx::Size(kIconSizePx, kIconSizePx)); | |
| 72 base::MessageLoopForUI::current()->PostTask( | |
| 73 FROM_HERE, | |
| 74 base::Bind(callback, 0, net::HTTP_OK, url, icons, pixel_sizes)); | |
| 75 return 0; | |
| 76 } | |
| 77 | |
| 78 void GetManifest(const GetManifestCallback& callback) override { | |
| 79 base::MessageLoopForUI::current()->PostDelayedTask( | |
| 80 FROM_HERE, base::Bind(callback, manifest_url_, manifest_), | |
| 81 base::TimeDelta::FromMilliseconds(manifest_fetch_delay_ms_)); | |
| 82 } | |
| 83 | |
| 84 private: | |
| 85 GURL manifest_url_; | |
| 86 content::Manifest manifest_; | |
| 87 int manifest_fetch_delay_ms_; | |
| 88 | |
| 89 DISALLOW_COPY_AND_ASSIGN(MockWebContents); | |
| 90 }; | |
| 91 | |
| 92 // Tracks which of the AddToHomescreenDataFetcher::Observer callbacks have been | |
| 93 // called. | |
| 94 class ObserverWaiter : public AddToHomescreenDataFetcher::Observer { | |
| 95 public: | |
| 96 ObserverWaiter() {} | |
| 97 ~ObserverWaiter() override {} | |
| 98 | |
| 99 // Waits till the OnDataAvailable() callback is called. | |
| 100 void WaitForDataAvailable() { | |
| 101 if (data_available_) | |
| 102 return; | |
| 103 | |
| 104 base::RunLoop run_loop; | |
| 105 quit_closure_ = run_loop.QuitClosure(); | |
| 106 run_loop.Run(); | |
| 107 } | |
| 108 | |
| 109 void OnDidDetermineWebApkCompatibility(bool is_webapk_compatible) override { | |
| 110 determined_webapk_compatibility = true; | |
| 111 is_webapk_compatible = is_webapk_compatible; | |
| 112 } | |
| 113 | |
| 114 void OnUserTitleAvailable(const base::string16& title) override { | |
| 115 title_available = true; | |
| 116 } | |
| 117 | |
| 118 SkBitmap FinalizeLauncherIconInBackground(const SkBitmap& icon, | |
| 119 const GURL& url, | |
| 120 bool* is_generated) override { | |
| 121 *is_generated = false; | |
| 122 return icon; | |
| 123 } | |
| 124 | |
| 125 void OnDataAvailable(const ShortcutInfo& info, | |
| 126 const SkBitmap& icon) override { | |
| 127 data_available_ = true; | |
| 128 if (!quit_closure_.is_null()) | |
| 129 quit_closure_.Run(); | |
| 130 } | |
| 131 | |
| 132 bool is_webapk_compatible; | |
| 133 bool determined_webapk_compatibility; | |
| 134 bool title_available; | |
| 135 | |
| 136 private: | |
| 137 bool data_available_; | |
| 138 base::Closure quit_closure_; | |
| 139 | |
| 140 DISALLOW_COPY_AND_ASSIGN(ObserverWaiter); | |
| 141 }; | |
| 142 | |
| 143 // Builds non-null base::NullableString16 from a UTF8 string. | |
| 144 base::NullableString16 NullableStringFromUTF8(const std::string& value) { | |
| 145 return base::NullableString16(base::UTF8ToUTF16(value), false); | |
| 146 } | |
| 147 | |
| 148 // Builds WebAPK compatible content::Manifest. | |
| 149 content::Manifest BuildDefaultManifest() { | |
| 150 content::Manifest manifest; | |
| 151 manifest.name = NullableStringFromUTF8(kDefaultManifestName); | |
| 152 manifest.short_name = NullableStringFromUTF8(kDefaultManifestShortName); | |
| 153 manifest.start_url = GURL(kDefaultStartUrl); | |
| 154 manifest.display = kDefaultManifestDisplayMode; | |
| 155 return manifest; | |
| 156 } | |
| 157 | |
| 158 } // anonymous namespace | |
| 159 | |
| 160 // Tests AddToHomescreenDataFetcher. These tests should be browser tests but | |
| 161 // Android does not support browser tests yet (crbug.com/611756). | |
| 162 class AddToHomescreenDataFetcherTest : public ChromeRenderViewHostTestHarness { | |
| 163 public: | |
| 164 AddToHomescreenDataFetcherTest() {} | |
| 165 ~AddToHomescreenDataFetcherTest() override {} | |
| 166 | |
| 167 void SetUp() override { | |
| 168 ChromeRenderViewHostTestHarness::SetUp(); | |
| 169 | |
| 170 ASSERT_TRUE(profile()->CreateHistoryService(false, true)); | |
| 171 profile()->CreateFaviconService(); | |
| 172 | |
| 173 embedded_worker_test_helper_.reset( | |
| 174 new content::EmbeddedWorkerTestHelper(base::FilePath())); | |
| 175 | |
| 176 scoped_refptr<content::SiteInstance> site_instance = | |
| 177 content::SiteInstance::Create(browser_context()); | |
| 178 site_instance->GetProcess()->Init(); | |
| 179 MockWebContents* mock_web_contents = new MockWebContents(browser_context()); | |
| 180 mock_web_contents->Init(content::WebContents::CreateParams( | |
| 181 browser_context(), std::move(site_instance))); | |
| 182 SetContents(mock_web_contents); | |
| 183 } | |
| 184 | |
| 185 scoped_refptr<AddToHomescreenDataFetcher> BuildFetcher( | |
| 186 bool check_webapk_compatible, | |
| 187 AddToHomescreenDataFetcher::Observer* observer) { | |
| 188 return new AddToHomescreenDataFetcher(web_contents(), 1, 1, 1, 1, | |
| 189 check_webapk_compatible, observer); | |
| 190 } | |
| 191 | |
| 192 // Set the manifest to be returned as a result of WebContents::GetManifest(). | |
| 193 void SetManifest(const GURL& manifest_url, | |
| 194 const content::Manifest& manifest, | |
| 195 int fetch_delay_ms) { | |
| 196 MockWebContents* mock_web_contents = | |
| 197 static_cast<MockWebContents*>(web_contents()); | |
| 198 mock_web_contents->SetManifest(manifest_url, manifest, fetch_delay_ms); | |
| 199 } | |
| 200 | |
| 201 // Registers service worker at |url|. Blocks till the service worker is | |
| 202 // registered. | |
| 203 void RegisterServiceWorker(const GURL& url) { | |
| 204 base::RunLoop run_loop; | |
| 205 embedded_worker_test_helper_->context()->RegisterServiceWorker( | |
| 206 url, GURL(url.spec() + "/service_worker.js"), nullptr, | |
| 207 base::Bind(&AddToHomescreenDataFetcherTest::OnServiceWorkerRegistered, | |
| 208 base::Unretained(this), run_loop.QuitClosure())); | |
| 209 } | |
| 210 | |
| 211 private: | |
| 212 // Callback for RegisterServiceWorker() for when service worker registration | |
| 213 // has completed. | |
| 214 void OnServiceWorkerRegistered(const base::Closure& callback, | |
| 215 content::ServiceWorkerStatusCode status, | |
| 216 const std::string& status_message, | |
| 217 int64_t registration_id) { | |
| 218 ASSERT_EQ(content::SERVICE_WORKER_OK, status) | |
| 219 << content::ServiceWorkerStatusToString(status); | |
| 220 callback.Run(); | |
| 221 } | |
| 222 | |
| 223 scoped_refptr<AddToHomescreenDataFetcher> fetcher_; | |
| 224 std::unique_ptr<ObserverWaiter> waiter_; | |
| 225 std::unique_ptr<content::EmbeddedWorkerTestHelper> | |
| 226 embedded_worker_test_helper_; | |
| 227 | |
| 228 DISALLOW_COPY_AND_ASSIGN(AddToHomescreenDataFetcherTest); | |
| 229 }; | |
| 230 | |
| 231 // Checks that AddToHomescreenDataFetcher::Observer::OnUserTitleAvailable() is | |
| 232 // called when the web manifest fetch times out. The add-to-homescreen dialog | |
| 233 // makes dialog's text field editable once OnUserTitleAvailable() is called. | |
| 234 TEST_F(AddToHomescreenDataFetcherTest, ManifestFetchTimesOutNoServiceWorker) { | |
| 235 SetManifest(GURL(kDefaultManifestUrl), BuildDefaultManifest(), 10000); | |
| 236 | |
| 237 ObserverWaiter waiter; | |
| 238 scoped_refptr<AddToHomescreenDataFetcher> fetcher( | |
| 239 BuildFetcher(false, &waiter)); | |
| 240 fetcher->OnDidGetWebApplicationInfo(WebApplicationInfo()); | |
| 241 waiter.WaitForDataAvailable(); | |
| 242 | |
| 243 EXPECT_FALSE(waiter.determined_webapk_compatibility); | |
| 244 EXPECT_TRUE(waiter.title_available); | |
| 245 | |
| 246 fetcher->set_weak_observer(nullptr); | |
| 247 } | |
| 248 | |
| 249 // Class for tests which should be run with AddToHomescreenDataFetcher built | |
| 250 // with both true and false values of |check_webapk_compatible|. | |
| 251 class AddToHomescreenDataFetcherTestCommon | |
| 252 : public AddToHomescreenDataFetcherTest, | |
| 253 public testing::WithParamInterface<bool> { | |
| 254 public: | |
| 255 AddToHomescreenDataFetcherTestCommon() {} | |
| 256 ~AddToHomescreenDataFetcherTestCommon() override {} | |
| 257 | |
| 258 scoped_refptr<AddToHomescreenDataFetcher> BuildFetcher( | |
| 259 AddToHomescreenDataFetcher::Observer* observer) { | |
| 260 return AddToHomescreenDataFetcherTest::BuildFetcher( | |
| 261 check_webapk_compatibility(), observer); | |
| 262 } | |
| 263 | |
| 264 // The value of |check_webapk_compatible| used when building the | |
| 265 // AddToHomescreenDataFetcher. | |
| 266 bool check_webapk_compatibility() { return GetParam(); } | |
| 267 | |
| 268 private: | |
| 269 DISALLOW_COPY_AND_ASSIGN(AddToHomescreenDataFetcherTestCommon); | |
| 270 }; | |
| 271 | |
| 272 // Checks that the AddToHomescreenDataFetcher::Observer callbacks are called | |
| 273 // when a service worker is registered and the manifest fetch times out. | |
| 274 TEST_P(AddToHomescreenDataFetcherTestCommon, ManifestFetchTimesOut) { | |
| 275 RegisterServiceWorker(GURL(kDefaultStartUrl)); | |
| 276 SetManifest(GURL(kDefaultManifestUrl), BuildDefaultManifest(), 10000); | |
| 277 | |
| 278 ObserverWaiter waiter; | |
| 279 scoped_refptr<AddToHomescreenDataFetcher> fetcher(BuildFetcher(&waiter)); | |
| 280 fetcher->OnDidGetWebApplicationInfo(WebApplicationInfo()); | |
| 281 waiter.WaitForDataAvailable(); | |
| 282 | |
| 283 if (check_webapk_compatibility()) { | |
| 284 EXPECT_TRUE(waiter.determined_webapk_compatibility); | |
| 285 EXPECT_FALSE(waiter.is_webapk_compatible); | |
| 286 } else { | |
| 287 EXPECT_FALSE(waiter.determined_webapk_compatibility); | |
| 288 } | |
| 289 // This callback enables the text field in the add-to-homescreen dialog. | |
| 290 EXPECT_TRUE(waiter.title_available); | |
| 291 | |
| 292 fetcher->set_weak_observer(nullptr); | |
| 293 } | |
| 294 | |
| 295 INSTANTIATE_TEST_CASE_P(CheckWebApkCompatibility, | |
| 296 AddToHomescreenDataFetcherTestCommon, | |
| 297 ::testing::Values(false, true)); | |
| OLD | NEW |