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 |