Skip to content

Commit 378627d

Browse files
Yi SuCommit Bot
Yi Su
authored and
Commit Bot
committed
Add image_fetcher::IOSImageDataFetcherWrapper to ImageFetchTabHelper
ImageFetchTabHelper will be able to get image data by both JavaScript and image_fetcher::IOSImageDataFetcherWrapper. The image_fetcher ivar of BVC can be removed once this is completed. Bug: 163201 Cq-Include-Trybots: luci.chromium.try:ios-simulator-full-configs;master.tryserver.chromium.mac:ios-simulator-cronet Change-Id: Ie5bb9279c5fb19498d3cde4c37cdf8944baa9e98 Reviewed-on: https://chromium-review.googlesource.com/1181571 Reviewed-by: Matt Menke <[email protected]> Reviewed-by: Eugene But <[email protected]> Commit-Queue: Yi Su <[email protected]> Cr-Commit-Position: refs/heads/master@{#586032}
1 parent b3c874a commit 378627d

12 files changed

+297
-126
lines changed

ios/DEPS

+1
Original file line numberDiff line numberDiff line change
@@ -21,5 +21,6 @@ include_rules = [
2121
specific_include_rules = {
2222
".*test\.mm": [
2323
"+services/network/public/cpp",
24+
"+services/network/test",
2425
],
2526
}

ios/chrome/browser/web/BUILD.gn

+2
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ source_set("web") {
4141
":tab_helper_delegates",
4242
"//base",
4343
"//components/error_page/common",
44+
"//components/image_fetcher/ios",
4445
"//components/resources:components_resources_grit",
4546
"//components/strings",
4647
"//ios/chrome/app/strings:ios_strings_grit",
@@ -119,6 +120,7 @@ source_set("unit_tests") {
119120
"//ios/web/public/test/fakes",
120121
"//ios/web/public/test/http_server",
121122
"//net:test_support",
123+
"//services/network:test_support",
122124
"//testing/gmock",
123125
"//testing/gtest",
124126
"//third_party/ocmock",

ios/chrome/browser/web/image_fetch_tab_helper.h

+38-20
Original file line numberDiff line numberDiff line change
@@ -12,31 +12,28 @@
1212
#include "ios/web/public/web_state/web_state_observer.h"
1313
#import "ios/web/public/web_state/web_state_user_data.h"
1414

15-
// Gets the image data in binary string by calling injected JavaScript.
16-
// Never keep a reference to this class, instead get it by
17-
// ImageFetchTabHelper::FromWebState everytime.
15+
// Gets the image data by JavaScript or
16+
// image_fetcher::IOSImageDataFetcherWrapper. Always use this class by
17+
// ImageFetchTabHelper::FromWebState on UI thread. All callbacks will also be
18+
// invoked on UI thread.
1819
class ImageFetchTabHelper : public web::WebStateObserver,
1920
public web::WebStateUserData<ImageFetchTabHelper> {
2021
public:
2122
~ImageFetchTabHelper() override;
2223

23-
// Callback for GetImageData. |data| will be in binary format, or nullptr if
24+
// Callback for GetImageData. |data| will be in binary format, or nil if
2425
// GetImageData failed.
25-
typedef base::OnceCallback<void(const std::string* data)> ImageDataCallback;
26+
typedef void (^ImageDataCallback)(NSData* data);
2627

27-
// Gets image data as binary string by trying 2 methods in following order:
28-
// 1. Draw <img> to <canvas> and export its data;
29-
// 2. Download the image by XMLHttpRequest and hopefully get responsed from
30-
// cache.
31-
// This method should only be called from UI thread, and |url| should be equal
32-
// to the resolved "src" attribute of <img>, otherwise the method 1 would
33-
// fail. |callback| will be called on UI thread. If the JavaScript does not
34-
// response after |timeout|, the |callback| will be invoked with nullptr.
28+
// Gets image data in binary format by following steps:
29+
// 1. Call injected JavaScript to get the image data from web page;
30+
// 2. If JavaScript fails or does not send a message back in 300ms, try
31+
// downloading the image by image_fetcher::IOSImageDataFetcherWrapper.
3532
void GetImageData(const GURL& url,
36-
base::TimeDelta timeout,
37-
ImageDataCallback&& callback);
33+
const web::Referrer& referrer,
34+
ImageDataCallback callback);
3835

39-
private:
36+
protected:
4037
friend class web::WebStateUserData<ImageFetchTabHelper>;
4138

4239
explicit ImageFetchTabHelper(web::WebState* web_state);
@@ -46,17 +43,38 @@ class ImageFetchTabHelper : public web::WebStateObserver,
4643
web::NavigationContext* navigation_context) override;
4744
void WebStateDestroyed(web::WebState* web_state) override;
4845

46+
// Callback for GetImageDataByJs. |data| will be in binary format, or nullptr
47+
// if GetImageDataByJs failed.
48+
typedef base::OnceCallback<void(const std::string* data)> JsCallback;
49+
50+
// Gets image data in binary format by trying 2 JavaScript methods in order:
51+
// 1. Draw <img> to <canvas> and export its data;
52+
// 2. Download the image by XMLHttpRequest and hopefully get responded from
53+
// cache.
54+
// |url| should be equal to the resolved "src" attribute of <img>, otherwise
55+
// the method 1 would fail. If the JavaScript does not respond after
56+
// |timeout|, the |callback| will be invoked with nullptr.
57+
void GetImageDataByJs(const GURL& url,
58+
base::TimeDelta timeout,
59+
JsCallback&& callback);
60+
4961
// Handler for messages sent back from injected JavaScript.
50-
bool OnImageDataReceived(const base::DictionaryValue& message);
62+
bool OnJsMessage(const base::DictionaryValue& message);
63+
64+
// Handler for timeout on GetImageDataByJs.
65+
void OnJsTimeout(int call_id);
5166

52-
// Handler for timeout on GetImageData.
53-
void OnImageDataTimeout(int call_id);
67+
// Handler for calling GetImageDataByJs inside GetImageData.
68+
void JsCallbackOfGetImageData(const GURL& url,
69+
const web::Referrer& referrer,
70+
ImageDataCallback callback,
71+
const std::string* data);
5472

5573
// WebState this tab helper is attached to.
5674
web::WebState* web_state_ = nullptr;
5775

5876
// Store callbacks for GetImageData, with url as key.
59-
std::unordered_map<int, ImageDataCallback> callbacks_;
77+
std::unordered_map<int, JsCallback> js_callbacks_;
6078

6179
// |GetImageData| uses this counter as ID to match calls with callbacks. Each
6280
// call on |GetImageData| will increment |call_id_| by 1 and pass it as ID

ios/chrome/browser/web/image_fetch_tab_helper.mm

+80-18
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,12 @@
88
#include "base/strings/stringprintf.h"
99
#include "base/strings/utf_string_conversions.h"
1010
#include "base/values.h"
11+
#include "components/image_fetcher/ios/ios_image_data_fetcher_wrapper.h"
12+
#include "ios/web/public/browser_state.h"
13+
#include "ios/web/public/referrer_util.h"
1114
#import "ios/web/public/web_state/navigation_context.h"
1215
#include "ios/web/public/web_thread.h"
16+
#include "services/network/public/cpp/shared_url_loader_factory.h"
1317

1418
#if !defined(__has_feature) || !__has_feature(objc_arc)
1519
#error "This file requires ARC support."
@@ -20,6 +24,35 @@
2024
namespace {
2125
// Command prefix for injected JavaScript.
2226
const char kCommandPrefix[] = "imageFetch";
27+
// Key for image_fetcher
28+
const char kImageFetcherKeyName[] = "0";
29+
30+
// Wrapper class for image_fetcher::IOSImageDataFetcherWrapper. ImageFetcher is
31+
// attached to web::BrowserState instead of web::WebState, because if a user
32+
// closes the tab immediately after Copy/Save image, the web::WebState will be
33+
// destroyed thus fail the download.
34+
class ImageFetcher : public image_fetcher::IOSImageDataFetcherWrapper,
35+
public base::SupportsUserData::Data {
36+
public:
37+
~ImageFetcher() override = default;
38+
39+
ImageFetcher(
40+
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory)
41+
: image_fetcher::IOSImageDataFetcherWrapper(url_loader_factory) {}
42+
43+
static ImageFetcher* FromBrowserState(web::BrowserState* browser_state) {
44+
if (!browser_state->GetUserData(&kImageFetcherKeyName)) {
45+
browser_state->SetUserData(
46+
&kImageFetcherKeyName,
47+
std::make_unique<ImageFetcher>(
48+
browser_state->GetSharedURLLoaderFactory()));
49+
}
50+
return static_cast<ImageFetcher*>(
51+
browser_state->GetUserData(&kImageFetcherKeyName));
52+
}
53+
54+
DISALLOW_COPY_AND_ASSIGN(ImageFetcher);
55+
};
2356
}
2457

2558
ImageFetchTabHelper::ImageFetchTabHelper(web::WebState* web_state)
@@ -32,7 +65,7 @@
3265
[](base::WeakPtr<ImageFetchTabHelper> ptr,
3366
const base::DictionaryValue& message, const GURL& page_url,
3467
bool has_user_gesture, bool form_in_main_frame) {
35-
return ptr ? ptr->OnImageDataReceived(message) : true;
68+
return ptr ? ptr->OnJsMessage(message) : true;
3669
},
3770
weak_ptr_factory_.GetWeakPtr()),
3871
kCommandPrefix);
@@ -46,29 +79,59 @@
4679
if (navigation_context->IsSameDocument()) {
4780
return;
4881
}
49-
for (auto&& pair : callbacks_)
82+
for (auto&& pair : js_callbacks_)
5083
std::move(pair.second).Run(nullptr);
51-
callbacks_.clear();
84+
js_callbacks_.clear();
5285
}
5386

5487
void ImageFetchTabHelper::WebStateDestroyed(web::WebState* web_state) {
5588
web_state->RemoveScriptCommandCallback(kCommandPrefix);
56-
for (auto&& pair : callbacks_)
89+
for (auto&& pair : js_callbacks_)
5790
std::move(pair.second).Run(nullptr);
5891
web_state->RemoveObserver(this);
5992
web_state_ = nullptr;
6093
}
6194

6295
void ImageFetchTabHelper::GetImageData(const GURL& url,
63-
base::TimeDelta timeout,
64-
ImageDataCallback&& callback) {
96+
const web::Referrer& referrer,
97+
ImageDataCallback callback) {
98+
// |this| is captured into the callback of GetImageDataByJs, which will always
99+
// be invoked before the |this| is destroyed, so it's safe.
100+
GetImageDataByJs(
101+
url, base::TimeDelta::FromMilliseconds(300),
102+
base::BindOnce(&ImageFetchTabHelper::JsCallbackOfGetImageData,
103+
base::Unretained(this), url, referrer, callback));
104+
}
105+
106+
void ImageFetchTabHelper::JsCallbackOfGetImageData(
107+
const GURL& url,
108+
const web::Referrer& referrer,
109+
ImageDataCallback callback,
110+
const std::string* data) {
111+
if (data) {
112+
callback([NSData dataWithBytes:data->c_str() length:data->size()]);
113+
return;
114+
}
115+
ImageFetcher::FromBrowserState(web_state_->GetBrowserState())
116+
->FetchImageDataWebpDecoded(
117+
url,
118+
^(NSData* data, const image_fetcher::RequestMetadata& metadata) {
119+
callback(data);
120+
},
121+
web::ReferrerHeaderValueForNavigation(url, referrer),
122+
web::PolicyForNavigation(url, referrer), /*send_cookies=*/true);
123+
}
124+
125+
void ImageFetchTabHelper::GetImageDataByJs(const GURL& url,
126+
base::TimeDelta timeout,
127+
JsCallback&& callback) {
65128
++call_id_;
66-
DCHECK_EQ(callbacks_.count(call_id_), 0UL);
67-
callbacks_.insert({call_id_, std::move(callback)});
129+
DCHECK_EQ(js_callbacks_.count(call_id_), 0UL);
130+
js_callbacks_.insert({call_id_, std::move(callback)});
68131

69132
web::WebThread::PostDelayedTask(
70133
web::WebThread::UI, FROM_HERE,
71-
base::BindRepeating(&ImageFetchTabHelper::OnImageDataTimeout,
134+
base::BindRepeating(&ImageFetchTabHelper::OnJsTimeout,
72135
weak_ptr_factory_.GetWeakPtr(), call_id_),
73136
timeout);
74137

@@ -89,18 +152,17 @@
89152
// For failure:
90153
// {'command': 'image.getImageData',
91154
// 'id': id_sent_to_gCrWeb_image_getImageData}
92-
bool ImageFetchTabHelper::OnImageDataReceived(
93-
const base::DictionaryValue& message) {
155+
bool ImageFetchTabHelper::OnJsMessage(const base::DictionaryValue& message) {
94156
const base::Value* id_key = message.FindKey("id");
95157
if (!id_key || !id_key->is_double()) {
96158
return false;
97159
}
98160
int id_value = static_cast<int>(id_key->GetDouble());
99-
if (!callbacks_.count(id_value)) {
161+
if (!js_callbacks_.count(id_value)) {
100162
return true;
101163
}
102-
ImageDataCallback callback = std::move(callbacks_[id_value]);
103-
callbacks_.erase(id_value);
164+
JsCallback callback = std::move(js_callbacks_[id_value]);
165+
js_callbacks_.erase(id_value);
104166

105167
const base::Value* data = message.FindKey("data");
106168
std::string decoded_data;
@@ -113,10 +175,10 @@
113175
return true;
114176
}
115177

116-
void ImageFetchTabHelper::OnImageDataTimeout(int call_id) {
117-
if (callbacks_.count(call_id)) {
118-
ImageDataCallback callback = std::move(callbacks_[call_id]);
119-
callbacks_.erase(call_id);
178+
void ImageFetchTabHelper::OnJsTimeout(int call_id) {
179+
if (js_callbacks_.count(call_id)) {
180+
JsCallback callback = std::move(js_callbacks_[call_id]);
181+
js_callbacks_.erase(call_id);
120182
std::move(callback).Run(nullptr);
121183
}
122184
}

0 commit comments

Comments
 (0)