Commit d023fe30 authored by Gauthier Ambard's avatar Gauthier Ambard Committed by Chromium LUCI CQ

[iOS] Extract JavaScript logic from ContextMenu

This CL extracts the JavaScript logic to find the DOM element on which
the user is long pressing and creating the ContextMenuParams out of it.

It is also renaming HTMLElementFetchRequest in
CRWHTMLElementFetchRequest to respect ios/web standard.

Bug: 1140387
Change-Id: I62e6e2df9be54f67706ff440354912b00d77337c
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2580073
Commit-Queue: Gauthier Ambard <gambard@chromium.org>
Reviewed-by: default avatarMike Dougherty <michaeldo@chromium.org>
Cr-Commit-Position: refs/heads/master@{#841459}
parent c29e41c1
...@@ -478,11 +478,11 @@ source_set("ios_web_web_state_ui_unittests") { ...@@ -478,11 +478,11 @@ source_set("ios_web_web_state_ui_unittests") {
] ]
sources = [ sources = [
"web_state/ui/crw_html_element_fetch_request_unittest.mm",
"web_state/ui/crw_web_controller_unittest.mm", "web_state/ui/crw_web_controller_unittest.mm",
"web_state/ui/crw_web_view_content_view_unittest.mm", "web_state/ui/crw_web_view_content_view_unittest.mm",
"web_state/ui/crw_web_view_proxy_impl_unittest.mm", "web_state/ui/crw_web_view_proxy_impl_unittest.mm",
"web_state/ui/crw_web_view_scroll_view_proxy_unittest.mm", "web_state/ui/crw_web_view_scroll_view_proxy_unittest.mm",
"web_state/ui/html_element_fetch_request_unittest.mm",
"web_state/ui/web_view_js_utils_unittest.mm", "web_state/ui/web_view_js_utils_unittest.mm",
"web_state/ui/wk_content_rule_list_util_unittest.mm", "web_state/ui/wk_content_rule_list_util_unittest.mm",
"web_state/ui/wk_web_view_configuration_provider_unittest.mm", "web_state/ui/wk_web_view_configuration_provider_unittest.mm",
...@@ -527,6 +527,7 @@ test("ios_web_inttests") { ...@@ -527,6 +527,7 @@ test("ios_web_inttests") {
":web", ":web",
"//base/test:test_support", "//base/test:test_support",
"//ios/net", "//ios/net",
"//ios/testing:block_swizzler",
"//ios/testing:embedded_test_server_support", "//ios/testing:embedded_test_server_support",
"//ios/testing:http_server_bundle_data", "//ios/testing:http_server_bundle_data",
"//ios/web:resources_grit", "//ios/web:resources_grit",
...@@ -554,6 +555,8 @@ test("ios_web_inttests") { ...@@ -554,6 +555,8 @@ test("ios_web_inttests") {
"//ios/web/test:test_constants", "//ios/web/test:test_constants",
"//ios/web/test:test_support", "//ios/web/test:test_support",
"//ios/web/web_state", "//ios/web/web_state",
"//ios/web/web_state:context_menu",
"//ios/web/web_state/ui:crw_context_menu_controller",
"//mojo/core/embedder", "//mojo/core/embedder",
"//net:test_support", "//net:test_support",
"//services/network/public/cpp", "//services/network/public/cpp",
...@@ -578,6 +581,7 @@ test("ios_web_inttests") { ...@@ -578,6 +581,7 @@ test("ios_web_inttests") {
"web_state/error_page_inttest.mm", "web_state/error_page_inttest.mm",
"web_state/http_auth_inttest.mm", "web_state/http_auth_inttest.mm",
"web_state/keep_render_process_alive_inttest.mm", "web_state/keep_render_process_alive_inttest.mm",
"web_state/ui/crw_context_menu_element_fetcher_inttest.mm",
"web_state/web_state_observer_inttest.mm", "web_state/web_state_observer_inttest.mm",
"webui/web_ui_inttest.mm", "webui/web_ui_inttest.mm",
"webui/web_ui_mojo_inttest.mm", "webui/web_ui_mojo_inttest.mm",
......
...@@ -11,10 +11,9 @@ ...@@ -11,10 +11,9 @@
namespace web { namespace web {
// Returns true if the |element| dictionary contains enough information to // Returns true if the |params| contain enough information to present a context
// present a context menu. (A valid url for either kContextMenuElementHyperlink // menu. (A valid url for either link_url or src_url must exist in the params.)
// or kContextMenuElementSource must exist in the dicitionary.) BOOL CanShowContextMenuForParams(const ContextMenuParams& params);
BOOL CanShowContextMenuForElementDictionary(NSDictionary* element);
// creates a ContextMenuParams from a NSDictionary representing an HTML element. // creates a ContextMenuParams from a NSDictionary representing an HTML element.
// The fields "href", "src", "title", "referrerPolicy" and "innerText" will // The fields "href", "src", "title", "referrerPolicy" and "innerText" will
......
...@@ -68,13 +68,11 @@ TitleAndOrigin GetContextMenuTitleAndOrigin(NSDictionary* element) { ...@@ -68,13 +68,11 @@ TitleAndOrigin GetContextMenuTitleAndOrigin(NSDictionary* element) {
namespace web { namespace web {
BOOL CanShowContextMenuForElementDictionary(NSDictionary* element) { BOOL CanShowContextMenuForParams(const ContextMenuParams& params) {
NSString* href = element[kContextMenuElementHyperlink]; if (params.link_url.is_valid()) {
if (GURL(base::SysNSStringToUTF8(href)).is_valid()) {
return YES; return YES;
} }
NSString* src = element[kContextMenuElementSource]; if (params.src_url.is_valid()) {
if (GURL(base::SysNSStringToUTF8(src)).is_valid()) {
return YES; return YES;
} }
return NO; return NO;
......
...@@ -113,55 +113,49 @@ TEST_F(ContextMenuParamsUtilsTest, DictionaryConstructorTestDataTitle) { ...@@ -113,55 +113,49 @@ TEST_F(ContextMenuParamsUtilsTest, DictionaryConstructorTestDataTitle) {
EXPECT_EQ(params.menu_title_origin, ContextMenuTitleOrigin::kURL); EXPECT_EQ(params.menu_title_origin, ContextMenuTitleOrigin::kURL);
} }
// Tests that a context menu will not be shown for an empty element dictionary. // Tests that a context menu will not be shown for empty params.
TEST_F(ContextMenuParamsUtilsTest, CanShowContextMenuTestEmptyDictionary) { TEST_F(ContextMenuParamsUtilsTest, CanShowContextMenuTestEmptyDictionary) {
EXPECT_FALSE(CanShowContextMenuForElementDictionary(@{})); EXPECT_FALSE(CanShowContextMenuForParams(ContextMenuParams()));
}
// Tests that a context menu will not be shown for an element dictionary with
// only a request id.
TEST_F(ContextMenuParamsUtilsTest, CanShowContextMenuTestRequestIdOnly) {
EXPECT_FALSE(CanShowContextMenuForElementDictionary(
@{kContextMenuElementRequestId : @"kContextMenuElementRequestId"}));
} }
// Tests that a context menu will be shown for a link. // Tests that a context menu will be shown for a link.
TEST_F(ContextMenuParamsUtilsTest, CanShowContextMenuTestHyperlink) { TEST_F(ContextMenuParamsUtilsTest, CanShowContextMenuTestHyperlink) {
EXPECT_TRUE(CanShowContextMenuForElementDictionary(@{ ContextMenuParams params;
kContextMenuElementHyperlink : @"http://example.com", params.link_url = GURL("http://example.com");
kContextMenuElementInnerText : @"Click me." params.link_text = @"Click me.";
})); EXPECT_TRUE(CanShowContextMenuForParams(params));
} }
// Tests that a context menu will not be shown for an invalid link. // Tests that a context menu will not be shown for an invalid link.
TEST_F(ContextMenuParamsUtilsTest, CanShowContextMenuTestInvalidHyperlink) { TEST_F(ContextMenuParamsUtilsTest, CanShowContextMenuTestInvalidHyperlink) {
EXPECT_FALSE(CanShowContextMenuForElementDictionary( ContextMenuParams params;
@{kContextMenuElementHyperlink : @"invalid_url"})); params.link_url = GURL("invalid_url");
EXPECT_FALSE(CanShowContextMenuForParams(params));
} }
// Tests that a context menu will be shown for an image. // Tests that a context menu will be shown for an image.
TEST_F(ContextMenuParamsUtilsTest, CanShowContextMenuTestImageWithTitle) { TEST_F(ContextMenuParamsUtilsTest, CanShowContextMenuTestImageWithTitle) {
EXPECT_TRUE(CanShowContextMenuForElementDictionary(@{ ContextMenuParams params;
kContextMenuElementSource : @"http://example.com/image.jpeg", params.src_url = GURL("http://example.com/image.jpeg");
kContextMenuElementTitle : @"Image" params.menu_title = @"Image";
})); EXPECT_TRUE(CanShowContextMenuForParams(params));
} }
// Tests that a context menu will not be shown for an image with an invalid // Tests that a context menu will not be shown for an image with an invalid
// source url. // source url.
TEST_F(ContextMenuParamsUtilsTest, TEST_F(ContextMenuParamsUtilsTest,
CanShowContextMenuTestImageWithInvalidSource) { CanShowContextMenuTestImageWithInvalidSource) {
EXPECT_FALSE(CanShowContextMenuForElementDictionary(@{ ContextMenuParams params;
kContextMenuElementSource : @"invalid_url", params.src_url = GURL("invalid_url");
})); EXPECT_FALSE(CanShowContextMenuForParams(params));
} }
// Tests that a context menu will be shown for a linked image. // Tests that a context menu will be shown for a linked image.
TEST_F(ContextMenuParamsUtilsTest, CanShowContextMenuTestLinkedImage) { TEST_F(ContextMenuParamsUtilsTest, CanShowContextMenuTestLinkedImage) {
EXPECT_TRUE(CanShowContextMenuForElementDictionary(@{ ContextMenuParams params;
kContextMenuElementHyperlink : @"http://example.com", params.link_url = GURL("http://example.com");
kContextMenuElementSource : @"http://example.com/image.jpeg" params.src_url = GURL("http://example.com/image.jpeg");
})); EXPECT_TRUE(CanShowContextMenuForParams(params));
} }
// Tests that the menu title prepends the element's alt text if it is an image // Tests that the menu title prepends the element's alt text if it is an image
......
...@@ -104,10 +104,12 @@ source_set("crw_context_menu_controller") { ...@@ -104,10 +104,12 @@ source_set("crw_context_menu_controller") {
] ]
sources = [ sources = [
"crw_context_menu_element_fetcher.h",
"crw_context_menu_element_fetcher.mm",
"crw_html_element_fetch_request.h",
"crw_html_element_fetch_request.mm",
"crw_legacy_context_menu_controller.h", "crw_legacy_context_menu_controller.h",
"crw_legacy_context_menu_controller.mm", "crw_legacy_context_menu_controller.mm",
"html_element_fetch_request.h",
"html_element_fetch_request.mm",
] ]
configs += [ "//build/config/compiler:enable_arc" ] configs += [ "//build/config/compiler:enable_arc" ]
......
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef IOS_WEB_WEB_STATE_UI_CRW_CONTEXT_MENU_ELEMENT_FETCHER_H_
#define IOS_WEB_WEB_STATE_UI_CRW_CONTEXT_MENU_ELEMENT_FETCHER_H_
#import <Foundation/Foundation.h>
#import <WebKit/WebKit.h>
namespace web {
struct ContextMenuParams;
class WebState;
}
// Class handling the fetching information about DOM element in a specific
// position.
@interface CRWContextMenuElementFetcher : NSObject
- (instancetype)initWithWebView:(WKWebView*)webView
webState:(web::WebState*)webState;
// Asynchronously fetches information about DOM element for the given |point|
// (in the scroll view coordinates). |handler| can not be nil.
- (void)fetchDOMElementAtPoint:(CGPoint)point
completionHandler:(void (^)(const web::ContextMenuParams&))handler;
// Cancels all the fetches current in progress.
- (void)cancelFetches;
@end
#endif // IOS_WEB_WEB_STATE_UI_CRW_CONTEXT_MENU_ELEMENT_FETCHER_H_
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import "ios/web/web_state/ui/crw_context_menu_element_fetcher.h"
#include "base/strings/sys_string_conversions.h"
#include "base/unguessable_token.h"
#include "base/values.h"
#import "ios/web/js_messaging/crw_wk_script_message_router.h"
#import "ios/web/public/js_messaging/web_frame.h"
#import "ios/web/public/js_messaging/web_frame_util.h"
#import "ios/web/public/ui/context_menu_params.h"
#import "ios/web/public/web_state.h"
#import "ios/web/public/web_state_observer_bridge.h"
#import "ios/web/web_state/context_menu_constants.h"
#import "ios/web/web_state/context_menu_params_utils.h"
#import "ios/web/web_state/ui/crw_html_element_fetch_request.h"
#import "ios/web/web_state/ui/wk_web_view_configuration_provider.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
namespace {
// Javascript function name to obtain element details at a point.
const char kFindElementAtPointFunctionName[] = "findElementAtPoint";
// JavaScript message handler name installed in WKWebView for found element
// response.
NSString* const kFindElementResultHandlerName = @"FindElementResultHandler";
} // namespace
@interface CRWContextMenuElementFetcher () <CRWWebStateObserver> {
std::unique_ptr<web::WebStateObserverBridge> _observer;
}
@property(nonatomic, readonly, weak) WKWebView* webView;
@property(nonatomic, assign) web::WebState* webState;
// Details for currently in progress element fetches. The objects are
// instances of CRWHTMLElementFetchRequest and are keyed by a unique requestId
// string.
@property(nonatomic, strong) NSMutableDictionary* pendingElementFetchRequests;
@end
@implementation CRWContextMenuElementFetcher
- (instancetype)initWithWebView:(WKWebView*)webView
webState:(web::WebState*)webState {
self = [super init];
if (self) {
_pendingElementFetchRequests = [[NSMutableDictionary alloc] init];
_webView = webView;
_webState = webState;
_observer = std::make_unique<web::WebStateObserverBridge>(self);
webState->AddObserver(_observer.get());
// Listen for fetched element response.
web::WKWebViewConfigurationProvider& configurationProvider =
web::WKWebViewConfigurationProvider::FromBrowserState(
webState->GetBrowserState());
CRWWKScriptMessageRouter* messageRouter =
configurationProvider.GetScriptMessageRouter();
__weak __typeof(self) weakSelf = self;
[messageRouter
setScriptMessageHandler:^(WKScriptMessage* message) {
[weakSelf didReceiveScriptMessage:message];
}
name:kFindElementResultHandlerName
webView:webView];
}
return self;
}
- (void)dealloc {
if (self.webState)
self.webState->RemoveObserver(_observer.get());
}
- (void)fetchDOMElementAtPoint:(CGPoint)point
completionHandler:
(void (^)(const web::ContextMenuParams&))handler {
if (!self.webState) {
return;
}
web::WebFrame* frame = GetMainFrame(self.webState);
if (!frame) {
// A WebFrame may not exist for certain types of content, like PDFs.
return;
}
DCHECK(handler);
std::string requestID = base::UnguessableToken::Create().ToString();
CRWHTMLElementFetchRequest* fetchRequest =
[[CRWHTMLElementFetchRequest alloc] initWithFoundElementHandler:handler];
_pendingElementFetchRequests[base::SysUTF8ToNSString(requestID)] =
fetchRequest;
CGSize webViewContentSize = self.webView.scrollView.contentSize;
std::vector<base::Value> args;
args.push_back(base::Value(requestID));
args.push_back(base::Value(point.x));
args.push_back(base::Value(point.y));
args.push_back(base::Value(webViewContentSize.width));
args.push_back(base::Value(webViewContentSize.height));
frame->CallJavaScriptFunction(std::string(kFindElementAtPointFunctionName),
args);
}
- (void)cancelFetches {
for (CRWHTMLElementFetchRequest* fetchRequest in _pendingElementFetchRequests
.allValues) {
[fetchRequest invalidate];
}
}
#pragma mark - Private
// Called when web controller receives a new message from the web page.
- (void)didReceiveScriptMessage:(WKScriptMessage*)message {
NSMutableDictionary* response =
[[NSMutableDictionary alloc] initWithDictionary:message.body];
NSString* requestID = response[web::kContextMenuElementRequestId];
CRWHTMLElementFetchRequest* fetchRequest =
_pendingElementFetchRequests[requestID];
if (!fetchRequest) {
// Do not process the message if a fetch request with a matching |requestID|
// was not found. This ensures that the response matches a request made by
// this instance.
return;
}
web::ContextMenuParams params =
web::ContextMenuParamsFromElementDictionary(response);
params.is_main_frame = message.frameInfo.mainFrame;
params.view = self.webView;
[_pendingElementFetchRequests removeObjectForKey:requestID];
[fetchRequest runHandlerWithResponse:params];
}
#pragma mark - CRWWebStateObserver
- (void)webStateDestroyed:(web::WebState*)webState {
if (self.webState)
self.webState->RemoveObserver(_observer.get());
self.webState = nullptr;
}
@end
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import "ios/web/web_state/ui/crw_context_menu_element_fetcher.h"
#import <WebKit/WebKit.h>
#include "base/macros.h"
#import "base/test/ios/wait_util.h"
#include "ios/testing/scoped_block_swizzler.h"
#import "ios/web/public/test/web_view_content_test_util.h"
#import "ios/web/test/web_test_with_web_controller.h"
#import "ios/web/web_state/context_menu_constants.h"
#import "ios/web/web_state/ui/crw_legacy_context_menu_controller.h"
#import "ios/web/web_state/ui/crw_web_controller.h"
#import "ios/web/web_state/web_state_impl.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/gtest_mac.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
namespace {
// This is the timeout used while waiting for the JavaScript to complete. The
// general kWaitForJSCompletionTimeout isn't used because one of the test is
// supposed to not complete and so will wait for the whole duration of the
// timeout. This constant is smaller to speed tests up. This constant is used in
// both the "successful" JavaScript calls and the "failing" JavaScript calls. It
// ensures that in the context of this test, the JavaScript completes in the
// given timespan (and so it ensures that if the "failing" JavaScript tests
// pass, it is because the JavaScript isn't called and not because it didn't
// have time to complete).
const CGFloat kFetcherJSTimeout = 1.0;
} // namespace
namespace web {
class CRWContextMenuElementFetcherTest : public WebTestWithWebController {
public:
CRWContextMenuElementFetcherTest() {
// Disable the existing long press handling to avoid duplicating message
// handlers.
swizzler_ = std::make_unique<ScopedBlockSwizzler>(
[CRWLegacyContextMenuController class],
@selector(initWithWebView:webState:), ^id(id self) {
return nil;
});
}
void SetUp() override {
WebTestWithWebState::SetUp();
WKWebView* web_view = [web_controller() ensureWebViewCreated];
fetcher_ =
[[CRWContextMenuElementFetcher alloc] initWithWebView:web_view
webState:web_state()];
}
// Loads a page containing a link and waits until the link is present on the
// page, making sure that the HTML is correctly injected.
bool LoadHtmlPage() WARN_UNUSED_RESULT {
NSString* html =
@"<html><head>"
"<style>body { font-size:14em; }</style>"
"<meta name=\"viewport\" content=\"user-scalable=no, width=100\">"
"</head><body><p><a id=\"linkID\" "
"href=\"http://destination/\">link</a></p></body></html>";
LoadHtml(html);
bool element_present = test::WaitForWebViewContainingElement(
web_state(), [ElementSelector selectorWithElementID:"linkID"]);
if (element_present) {
// If the element is present, we still need a small delay to let all the
// scripts be injected in the page.
base::test::ios::SpinRunLoopWithMinDelay(
base::TimeDelta::FromSecondsD(0.5));
}
return element_present;
}
CRWContextMenuElementFetcher* GetFetcher() { return fetcher_; }
private:
std::unique_ptr<ScopedBlockSwizzler> swizzler_;
CRWContextMenuElementFetcher* fetcher_;
};
// Tests that the fetcher is triggering a callback for one element.
TEST_F(CRWContextMenuElementFetcherTest, FetchOneElement) {
EXPECT_TRUE(LoadHtmlPage());
CRWContextMenuElementFetcher* fetcher = GetFetcher();
__block bool callback_called = false;
[fetcher fetchDOMElementAtPoint:CGPointMake(10, 10)
completionHandler:^(const web::ContextMenuParams&) {
callback_called = true;
}];
EXPECT_TRUE(base::test::ios::WaitUntilConditionOrTimeout(kFetcherJSTimeout, ^{
return callback_called;
}));
}
// Tests that cancelled fetches don't trigger callback.
TEST_F(CRWContextMenuElementFetcherTest, CancelFetch) {
EXPECT_TRUE(LoadHtmlPage());
CRWContextMenuElementFetcher* fetcher = GetFetcher();
__block bool callback_called = false;
[fetcher fetchDOMElementAtPoint:CGPointMake(10, 10)
completionHandler:^(const web::ContextMenuParams&) {
callback_called = true;
}];
[fetcher cancelFetches];
// The callback should never be called.
EXPECT_FALSE(
base::test::ios::WaitUntilConditionOrTimeout(kFetcherJSTimeout, ^{
return callback_called;
}));
}
} // namespace web
...@@ -2,8 +2,8 @@ ...@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
#ifndef IOS_WEB_WEB_STATE_UI_HTML_ELEMENT_FETCH_REQUEST_H_ #ifndef IOS_WEB_WEB_STATE_UI_CRW_HTML_ELEMENT_FETCH_REQUEST_H_
#define IOS_WEB_WEB_STATE_UI_HTML_ELEMENT_FETCH_REQUEST_H_ #define IOS_WEB_WEB_STATE_UI_CRW_HTML_ELEMENT_FETCH_REQUEST_H_
#import <Foundation/Foundation.h> #import <Foundation/Foundation.h>
...@@ -11,8 +11,12 @@ namespace base { ...@@ -11,8 +11,12 @@ namespace base {
class TimeTicks; class TimeTicks;
} // namespace base } // namespace base
namespace web {
struct ContextMenuParams;
}
// Tracks request details for fetching attributes of an element. // Tracks request details for fetching attributes of an element.
@interface HTMLElementFetchRequest : NSObject @interface CRWHTMLElementFetchRequest : NSObject
// The time this object was created. // The time this object was created.
@property(nonatomic, readonly) base::TimeTicks creationTime; @property(nonatomic, readonly) base::TimeTicks creationTime;
...@@ -21,16 +25,17 @@ class TimeTicks; ...@@ -21,16 +25,17 @@ class TimeTicks;
// Designated initializer to create a new object with the given completion // Designated initializer to create a new object with the given completion
// handler |foundElementHandler|. // handler |foundElementHandler|.
- (instancetype)initWithFoundElementHandler: - (instancetype)initWithFoundElementHandler:
(void (^)(NSDictionary*))foundElementHandler NS_DESIGNATED_INITIALIZER; (void (^)(const web::ContextMenuParams&))foundElementHandler
NS_DESIGNATED_INITIALIZER;
// Calls the |foundElementHandler| from the receiver's initializer with // Calls the |foundElementHandler| from the receiver's initializer with
// |response| as the parameter. This method has no effect if |invalidate| has // |response| as the parameter. This method has no effect if |invalidate| has
// been called. // been called.
- (void)runHandlerWithResponse:(NSDictionary*)response; - (void)runHandlerWithResponse:(const web::ContextMenuParams&)response;
// Removes the stored |foundElementHandler| from the receiver's initializer. // Removes the stored |foundElementHandler| from the receiver's initializer.
// |runHandlerWithResponse:| will have no effect if called after |invalidate|. // |runHandlerWithResponse:| will have no effect if called after |invalidate|.
- (void)invalidate; - (void)invalidate;
@end @end
#endif // IOS_WEB_WEB_STATE_UI_HTML_ELEMENT_FETCH_REQUEST_H_ #endif // IOS_WEB_WEB_STATE_UI_CRW_HTML_ELEMENT_FETCH_REQUEST_H_
...@@ -2,26 +2,28 @@ ...@@ -2,26 +2,28 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
#import "ios/web/web_state/ui/html_element_fetch_request.h" #import "ios/web/web_state/ui/crw_html_element_fetch_request.h"
#include "base/time/time.h" #include "base/time/time.h"
#import "ios/web/public/ui/context_menu_params.h"
#if !defined(__has_feature) || !__has_feature(objc_arc) #if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support." #error "This file requires ARC support."
#endif #endif
@interface HTMLElementFetchRequest () @interface CRWHTMLElementFetchRequest ()
// Completion handler to call with found DOM element. // Completion handler to call with found DOM element.
@property(nonatomic, copy) void (^foundElementHandler)(NSDictionary*); @property(nonatomic, copy) void (^foundElementHandler)
(const web::ContextMenuParams&);
@end @end
@implementation HTMLElementFetchRequest @implementation CRWHTMLElementFetchRequest
@synthesize creationTime = _creationTime; @synthesize creationTime = _creationTime;
@synthesize foundElementHandler = _foundElementHandler; @synthesize foundElementHandler = _foundElementHandler;
- (instancetype)initWithFoundElementHandler: - (instancetype)initWithFoundElementHandler:
(void (^)(NSDictionary*))foundElementHandler { (void (^)(const web::ContextMenuParams&))foundElementHandler {
self = [super init]; self = [super init];
if (self) { if (self) {
_creationTime = base::TimeTicks::Now(); _creationTime = base::TimeTicks::Now();
...@@ -30,7 +32,7 @@ ...@@ -30,7 +32,7 @@
return self; return self;
} }
- (void)runHandlerWithResponse:(NSDictionary*)response { - (void)runHandlerWithResponse:(const web::ContextMenuParams&)response {
if (_foundElementHandler) { if (_foundElementHandler) {
_foundElementHandler(response); _foundElementHandler(response);
} }
......
...@@ -2,9 +2,10 @@ ...@@ -2,9 +2,10 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
#import "ios/web/web_state/ui/html_element_fetch_request.h" #import "ios/web/web_state/ui/crw_html_element_fetch_request.h"
#include "base/time/time.h" #include "base/time/time.h"
#import "ios/web/public/ui/context_menu_params.h"
#import "ios/web/web_state/context_menu_constants.h" #import "ios/web/web_state/context_menu_constants.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
#include "testing/gtest_mac.h" #include "testing/gtest_mac.h"
...@@ -16,12 +17,13 @@ ...@@ -16,12 +17,13 @@
namespace web { namespace web {
using HtmlElementFetchRequestTest = PlatformTest; using CRWHTMLElementFetchRequestTest = PlatformTest;
// Tests that |creationTime| is set at HtmlElementFetchRequest object creation. // Tests that |creationTime| is set at CRWHTMLElementFetchRequest object
TEST_F(HtmlElementFetchRequestTest, CreationTime) { // creation.
HTMLElementFetchRequest* request = TEST_F(CRWHTMLElementFetchRequestTest, CreationTime) {
[[HTMLElementFetchRequest alloc] initWithFoundElementHandler:nil]; CRWHTMLElementFetchRequest* request =
[[CRWHTMLElementFetchRequest alloc] initWithFoundElementHandler:nil];
base::TimeDelta delta = base::TimeTicks::Now() - request.creationTime; base::TimeDelta delta = base::TimeTicks::Now() - request.creationTime;
// Validate that |request.creationTime| is "now", but only use second // Validate that |request.creationTime| is "now", but only use second
// precision to avoid performance induced test flake. // precision to avoid performance induced test flake.
...@@ -30,32 +32,35 @@ TEST_F(HtmlElementFetchRequestTest, CreationTime) { ...@@ -30,32 +32,35 @@ TEST_F(HtmlElementFetchRequestTest, CreationTime) {
// Tests that |runHandlerWithResponse:| runs the handler from the object's // Tests that |runHandlerWithResponse:| runs the handler from the object's
// initializer with the expected |response|. // initializer with the expected |response|.
TEST_F(HtmlElementFetchRequestTest, RunHandler) { TEST_F(CRWHTMLElementFetchRequestTest, RunHandler) {
__block bool handler_called = false; __block bool handler_called = false;
__block NSDictionary* received_response = nil; __block web::ContextMenuParams received_params;
void (^handler)(NSDictionary*) = ^(NSDictionary* response) { void (^handler)(const web::ContextMenuParams&) =
handler_called = true; ^(const web::ContextMenuParams& params) {
received_response = response; handler_called = true;
}; received_params = params;
HTMLElementFetchRequest* request = };
[[HTMLElementFetchRequest alloc] initWithFoundElementHandler:handler]; CRWHTMLElementFetchRequest* request =
NSDictionary* response = @{kContextMenuElementInnerText : @"text"}; [[CRWHTMLElementFetchRequest alloc] initWithFoundElementHandler:handler];
[request runHandlerWithResponse:response]; web::ContextMenuParams params = web::ContextMenuParams();
params.link_text = @"text";
[request runHandlerWithResponse:params];
EXPECT_TRUE(handler_called); EXPECT_TRUE(handler_called);
EXPECT_NSEQ(response, received_response); EXPECT_NSEQ(params.link_text, received_params.link_text);
} }
// Tests that |runHandlerWithResponse:| does not run the handler from the // Tests that |runHandlerWithResponse:| does not run the handler from the
// object's initializer if |invalidate| has been called. // object's initializer if |invalidate| has been called.
TEST_F(HtmlElementFetchRequestTest, Invalidate) { TEST_F(CRWHTMLElementFetchRequestTest, Invalidate) {
__block bool handler_called = false; __block bool handler_called = false;
void (^handler)(NSDictionary*) = ^(NSDictionary* response) { void (^handler)(const web::ContextMenuParams&) =
handler_called = true; ^(const web::ContextMenuParams& params) {
}; handler_called = true;
HTMLElementFetchRequest* request = };
[[HTMLElementFetchRequest alloc] initWithFoundElementHandler:handler]; CRWHTMLElementFetchRequest* request =
[[CRWHTMLElementFetchRequest alloc] initWithFoundElementHandler:handler];
[request invalidate]; [request invalidate];
[request runHandlerWithResponse:nil]; [request runHandlerWithResponse:web::ContextMenuParams()];
EXPECT_FALSE(handler_called); EXPECT_FALSE(handler_called);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment