summaryrefslogtreecommitdiffstats
path: root/chrome/browser/web_app.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chrome/browser/web_app.cc')
-rw-r--r--chrome/browser/web_app.cc305
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));
+}