diff options
Diffstat (limited to 'chrome/browser/web_app.cc')
-rw-r--r-- | chrome/browser/web_app.cc | 305 |
1 files changed, 305 insertions, 0 deletions
diff --git a/chrome/browser/web_app.cc b/chrome/browser/web_app.cc new file mode 100644 index 0000000..4c4620f --- /dev/null +++ b/chrome/browser/web_app.cc @@ -0,0 +1,305 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#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/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") || + !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)); +} |