| // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/web_app.h" |
| |
| #include "base/gfx/png_decoder.h" |
| #include "chrome/browser/profile.h" |
| #include "chrome/browser/render_view_host.h" |
| #include "chrome/browser/tab_contents/web_contents.h" |
| #include "chrome/common/gfx/favicon_size.h" |
| #include "net/base/base64.h" |
| #include "net/base/data_url.h" |
| |
| namespace { |
| |
| static const char kPNGImageMimeType[] = "image/png"; |
| |
| static std::set<GURL> ExtractImageURLs(const GearsShortcutData& data) { |
| std::set<GURL> image_urls; |
| for (size_t i = 0; i < arraysize(data.icons); ++i) { |
| if (data.icons[i].url) { |
| GURL image_url(data.icons[i].url); |
| if (image_url.is_valid()) |
| image_urls.insert(image_url); |
| else |
| NOTREACHED(); |
| } |
| } |
| return image_urls; |
| } |
| |
| static SkBitmap DecodePNGEncodedURL(const GURL& url) { |
| std::string mime_type, charset, data; |
| if (!url.SchemeIs("data") || |
| !net::DataURL::Parse(url, &mime_type, &charset, &data) || |
| mime_type != kPNGImageMimeType) { |
| return SkBitmap(); |
| } |
| |
| SkBitmap image; |
| std::vector<unsigned char> v_data; |
| v_data.resize(data.size(), 0); |
| memcpy(&v_data.front(), data.c_str(), data.size()); |
| PNGDecoder::Decode(&v_data, &image); |
| return image; |
| } |
| |
| } // namespace |
| |
| // WebApp ---------------------------------------------------------------------- |
| |
| WebApp::WebApp(Profile* profile, |
| const GURL& url, |
| const std::wstring& name) |
| : web_contents_(NULL), |
| profile_(profile), |
| url_(url), |
| name_(name), |
| loaded_images_from_web_data_(false), |
| image_load_handle_(0), |
| download_images_(false) { |
| } |
| |
| WebApp::WebApp(Profile* profile, |
| const GearsShortcutData& shortcut) |
| : web_contents_(NULL), |
| profile_(profile), |
| url_(shortcut.url), |
| name_(shortcut.name ? UTF8ToWide(shortcut.name) : std::wstring()), |
| loaded_images_from_web_data_(false), |
| image_load_handle_(0), |
| image_urls_(ExtractImageURLs(shortcut)), |
| download_images_(!image_urls_.empty()) { |
| ExtractPNGEncodedURLs(); |
| // If the image urls are all data encoded urls and at least one is favicon |
| // sized, then no need to load/store in web data. |
| loaded_images_from_web_data_ = (GetFavIconIterator() != images_.end() && |
| image_urls_.empty()); |
| } |
| |
| WebApp::~WebApp() { |
| if (image_load_handle_) { |
| WebDataService* service = |
| profile_->GetWebDataService(Profile::EXPLICIT_ACCESS); |
| if (service) |
| service->CancelRequest(image_load_handle_); |
| } |
| } |
| |
| void WebApp::SetImage(const GURL& image_url, const SkBitmap& image) { |
| std::set<GURL>::iterator i = image_urls_.find(image_url); |
| if (i == image_urls_.end()) |
| return; // We didn't request the url. |
| |
| if (image.width() == 0 || image.height() == 0) { |
| // Assume there was an error downloading. By ignoring this we ensure we |
| // attempt to download the image next time user launches the app. |
| return; |
| } |
| |
| image_urls_.erase(i); |
| |
| WebDataService* service = |
| profile_->GetWebDataService(Profile::EXPLICIT_ACCESS); |
| |
| if (!image.isNull()) { |
| if (image.width() == kFavIconSize && image.height() == kFavIconSize) { |
| Images::iterator fav_icon_i = GetFavIconIterator(); |
| if (fav_icon_i != images_.end()) |
| images_.erase(fav_icon_i); // Only allow one favicon. |
| } |
| images_.push_back(image); |
| NotifyObservers(); |
| if (service) |
| service->SetWebAppImage(url_, image); |
| } |
| |
| if (service && image_urls_.empty()) |
| service->SetWebAppHasAllImages(url_, true); |
| } |
| |
| const WebApp::Images& WebApp::GetImages() { |
| LoadImagesFromWebData(); |
| |
| return images_; |
| } |
| |
| SkBitmap WebApp::GetFavIcon() { |
| // Force a load. |
| GetImages(); |
| |
| Images::iterator fav_icon_i = GetFavIconIterator(); |
| return (fav_icon_i == images_.end()) ? SkBitmap() : *fav_icon_i; |
| } |
| |
| void WebApp::SetWebContents(WebContents* host) { |
| web_contents_ = host; |
| |
| if (host && loaded_images_from_web_data_ && image_load_handle_ == 0 && |
| !image_urls_.empty()) { |
| // We haven't downloaded all the images and got a new WebContents. Download |
| // the images from it. |
| DownloadImagesFromSite(); |
| } |
| } |
| |
| void WebApp::AddObserver(Observer* obs) { |
| observer_list_.AddObserver(obs); |
| } |
| |
| void WebApp::RemoveObserver(Observer* obs) { |
| observer_list_.RemoveObserver(obs); |
| } |
| |
| void WebApp::LoadImagesFromWebData() { |
| if (loaded_images_from_web_data_) |
| return; |
| |
| loaded_images_from_web_data_ = true; |
| WebDataService* service = |
| profile_->GetWebDataService(Profile::EXPLICIT_ACCESS); |
| if (service) |
| image_load_handle_ = service->GetWebAppImages(url_, this); |
| } |
| |
| void WebApp::OnWebDataServiceRequestDone(WebDataService::Handle h, |
| const WDTypedResult* r) { |
| image_load_handle_ = 0; |
| |
| if (!r) { |
| // Results are null if the database went away. |
| return; |
| } |
| |
| WDAppImagesResult result = reinterpret_cast< |
| const WDResult<WDAppImagesResult>*>(r)->GetValue(); |
| images_.insert(images_.end(), result.images.begin(), result.images.end()); |
| |
| if (!result.has_all_images) { |
| // Not all of the images for the app have been downloaded yet; download them |
| // now. |
| DownloadImagesFromSite(); |
| } else { |
| // We have all the images. Clear image_urls_ to indicate we've got all the |
| // images. |
| image_urls_.clear(); |
| } |
| |
| if (GetFavIconIterator() == images_.end()) { |
| // No favicon. Request one from the history db. |
| LoadFavIconFromHistory(); |
| } |
| |
| if (!images_.empty()) |
| NotifyObservers(); |
| } |
| |
| void WebApp::OnFavIconFromHistory(HistoryService::Handle handle, |
| bool know_favicon, |
| scoped_refptr<RefCountedBytes> data, |
| bool expired, |
| GURL icon_url) { |
| // Make sure we still don't have a favicon. |
| if (GetFavIconIterator() != images_.end() || !data || data->data.empty()) |
| return; |
| |
| SkBitmap fav_icon; |
| if (PNGDecoder::Decode(&data->data, &fav_icon)) { |
| images_.push_back(fav_icon); |
| NotifyObservers(); |
| } |
| } |
| |
| void WebApp::LoadFavIconFromHistory() { |
| HistoryService* service = |
| profile_->GetHistoryService(Profile::EXPLICIT_ACCESS); |
| if (!service) |
| return; |
| |
| service->GetFavIconForURL(url_, &request_consumer_, |
| NewCallback(this, &WebApp::OnFavIconFromHistory)); |
| } |
| |
| void WebApp::DownloadImagesFromSite() { |
| if (!download_images_) |
| return; |
| |
| RenderViewHost* rvh = |
| web_contents_ ? web_contents_->render_view_host() : NULL; |
| if (!rvh) |
| return; |
| |
| // Copy off the images to load as we may need to mutate image_urls_ while |
| // iterating. |
| std::set<GURL> image_urls = image_urls_; |
| for (std::set<GURL>::iterator i = image_urls.begin(); i != image_urls.end(); |
| ++i) { |
| const GURL image_url = *i; |
| SkBitmap data_image = DecodePNGEncodedURL(image_url); |
| if (!data_image.isNull()) |
| SetImage(image_url, data_image); |
| else if (rvh) |
| rvh->DownloadImage(image_url, 0); // Download the image via the renderer. |
| } |
| |
| if (image_urls_.empty()) { |
| // We got all the images immediately, notifiy the web db. |
| WebDataService* service = |
| profile_->GetWebDataService(Profile::EXPLICIT_ACCESS); |
| if (service) |
| service->SetWebAppHasAllImages(url_, true); |
| } |
| } |
| |
| WebApp::Images::iterator WebApp::GetFavIconIterator() { |
| for (Images::iterator i = images_.begin(); i != images_.end(); ++i) { |
| if (i->width() == kFavIconSize && i->height() == kFavIconSize) |
| return i; |
| } |
| return images_.end(); |
| } |
| |
| void WebApp::ExtractPNGEncodedURLs() { |
| for (std::set<GURL>::iterator i = image_urls_.begin(); |
| i != image_urls_.end();) { |
| const GURL image_url = *i; |
| SkBitmap data_image = DecodePNGEncodedURL(image_url); |
| if (!data_image.isNull()) { |
| i = image_urls_.erase(i); |
| images_.push_back(data_image); |
| } else { |
| ++i; |
| } |
| } |
| } |
| |
| void WebApp::NotifyObservers() { |
| FOR_EACH_OBSERVER(Observer, observer_list_, WebAppImagesChanged(this)); |
| } |
| |