Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(230)

Side by Side Diff: ios/chrome/browser/enhanced_bookmarks/bookmark_image_service_ios.mm

Issue 1219713002: Removed bookmark_image_service (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: fixed accidental BookmarkImageServiceFactory mention Created 5 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2014 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 #import "ios/chrome/browser/enhanced_bookmarks/bookmark_image_service_ios.h"
6
7 #include "base/bind.h"
8 #include "base/callback.h"
9 #include "base/json/json_reader.h"
10 #include "base/mac/bind_objc_block.h"
11 #include "base/mac/bundle_locations.h"
12 #include "base/strings/sys_string_conversions.h"
13 #include "base/values.h"
14 #include "components/bookmarks/browser/bookmark_model.h"
15 #include "components/enhanced_bookmarks/enhanced_bookmark_model.h"
16 #include "components/enhanced_bookmarks/enhanced_bookmark_utils.h"
17 #include "ios/chrome/browser/experimental_flags.h"
18 #include "ios/chrome/browser/ui/ui_util.h"
19 #import "ios/chrome/browser/ui/uikit_ui_util.h"
20 #include "ios/web/public/navigation_item.h"
21 #include "ios/web/public/referrer.h"
22 #include "ios/web/public/referrer_util.h"
23 #include "ios/web/public/web_state/js/crw_js_injection_evaluator.h"
24 #include "ios/web/public/web_thread.h"
25 #include "net/url_request/url_request_context_getter.h"
26 #include "ui/base/device_form_factor.h"
27 #include "url/gurl.h"
28
29 namespace {
30
31 size_t kMaxNumberCachedImageTablet = 25;
32 size_t kMaxNumberCachedImageHandset = 12;
33
34 scoped_refptr<enhanced_bookmarks::ImageRecord> ResizeImageInternalTask(
35 CGSize size,
36 bool darkened,
37 scoped_refptr<enhanced_bookmarks::ImageRecord> image_record) {
38 UIImage* result = image_record->image->ToUIImage();
39
40 if (!CGSizeEqualToSize(size, CGSizeZero) &&
41 !CGSizeEqualToSize(size, result.size)) {
42 result = ResizeImage(result, size, ProjectionMode::kAspectFill);
43 }
44
45 if (darkened)
46 result = DarkenImage(result);
47
48 if (result != image_record->image->ToUIImage())
49 image_record->image.reset(new gfx::Image([result retain]));
50
51 return image_record;
52 }
53
54 void ResizeImageInternal(
55 base::TaskRunner* task_runner,
56 CGSize size,
57 bool darkened,
58 enhanced_bookmarks::BookmarkImageService::ImageCallback callback,
59 scoped_refptr<enhanced_bookmarks::ImageRecord> image_record) {
60 const gfx::Size gfx_size(size);
61
62 if (image_record->image->IsEmpty()) {
63 callback.Run(image_record);
64 } else if ((CGSizeEqualToSize(size, CGSizeZero) ||
65 gfx_size == image_record->image->Size()) &&
66 !darkened) {
67 callback.Run(image_record);
68 } else {
69 base::Callback<scoped_refptr<enhanced_bookmarks::ImageRecord>(void)> task =
70 base::Bind(&ResizeImageInternalTask, size, darkened, image_record);
71
72 base::PostTaskAndReplyWithResult(task_runner, FROM_HERE, task, callback);
73 }
74 }
75
76 // Returns a salient image URL and a referrer for the JSON string provided.
77 std::pair<GURL, web::Referrer> RetrieveSalientImageFromJSON(
78 const std::string& json,
79 const GURL& page_url,
80 std::set<GURL>* in_progress_page_urls) {
81 DCHECK(in_progress_page_urls);
82 std::pair<GURL, web::Referrer> empty_result =
83 std::make_pair(GURL(), web::Referrer());
84 if (!json.length())
85 return empty_result;
86
87 scoped_ptr<base::Value> jsonData;
88 int errorCode = 0;
89 std::string errorMessage;
90 jsonData = base::JSONReader::ReadAndReturnError(json, base::JSON_PARSE_RFC,
91 &errorCode, &errorMessage);
92 if (errorCode || !jsonData) {
93 LOG(WARNING) << "JSON parse error: " << errorMessage.c_str() << json;
94 return empty_result;
95 }
96
97 base::DictionaryValue* dict;
98 if (!jsonData->GetAsDictionary(&dict)) {
99 LOG(WARNING) << "JSON parse error, not a dict: " << json;
100 return empty_result;
101 }
102 std::string referrerPolicy;
103 std::string image_url;
104 dict->GetString("referrerPolicy", &referrerPolicy);
105 dict->GetString("imageUrl", &image_url);
106
107 // The value is lower-cased on the JS side so comparison can be exact.
108 // Any unknown value is treated as default.
109 web::ReferrerPolicy policy = web::ReferrerPolicyDefault;
110 if (referrerPolicy == "never")
111 policy = web::ReferrerPolicyNever;
112 if (referrerPolicy == "always")
113 policy = web::ReferrerPolicyAlways;
114 if (referrerPolicy == "origin")
115 policy = web::ReferrerPolicyOrigin;
116
117 in_progress_page_urls->insert(page_url);
118
119 web::Referrer referrer(page_url, policy);
120 return std::make_pair(GURL(image_url), referrer);
121 }
122 } // namespace
123
124 class BookmarkImageServiceIOS::MRUKey {
125 public:
126 MRUKey(const GURL& page_url, const CGSize& size, bool darkened)
127 : page_url_(page_url), size_(size), darkened_(darkened) {}
128 ~MRUKey() {}
129
130 bool operator<(const MRUKey& rhs) const {
131 if (page_url_ != rhs.page_url_)
132 return page_url_ < rhs.page_url_;
133 if (size_.height != rhs.size_.height)
134 return size_.height < rhs.size_.height;
135 if (size_.width != rhs.size_.width)
136 return size_.width < rhs.size_.width;
137 return darkened_ < rhs.darkened_;
138 }
139
140 private:
141 const GURL page_url_;
142 const CGSize size_;
143 const bool darkened_;
144 };
145
146 BookmarkImageServiceIOS::BookmarkImageServiceIOS(
147 const base::FilePath& path,
148 enhanced_bookmarks::EnhancedBookmarkModel* enhanced_bookmark_model,
149 net::URLRequestContextGetter* context,
150 scoped_refptr<base::SequencedWorkerPool> store_pool)
151 : BookmarkImageService(path, enhanced_bookmark_model, store_pool),
152 imageFetcher_(new image_fetcher::ImageFetcher(store_pool)),
153 pool_(store_pool),
154 weak_ptr_factory_(this) {
155 imageFetcher_->SetRequestContextGetter(context);
156 cache_.reset(new base::MRUCache<MRUKey, MRUValue>(
157 IsIPadIdiom() ? kMaxNumberCachedImageTablet
158 : kMaxNumberCachedImageHandset));
159 }
160
161 BookmarkImageServiceIOS::BookmarkImageServiceIOS(
162 scoped_ptr<ImageStore> store,
163 enhanced_bookmarks::EnhancedBookmarkModel* enhanced_bookmark_model,
164 net::URLRequestContextGetter* context,
165 scoped_refptr<base::SequencedWorkerPool> store_pool)
166 : BookmarkImageService(store.Pass(), enhanced_bookmark_model, store_pool),
167 imageFetcher_(new image_fetcher::ImageFetcher(store_pool)),
168 pool_(store_pool),
169 weak_ptr_factory_(this) {
170 imageFetcher_->SetRequestContextGetter(context);
171 cache_.reset(new base::MRUCache<MRUKey, MRUValue>(
172 IsIPadIdiom() ? kMaxNumberCachedImageTablet
173 : kMaxNumberCachedImageHandset));
174 }
175
176 BookmarkImageServiceIOS::~BookmarkImageServiceIOS() {
177 }
178
179 scoped_ptr<gfx::Image> BookmarkImageServiceIOS::ResizeImage(
180 const gfx::Image& image) {
181 // Figure out the maximum dimension of images used on this device. On handset
182 // this is the width of the view, depending on rotation. So it is the longest
183 // screen size.
184 UIScreen* mainScreen = [UIScreen mainScreen];
185 DCHECK(mainScreen);
186
187 // Capture the max_width in pixels.
188 CGFloat max_width =
189 std::max(mainScreen.bounds.size.height, mainScreen.bounds.size.width) *
190 mainScreen.scale;
191 DCHECK(max_width > 0);
192 // On tablet the view is half the width of the screen.
193 if (ui::GetDeviceFormFactor() == ui::DEVICE_FORM_FACTOR_TABLET)
194 max_width = max_width / 2.0;
195
196 UIImage* ui_image = image.ToUIImage();
197
198 // Images already fitting the desired size are left untouched.
199 if (image.Width() < max_width || image.Height() < max_width) {
200 // This enforce the creation of a new image with a new representation
201 // instead of having the image share an internal representation. This is to
202 // avoid a crash when the internal representation of a gfx::Image is
203 // refcounted on multiple threads.
204 return scoped_ptr<gfx::Image>(new gfx::Image([ui_image retain]));
205 }
206
207 // Adjust the max_width to be in the same reference model as the
208 // UIImage.
209 max_width = max_width / ui_image.scale;
210
211 CGSize desired_size = CGSizeMake(max_width, max_width);
212
213 return scoped_ptr<gfx::Image>(new gfx::Image([ ::ResizeImage(
214 ui_image, desired_size, ProjectionMode::kAspectFillNoClipping) retain]));
215 }
216
217 // IOS does WebP transcoding as none of the platform frameworks supports this
218 // format natively. For this reason RetrieveSalientImageForPageUrl() needs to
219 // use ImageFetcher which is doing the transcoding during download.
220 void BookmarkImageServiceIOS::RetrieveSalientImage(
221 const GURL& page_url,
222 const GURL& image_url,
223 const std::string& referrer,
224 net::URLRequest::ReferrerPolicy referrer_policy,
225 bool update_bookmark) {
226 DCHECK(CalledOnValidThread());
227 DCHECK(IsPageUrlInProgress(page_url));
228
229 const bookmarks::BookmarkNode* bookmark =
230 enhanced_bookmark_model_->bookmark_model()
231 ->GetMostRecentlyAddedUserNodeForURL(page_url);
232 if (!bookmark) {
233 ProcessNewImage(page_url, update_bookmark, image_url,
234 scoped_ptr<gfx::Image>(new gfx::Image()));
235 return;
236 }
237
238 const GURL page_url_not_ref(page_url);
239
240 // From the URL request the image asynchronously.
241 image_fetcher::ImageFetchedCallback callback =
242 ^(const GURL& local_image_url, int response_code, NSData* data) {
243 DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI);
244 // Build the UIImage.
245 UIImage* image = nil;
246 if (data) {
247 image =
248 [UIImage imageWithData:data scale:[UIScreen mainScreen].scale];
249 }
250
251 ProcessNewImage(page_url_not_ref, update_bookmark, local_image_url,
252 scoped_ptr<gfx::Image>(new gfx::Image([image retain])));
253 };
254
255 if (image_url.is_valid())
256 imageFetcher_->StartDownload(image_url, callback, referrer,
257 referrer_policy);
258 else
259 ProcessNewImage(page_url, update_bookmark, image_url,
260 scoped_ptr<gfx::Image>(new gfx::Image()));
261 }
262
263 void BookmarkImageServiceIOS::RetrieveSalientImageFromContext(
264 id<CRWJSInjectionEvaluator> page_context,
265 const GURL& page_url,
266 bool update_bookmark) {
267 DCHECK(CalledOnValidThread());
268 if (IsPageUrlInProgress(page_url))
269 return; // A request for this URL is already in progress.
270
271 const bookmarks::BookmarkNode* bookmark =
272 enhanced_bookmark_model_->bookmark_model()
273 ->GetMostRecentlyAddedUserNodeForURL(page_url);
274 if (!bookmark)
275 return;
276
277 if (!experimental_flags::IsBookmarkImageFetchingOnVisitEnabled()) {
278 // Stop the image extraction if there is already an image present.
279 GURL url;
280 int height, width;
281
282 // This test below is not ideal : Having an URL for an image is not quite
283 // the same thing as having successfuly downloaded that URL. Testing for the
284 // presence of the downloaded image itself would be quite costly as it would
285 // require jumping to another thread to access the image store. Also if the
286 // user has bookmarks, but never opened the bookmark UI, there will be no
287 // image yet, so even that test would be incomplete.
288 if (enhanced_bookmark_model_->GetOriginalImage(bookmark, &url, &width,
289 &height) ||
290 enhanced_bookmark_model_->GetThumbnailImage(bookmark, &url, &width,
291 &height))
292 return;
293 }
294
295 if (!script_) {
296 NSString* path = [base::mac::FrameworkBundle()
297 pathForResource:@"bookmark_image_service_ios"
298 ofType:@"js"];
299 DCHECK(path) << "bookmark_image_service_ios script file not found.";
300 script_.reset([[NSString stringWithContentsOfFile:path
301 encoding:NSUTF8StringEncoding
302 error:nil] copy]);
303 }
304
305 if (!script_) {
306 LOG(WARNING) << "Unable to load bookmark_images_ios resource";
307 return;
308 }
309
310 // Since |page_url| is a reference make a copy since it will be used inside a
311 // block.
312 const GURL page_url_copy = page_url;
313 // Since a method on |this| is called from a block, this dance is necessary to
314 // make sure a method on |this| is not called when the object has gone away.
315 base::WeakPtr<BookmarkImageServiceIOS> weak_this =
316 weak_ptr_factory_.GetWeakPtr();
317
318 [page_context evaluateJavaScript:script_
319 stringResultHandler:^(NSString* result, NSError* error) {
320 if (!weak_this)
321 return;
322 // The script returns a json dict with just an image URL and
323 // the referrer policy for the page.
324 std::string json = base::SysNSStringToUTF8(result);
325 std::pair<GURL, web::Referrer> image_load_info =
326 RetrieveSalientImageFromJSON(json, page_url_copy,
327 &in_progress_page_urls_);
328 if (image_load_info.first.is_empty())
329 return;
330 RetrieveSalientImage(
331 page_url_copy, image_load_info.first,
332 web::ReferrerHeaderValueForNavigation(
333 image_load_info.first, image_load_info.second),
334 web::PolicyForNavigation(image_load_info.first,
335 image_load_info.second),
336 update_bookmark);
337 }];
338 }
339
340 void BookmarkImageServiceIOS::FinishSuccessfulPageLoadForNativationItem(
341 id<CRWJSInjectionEvaluator> page_context,
342 web::NavigationItem* navigation_item,
343 const GURL& original_url) {
344 DCHECK(CalledOnValidThread());
345 DCHECK(navigation_item);
346
347 // If the navigation is a simple back or forward, do not extract images, those
348 // were extracted already.
349 if (navigation_item->GetTransitionType() & ui::PAGE_TRANSITION_FORWARD_BACK)
350 return;
351
352 std::vector<GURL> urls;
353 urls.push_back(navigation_item->GetURL());
354 if (navigation_item->GetURL() != original_url)
355 urls.push_back(original_url);
356
357 for (auto url : urls) {
358 if (enhanced_bookmark_model_->bookmark_model()->IsBookmarked(url)) {
359 RetrieveSalientImageFromContext(page_context, url, true);
360 }
361 }
362 }
363
364 void BookmarkImageServiceIOS::ReturnAndCache(
365 GURL page_url,
366 CGSize size,
367 bool darkened,
368 ImageCallback callback,
369 scoped_refptr<enhanced_bookmarks::ImageRecord> image_record) {
370 cache_->Put(MRUKey(page_url, size, darkened), image_record);
371
372 callback.Run(image_record);
373 }
374
375 void BookmarkImageServiceIOS::SalientImageResizedForUrl(
376 const GURL& page_url,
377 const CGSize size,
378 bool darkened,
379 const ImageCallback& callback) {
380 MRUKey tuple = MRUKey(page_url, size, darkened);
381 base::MRUCache<MRUKey, MRUValue>::iterator it = cache_->Get(tuple);
382 if (it != cache_->end()) {
383 callback.Run(it->second);
384 } else {
385 SalientImageForUrl(
386 page_url,
387 base::Bind(&ResizeImageInternal, pool_, size, darkened,
388 base::Bind(&BookmarkImageServiceIOS::ReturnAndCache,
389 base::Unretained(this), page_url, size, darkened,
390 callback)));
391 }
392 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698