| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2006-2008 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/web_app.h" | |
| 6 | |
| 7 #include "base/gfx/png_decoder.h" | |
| 8 #include "chrome/browser/profile.h" | |
| 9 #include "chrome/browser/renderer_host/render_view_host.h" | |
| 10 #include "chrome/browser/tab_contents/web_contents.h" | |
| 11 #include "chrome/common/gfx/favicon_size.h" | |
| 12 #include "net/base/base64.h" | |
| 13 #include "net/base/data_url.h" | |
| 14 | |
| 15 namespace { | |
| 16 | |
| 17 static const char kPNGImageMimeType[] = "image/png"; | |
| 18 | |
| 19 static std::set<GURL> ExtractImageURLs(const GearsShortcutData& data) { | |
| 20 std::set<GURL> image_urls; | |
| 21 for (size_t i = 0; i < arraysize(data.icons); ++i) { | |
| 22 if (data.icons[i].url) { | |
| 23 GURL image_url(data.icons[i].url); | |
| 24 if (image_url.is_valid()) | |
| 25 image_urls.insert(image_url); | |
| 26 else | |
| 27 NOTREACHED(); | |
| 28 } | |
| 29 } | |
| 30 return image_urls; | |
| 31 } | |
| 32 | |
| 33 static SkBitmap DecodePNGEncodedURL(const GURL& url) { | |
| 34 std::string mime_type, charset, data; | |
| 35 if (!url.SchemeIs("data") || | |
| 36 !net::DataURL::Parse(url, &mime_type, &charset, &data) || | |
| 37 mime_type != kPNGImageMimeType) { | |
| 38 return SkBitmap(); | |
| 39 } | |
| 40 | |
| 41 SkBitmap image; | |
| 42 std::vector<unsigned char> v_data; | |
| 43 v_data.resize(data.size(), 0); | |
| 44 memcpy(&v_data.front(), data.c_str(), data.size()); | |
| 45 PNGDecoder::Decode(&v_data, &image); | |
| 46 return image; | |
| 47 } | |
| 48 | |
| 49 } // namespace | |
| 50 | |
| 51 // WebApp ---------------------------------------------------------------------- | |
| 52 | |
| 53 WebApp::WebApp(Profile* profile, | |
| 54 const GURL& url, | |
| 55 const std::wstring& name) | |
| 56 : web_contents_(NULL), | |
| 57 profile_(profile), | |
| 58 url_(url), | |
| 59 name_(name), | |
| 60 loaded_images_from_web_data_(false), | |
| 61 image_load_handle_(0), | |
| 62 download_images_(false) { | |
| 63 } | |
| 64 | |
| 65 WebApp::WebApp(Profile* profile, | |
| 66 const GearsShortcutData& shortcut) | |
| 67 : web_contents_(NULL), | |
| 68 profile_(profile), | |
| 69 url_(shortcut.url), | |
| 70 name_(shortcut.name ? UTF8ToWide(shortcut.name) : std::wstring()), | |
| 71 loaded_images_from_web_data_(false), | |
| 72 image_load_handle_(0), | |
| 73 image_urls_(ExtractImageURLs(shortcut)), | |
| 74 download_images_(!image_urls_.empty()) { | |
| 75 ExtractPNGEncodedURLs(); | |
| 76 // If the image urls are all data encoded urls and at least one is favicon | |
| 77 // sized, then no need to load/store in web data. | |
| 78 loaded_images_from_web_data_ = (GetFavIconIterator() != images_.end() && | |
| 79 image_urls_.empty()); | |
| 80 } | |
| 81 | |
| 82 WebApp::~WebApp() { | |
| 83 if (image_load_handle_) { | |
| 84 WebDataService* service = | |
| 85 profile_->GetWebDataService(Profile::EXPLICIT_ACCESS); | |
| 86 if (service) | |
| 87 service->CancelRequest(image_load_handle_); | |
| 88 } | |
| 89 } | |
| 90 | |
| 91 void WebApp::SetImage(const GURL& image_url, const SkBitmap& image) { | |
| 92 std::set<GURL>::iterator i = image_urls_.find(image_url); | |
| 93 if (i == image_urls_.end()) | |
| 94 return; // We didn't request the url. | |
| 95 | |
| 96 if (image.width() == 0 || image.height() == 0) { | |
| 97 // Assume there was an error downloading. By ignoring this we ensure we | |
| 98 // attempt to download the image next time user launches the app. | |
| 99 return; | |
| 100 } | |
| 101 | |
| 102 image_urls_.erase(i); | |
| 103 | |
| 104 WebDataService* service = | |
| 105 profile_->GetWebDataService(Profile::EXPLICIT_ACCESS); | |
| 106 | |
| 107 if (!image.isNull()) { | |
| 108 if (image.width() == kFavIconSize && image.height() == kFavIconSize) { | |
| 109 Images::iterator fav_icon_i = GetFavIconIterator(); | |
| 110 if (fav_icon_i != images_.end()) | |
| 111 images_.erase(fav_icon_i); // Only allow one favicon. | |
| 112 } | |
| 113 images_.push_back(image); | |
| 114 NotifyObservers(); | |
| 115 if (service) | |
| 116 service->SetWebAppImage(url_, image); | |
| 117 } | |
| 118 | |
| 119 if (service && image_urls_.empty()) | |
| 120 service->SetWebAppHasAllImages(url_, true); | |
| 121 } | |
| 122 | |
| 123 const WebApp::Images& WebApp::GetImages() { | |
| 124 LoadImagesFromWebData(); | |
| 125 | |
| 126 return images_; | |
| 127 } | |
| 128 | |
| 129 SkBitmap WebApp::GetFavIcon() { | |
| 130 // Force a load. | |
| 131 GetImages(); | |
| 132 | |
| 133 Images::iterator fav_icon_i = GetFavIconIterator(); | |
| 134 return (fav_icon_i == images_.end()) ? SkBitmap() : *fav_icon_i; | |
| 135 } | |
| 136 | |
| 137 void WebApp::SetWebContents(WebContents* host) { | |
| 138 web_contents_ = host; | |
| 139 | |
| 140 if (host && loaded_images_from_web_data_ && image_load_handle_ == 0 && | |
| 141 !image_urls_.empty()) { | |
| 142 // We haven't downloaded all the images and got a new WebContents. Download | |
| 143 // the images from it. | |
| 144 DownloadImagesFromSite(); | |
| 145 } | |
| 146 } | |
| 147 | |
| 148 void WebApp::AddObserver(Observer* obs) { | |
| 149 observer_list_.AddObserver(obs); | |
| 150 } | |
| 151 | |
| 152 void WebApp::RemoveObserver(Observer* obs) { | |
| 153 observer_list_.RemoveObserver(obs); | |
| 154 } | |
| 155 | |
| 156 void WebApp::LoadImagesFromWebData() { | |
| 157 if (loaded_images_from_web_data_) | |
| 158 return; | |
| 159 | |
| 160 loaded_images_from_web_data_ = true; | |
| 161 WebDataService* service = | |
| 162 profile_->GetWebDataService(Profile::EXPLICIT_ACCESS); | |
| 163 if (service) | |
| 164 image_load_handle_ = service->GetWebAppImages(url_, this); | |
| 165 } | |
| 166 | |
| 167 void WebApp::OnWebDataServiceRequestDone(WebDataService::Handle h, | |
| 168 const WDTypedResult* r) { | |
| 169 image_load_handle_ = 0; | |
| 170 | |
| 171 if (!r) { | |
| 172 // Results are null if the database went away. | |
| 173 return; | |
| 174 } | |
| 175 | |
| 176 WDAppImagesResult result = reinterpret_cast< | |
| 177 const WDResult<WDAppImagesResult>*>(r)->GetValue(); | |
| 178 images_.insert(images_.end(), result.images.begin(), result.images.end()); | |
| 179 | |
| 180 if (!result.has_all_images) { | |
| 181 // Not all of the images for the app have been downloaded yet; download them | |
| 182 // now. | |
| 183 DownloadImagesFromSite(); | |
| 184 } else { | |
| 185 // We have all the images. Clear image_urls_ to indicate we've got all the | |
| 186 // images. | |
| 187 image_urls_.clear(); | |
| 188 } | |
| 189 | |
| 190 if (GetFavIconIterator() == images_.end()) { | |
| 191 // No favicon. Request one from the history db. | |
| 192 LoadFavIconFromHistory(); | |
| 193 } | |
| 194 | |
| 195 if (!images_.empty()) | |
| 196 NotifyObservers(); | |
| 197 } | |
| 198 | |
| 199 void WebApp::OnFavIconFromHistory(HistoryService::Handle handle, | |
| 200 bool know_favicon, | |
| 201 scoped_refptr<RefCountedBytes> data, | |
| 202 bool expired, | |
| 203 GURL icon_url) { | |
| 204 // Make sure we still don't have a favicon. | |
| 205 if (GetFavIconIterator() != images_.end() || !data || data->data.empty()) | |
| 206 return; | |
| 207 | |
| 208 SkBitmap fav_icon; | |
| 209 if (PNGDecoder::Decode(&data->data, &fav_icon)) { | |
| 210 images_.push_back(fav_icon); | |
| 211 NotifyObservers(); | |
| 212 } | |
| 213 } | |
| 214 | |
| 215 void WebApp::LoadFavIconFromHistory() { | |
| 216 HistoryService* service = | |
| 217 profile_->GetHistoryService(Profile::EXPLICIT_ACCESS); | |
| 218 if (!service) | |
| 219 return; | |
| 220 | |
| 221 service->GetFavIconForURL(url_, &request_consumer_, | |
| 222 NewCallback(this, &WebApp::OnFavIconFromHistory)); | |
| 223 } | |
| 224 | |
| 225 void WebApp::DownloadImagesFromSite() { | |
| 226 if (!download_images_) | |
| 227 return; | |
| 228 | |
| 229 RenderViewHost* rvh = | |
| 230 web_contents_ ? web_contents_->render_view_host() : NULL; | |
| 231 if (!rvh) | |
| 232 return; | |
| 233 | |
| 234 // Copy off the images to load as we may need to mutate image_urls_ while | |
| 235 // iterating. | |
| 236 std::set<GURL> image_urls = image_urls_; | |
| 237 for (std::set<GURL>::iterator i = image_urls.begin(); i != image_urls.end(); | |
| 238 ++i) { | |
| 239 const GURL image_url = *i; | |
| 240 SkBitmap data_image = DecodePNGEncodedURL(image_url); | |
| 241 if (!data_image.isNull()) | |
| 242 SetImage(image_url, data_image); | |
| 243 else if (rvh) | |
| 244 rvh->DownloadImage(image_url, 0); // Download the image via the renderer. | |
| 245 } | |
| 246 | |
| 247 if (image_urls_.empty()) { | |
| 248 // We got all the images immediately, notifiy the web db. | |
| 249 WebDataService* service = | |
| 250 profile_->GetWebDataService(Profile::EXPLICIT_ACCESS); | |
| 251 if (service) | |
| 252 service->SetWebAppHasAllImages(url_, true); | |
| 253 } | |
| 254 } | |
| 255 | |
| 256 WebApp::Images::iterator WebApp::GetFavIconIterator() { | |
| 257 for (Images::iterator i = images_.begin(); i != images_.end(); ++i) { | |
| 258 if (i->width() == kFavIconSize && i->height() == kFavIconSize) | |
| 259 return i; | |
| 260 } | |
| 261 return images_.end(); | |
| 262 } | |
| 263 | |
| 264 void WebApp::ExtractPNGEncodedURLs() { | |
| 265 for (std::set<GURL>::iterator i = image_urls_.begin(); | |
| 266 i != image_urls_.end();) { | |
| 267 const GURL image_url = *i; | |
| 268 SkBitmap data_image = DecodePNGEncodedURL(image_url); | |
| 269 if (!data_image.isNull()) { | |
| 270 i = image_urls_.erase(i); | |
| 271 images_.push_back(data_image); | |
| 272 } else { | |
| 273 ++i; | |
| 274 } | |
| 275 } | |
| 276 } | |
| 277 | |
| 278 void WebApp::NotifyObservers() { | |
| 279 FOR_EACH_OBSERVER(Observer, observer_list_, WebAppImagesChanged(this)); | |
| 280 } | |
| 281 | |
| OLD | NEW |