Commit dc2dbcbd authored by Mike Dougherty's avatar Mike Dougherty Committed by Commit Bot

Enable post message context menu and remove flag.

This fully enables support for context menu in iFrames.

Bug: 873660, 873662
Cq-Include-Trybots: luci.chromium.try:ios-simulator-full-configs;master.tryserver.chromium.mac:ios-simulator-cronet
Change-Id: I3345f56f6812ef619c6ed5b7bcabd34e09de410f
Reviewed-on: https://chromium-review.googlesource.com/1174052
Commit-Queue: Mike Dougherty <michaeldo@chromium.org>
Reviewed-by: default avatarEugene But <eugenebut@chromium.org>
Cr-Commit-Position: refs/heads/master@{#583053}
parent 7755fb6c
......@@ -274,11 +274,6 @@ const flags_ui::FeatureEntry kFeatureEntries[] = {
{"infobars-ui-reboot", flag_descriptions::kInfobarsUIRebootName,
flag_descriptions::kInfobarsUIRebootDescription, flags_ui::kOsIos,
FEATURE_VALUE_TYPE(kInfobarsUIReboot)},
{"context-menu-element-post-message",
flag_descriptions::kContextMenuElementPostMessageName,
flag_descriptions::kContextMenuElementPostMessageDescription,
flags_ui::kOsIos,
FEATURE_VALUE_TYPE(web::features::kContextMenuElementPostMessage)},
{"mailto-handling-google-ui",
flag_descriptions::kMailtoHandlingWithGoogleUIName,
flag_descriptions::kMailtoHandlingWithGoogleUIDescription,
......
......@@ -7,7 +7,6 @@
#import <XCTest/XCTest.h>
#import "base/test/ios/wait_util.h"
#include "base/test/scoped_feature_list.h"
#include "ios/chrome/browser/ui/ui_util.h"
#include "ios/chrome/grit/ios_strings.h"
#import "ios/chrome/test/app/chrome_test_util.h"
......@@ -21,7 +20,6 @@
#import "ios/chrome/test/earl_grey/chrome_matchers.h"
#import "ios/chrome/test/earl_grey/chrome_test_case.h"
#import "ios/testing/earl_grey/disabled_test_macros.h"
#import "ios/web/public/features.h"
#import "ios/web/public/test/earl_grey/web_view_matchers.h"
#include "ios/web/public/test/element_selector.h"
#include "net/test/embedded_test_server/http_request.h"
......@@ -183,33 +181,24 @@ void SelectTabAtIndexInCurrentMode(NSUInteger index) {
}
// Tests that selecting "Open Image" from the context menu properly opens the
// image in the current tab. (With the kContextMenuElementPostMessage feature
// disabled.)
// image in the current tab.
- (void)testOpenImageInCurrentTabFromContextMenu {
base::test::ScopedFeatureList scopedFeatureList;
scopedFeatureList.InitAndDisableFeature(
web::features::kContextMenuElementPostMessage);
const GURL pageURL = self.testServer->GetURL(kLogoPagePath);
[ChromeEarlGrey loadURL:pageURL];
[ChromeEarlGrey waitForWebViewContainingText:kLogoPageText];
LongPressElement(kLogoPageChromiumImageId);
TapOnContextMenuButton(OpenImageButton());
[ChromeEarlGrey waitForPageToFinishLoading];
// Verify url and tab count.
GREYAssertEqual(chrome_test_util::GetMainTabCount(), 1,
@"Image should be opened in existing tab.");
// Verify url.
const GURL imageURL = self.testServer->GetURL(kLogoPageImageSourcePath);
[[EarlGrey selectElementWithMatcher:OmniboxText(imageURL.GetContent())]
assertWithMatcher:grey_notNil()];
}
// Tests that selecting "Open Image in New Tab" from the context menu properly
// opens the image in a new background tab. (With the
// kContextMenuElementPostMessage feature disabled.)
// opens the image in a new background tab.
// TODO(crbug.com/817810): Enable this test.
#if TARGET_IPHONE_SIMULATOR
#define MAYBE_testOpenImageInNewTabFromContextMenu \
......@@ -219,10 +208,6 @@ void SelectTabAtIndexInCurrentMode(NSUInteger index) {
FLAKY_testOpenImageInNewTabFromContextMenu
#endif
- (void)MAYBE_testOpenImageInNewTabFromContextMenu {
base::test::ScopedFeatureList scopedFeatureList;
scopedFeatureList.InitAndDisableFeature(
web::features::kContextMenuElementPostMessage);
const GURL pageURL = self.testServer->GetURL(kLogoPagePath);
[ChromeEarlGrey loadURL:pageURL];
[ChromeEarlGrey waitForWebViewContainingText:kLogoPageText];
......@@ -240,8 +225,7 @@ void SelectTabAtIndexInCurrentMode(NSUInteger index) {
assertWithMatcher:grey_notNil()];
}
// Tests "Open in New Tab" on context menu. (With the
// kContextMenuElementPostMessage feature disabled.)
// Tests "Open in New Tab" on context menu.
// TODO(crbug.com/817810): Enable this test.
#if TARGET_IPHONE_SIMULATOR
#define MAYBE_testContextMenuOpenInNewTab testContextMenuOpenInNewTab
......@@ -249,10 +233,6 @@ void SelectTabAtIndexInCurrentMode(NSUInteger index) {
#define MAYBE_testContextMenuOpenInNewTab FLAKY_testContextMenuOpenInNewTab
#endif
- (void)MAYBE_testContextMenuOpenInNewTab {
base::test::ScopedFeatureList scopedFeatureList;
scopedFeatureList.InitAndDisableFeature(
web::features::kContextMenuElementPostMessage);
const GURL initialURL = self.testServer->GetURL(kInitialPageUrl);
[ChromeEarlGrey loadURL:initialURL];
[ChromeEarlGrey waitForWebViewContainingText:kInitialPageDestinationLinkText];
......@@ -270,8 +250,7 @@ void SelectTabAtIndexInCurrentMode(NSUInteger index) {
assertWithMatcher:grey_notNil()];
}
// Tests that the context menu is displayed for an image url. (With the
// kContextMenuElementPostMessage feature disabled.)
// Tests that the context menu is displayed for an image url.
// TODO(crbug.com/817810): Enable this test.
#if TARGET_IPHONE_SIMULATOR
#define MAYBE_testContextMenuDisplayedOnImage testContextMenuDisplayedOnImage
......@@ -280,10 +259,6 @@ void SelectTabAtIndexInCurrentMode(NSUInteger index) {
FLAKY_testContextMenuDisplayedOnImage
#endif
- (void)MAYBE_testContextMenuDisplayedOnImage {
base::test::ScopedFeatureList scopedFeatureList;
scopedFeatureList.InitAndDisableFeature(
web::features::kContextMenuElementPostMessage);
const GURL imageURL = self.testServer->GetURL(kLogoPageImageSourcePath);
[ChromeEarlGrey loadURL:imageURL];
......@@ -309,161 +284,8 @@ void SelectTabAtIndexInCurrentMode(NSUInteger index) {
assertWithMatcher:grey_notNil()];
}
// Tests that the element fetch duration is logged once with the
// kContextMenuElementPostMessage feature disabled.
// Tests that the element fetch duration is logged once.
- (void)testContextMenuElementFetchDurationMetric {
base::test::ScopedFeatureList scopedFeatureList;
scopedFeatureList.InitAndDisableFeature(
web::features::kContextMenuElementPostMessage);
chrome_test_util::HistogramTester histogramTester;
const GURL pageURL = self.testServer->GetURL(kLogoPagePath);
[ChromeEarlGrey loadURL:pageURL];
[ChromeEarlGrey waitForWebViewContainingText:kLogoPageText];
LongPressElement(kLogoPageChromiumImageId);
TapOnContextMenuButton(OpenImageButton());
[ChromeEarlGrey waitForPageToFinishLoading];
histogramTester.ExpectTotalCount("ContextMenu.DOMElementFetchDuration", 1,
^(NSString* error) {
GREYFail(error);
});
}
// Tests that selecting "Open Image" from the context menu properly opens the
// image in the current tab. (With the kContextMenuElementPostMessage feature
// enabled.)
- (void)testOpenImageInCurrentTabFromContextMenuPostMessage {
base::test::ScopedFeatureList scopedFeatureList;
scopedFeatureList.InitAndEnableFeature(
web::features::kContextMenuElementPostMessage);
const GURL pageURL = self.testServer->GetURL(kLogoPagePath);
[ChromeEarlGrey loadURL:pageURL];
[ChromeEarlGrey waitForWebViewContainingText:kLogoPageText];
LongPressElement(kLogoPageChromiumImageId);
TapOnContextMenuButton(OpenImageButton());
[ChromeEarlGrey waitForPageToFinishLoading];
// Verify url.
const GURL imageURL = self.testServer->GetURL(kLogoPageImageSourcePath);
[[EarlGrey selectElementWithMatcher:OmniboxText(imageURL.GetContent())]
assertWithMatcher:grey_notNil()];
}
// Tests that selecting "Open Image in New Tab" from the context menu properly
// opens the image in a new background tab. (With the
// kContextMenuElementPostMessage feature enabled.)
// TODO(crbug.com/817810): Enable this test.
#if TARGET_IPHONE_SIMULATOR
#define MAYBE_testOpenImageInNewTabFromContextMenuPostMessage \
testOpenImageInNewTabFromContextMenuPostMessage
#else
#define MAYBE_testOpenImageInNewTabFromContextMenuPostMessage \
FLAKY_testOpenImageInNewTabFromContextMenuPostMessage
#endif
- (void)MAYBE_testOpenImageInNewTabFromContextMenuPostMessage {
base::test::ScopedFeatureList scopedFeatureList;
scopedFeatureList.InitAndEnableFeature(
web::features::kContextMenuElementPostMessage);
const GURL pageURL = self.testServer->GetURL(kLogoPagePath);
[ChromeEarlGrey loadURL:pageURL];
[ChromeEarlGrey waitForWebViewContainingText:kLogoPageText];
LongPressElement(kLogoPageChromiumImageId);
TapOnContextMenuButton(OpenImageInNewTabButton());
[ChromeEarlGrey waitForMainTabCount:2];
SelectTabAtIndexInCurrentMode(1U);
[ChromeEarlGrey waitForPageToFinishLoading];
// Verify url.
const GURL imageURL = self.testServer->GetURL(kLogoPageImageSourcePath);
[[EarlGrey selectElementWithMatcher:OmniboxText(imageURL.GetContent())]
assertWithMatcher:grey_notNil()];
}
// Tests "Open in New Tab" on context menu. (With the
// kContextMenuElementPostMessage feature enabled.)
// TODO(crbug.com/817810): Enable this test.
#if TARGET_IPHONE_SIMULATOR
#define MAYBE_testContextMenuOpenInNewTabPostMessage \
testContextMenuOpenInNewTabPostMessage
#else
#define MAYBE_testContextMenuOpenInNewTabPostMessage \
FLAKY_testContextMenuOpenInNewTabPostMessage
#endif
- (void)MAYBE_testContextMenuOpenInNewTabPostMessage {
base::test::ScopedFeatureList scopedFeatureList;
scopedFeatureList.InitAndEnableFeature(
web::features::kContextMenuElementPostMessage);
const GURL initialURL = self.testServer->GetURL(kInitialPageUrl);
[ChromeEarlGrey loadURL:initialURL];
[ChromeEarlGrey waitForWebViewContainingText:kInitialPageDestinationLinkText];
LongPressElement(kInitialPageDestinationLinkId);
TapOnContextMenuButton(OpenLinkInNewTabButton());
[ChromeEarlGrey waitForMainTabCount:2];
SelectTabAtIndexInCurrentMode(1U);
[ChromeEarlGrey waitForWebViewContainingText:kDestinationPageText];
// Verify url.
const GURL destinationURL = self.testServer->GetURL(kDestinationPageUrl);
[[EarlGrey selectElementWithMatcher:OmniboxText(destinationURL.GetContent())]
assertWithMatcher:grey_notNil()];
}
// Tests that the context menu is displayed for an image url. (With the
// kContextMenuElementPostMessage feature enabled.)
// TODO(crbug.com/817810): Enable this test.
#if TARGET_IPHONE_SIMULATOR
#define MAYBE_testContextMenuDisplayedOnImagePostMessage \
testContextMenuDisplayedOnImagePostMessage
#else
#define MAYBE_testContextMenuDisplayedOnImagePostMessage \
FLAKY_testContextMenuDisplayedOnImagePostMessage
#endif
- (void)MAYBE_testContextMenuDisplayedOnImagePostMessage {
base::test::ScopedFeatureList scopedFeatureList;
scopedFeatureList.InitAndEnableFeature(
web::features::kContextMenuElementPostMessage);
const GURL imageURL = self.testServer->GetURL(kLogoPageImageSourcePath);
[ChromeEarlGrey loadURL:imageURL];
// Calculate a point inside the displayed image. Javascript can not be used to
// find the element because no DOM exists.
CGPoint point = CGPointMake(
CGRectGetMidX([chrome_test_util::GetActiveViewController() view].bounds),
20.0);
id<GREYMatcher> web_view_matcher =
web::WebViewInWebState(chrome_test_util::GetCurrentWebState());
[[EarlGrey selectElementWithMatcher:web_view_matcher]
performAction:grey_longPressAtPointWithDuration(
point, kGREYLongPressDefaultDuration)];
TapOnContextMenuButton(OpenImageInNewTabButton());
[ChromeEarlGrey waitForMainTabCount:2];
SelectTabAtIndexInCurrentMode(1U);
[ChromeEarlGrey waitForPageToFinishLoading];
// Verify url.
[[EarlGrey selectElementWithMatcher:OmniboxText(imageURL.GetContent())]
assertWithMatcher:grey_notNil()];
}
// Tests that the element fetch duration is logged once with the
// kContextMenuElementPostMessage feature enabled.
- (void)testContextMenuElementFetchDurationMetricPostMessage {
base::test::ScopedFeatureList scopedFeatureList;
scopedFeatureList.InitAndEnableFeature(
web::features::kContextMenuElementPostMessage);
chrome_test_util::HistogramTester histogramTester;
const GURL pageURL = self.testServer->GetURL(kLogoPagePath);
......@@ -481,11 +303,7 @@ void SelectTabAtIndexInCurrentMode(NSUInteger index) {
}
// Tests that system touches are cancelled when the context menu is shown.
// (While the kContextMenuElementPostMessage feature is enabled.)
- (void)testContextMenuCancelSystemTouchesMetricPostMessage {
base::test::ScopedFeatureList scopedFeatureList;
scopedFeatureList.InitAndEnableFeature(
web::features::kContextMenuElementPostMessage);
- (void)testContextMenuCancelSystemTouchesMetric {
chrome_test_util::HistogramTester histogramTester;
const GURL pageURL = self.testServer->GetURL(kLogoPagePath);
......@@ -505,10 +323,7 @@ void SelectTabAtIndexInCurrentMode(NSUInteger index) {
// Tests that the system selected text callout is displayed instead of the
// context menu when user long presses on plain text.
- (void)testContextMenuSelectedTextCalloutPostMessage {
base::test::ScopedFeatureList scopedFeatureList;
scopedFeatureList.InitAndEnableFeature(
web::features::kContextMenuElementPostMessage);
- (void)testContextMenuSelectedTextCallout {
chrome_test_util::HistogramTester histogramTester;
// Load the destination page directly because it has a plain text message on
......
......@@ -125,13 +125,6 @@ const char kContextualSearch[] = "Contextual Search";
const char kContextualSearchDescription[] =
"Whether or not Contextual Search is enabled.";
const char kContextMenuElementPostMessageName[] =
"Context Menu Element Post Message";
const char kContextMenuElementPostMessageDescription[] =
"When enabled, the DOM element for the Context Menu is returned using a "
"webkit postMessage call instead of directly returned from the JavaScript "
"function.";
const char kCopyImageName[] = "Copy Image";
const char kCopyImageDescription[] =
"Enable copying image to system pasteboard via context menu.";
......
......@@ -100,11 +100,6 @@ extern const char kCaptivePortalMetricsDescription[];
extern const char kContextualSearch[];
extern const char kContextualSearchDescription[];
// Title and description for the flag to enable returning the DOM element for
// context menu using webkit postMessage API.
extern const char kContextMenuElementPostMessageName[];
extern const char kContextMenuElementPostMessageDescription[];
// Title and description for the flag to enable copying image.
extern const char kCopyImageName[];
extern const char kCopyImageDescription[];
......
......@@ -7,9 +7,6 @@
namespace web {
namespace features {
const base::Feature kContextMenuElementPostMessage{
"ContextMenuElementPostMessage", base::FEATURE_DISABLED_BY_DEFAULT};
const base::Feature kWebFrameMessaging{"WebFrameMessaging",
base::FEATURE_DISABLED_BY_DEFAULT};
......
......@@ -10,9 +10,6 @@
namespace web {
namespace features {
// Used to enable asynchronous DOM element fetching for context menu.
extern const base::Feature kContextMenuElementPostMessage;
// Used to enable API to send messages directly to frames of a webpage.
extern const base::Feature kWebFrameMessaging;
......
......@@ -7,15 +7,15 @@
#import <Foundation/Foundation.h>
// Contains keys present in dictionary returned by __gCrWeb.getElementFromPoint
// and __gCrWeb.findElementAtPoint JS APIs.
// Contains keys present in dictionary created by __gCrWeb.findElementAtPoint
// to represent the DOM element.
namespace web {
// Required in findElementAtPoint response. (Not used by getElementFromPoint.)
// Represents a unique string request ID that is passed through directly from a
// call to findElementAtPoint to the response dictionary. The request ID should
// be used to correlate a response with a previous call to findElementAtPoint.
// Required key. Represents a unique string request ID that is passed through
// directly from a call to findElementAtPoint to the response dictionary. The
// request ID should be used to correlate a response with a previous call to
// findElementAtPoint.
extern NSString* const kContextMenuElementRequestId;
// Optional key. Represents element's href attribute if present or parent's href
......
......@@ -10,18 +10,13 @@
#import <WebKit/WebKit.h>
#include "base/macros.h"
#include "base/strings/stringprintf.h"
#include "base/strings/sys_string_conversions.h"
#import "base/test/ios/wait_util.h"
#import "ios/web/public/test/js_test_util.h"
#include "ios/web/public/test/web_test.h"
#import "ios/web/public/test/web_test_with_web_state.h"
#import "ios/web/public/web_state/ui/crw_web_view_proxy.h"
#import "ios/web/public/web_state/ui/crw_web_view_scroll_view_proxy.h"
#import "ios/web/public/web_state/web_state.h"
#import "ios/web/public/web_view_creation_util.h"
#import "ios/web/web_state/context_menu_constants.h"
#import "ios/web/web_state/ui/wk_web_view_configuration_provider.h"
#import "net/base/mac/url_conversions.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/gtest_mac.h"
......@@ -52,15 +47,6 @@ using base::test::ios::WaitUntilConditionOrTimeout;
namespace {
// Test coordinates and expected result for __gCrWeb.getElementFromPoint call.
struct TestCoordinatesAndExpectedValue {
TestCoordinatesAndExpectedValue(CGFloat x, CGFloat y, id expected_value)
: x(x), y(y), expected_value(expected_value) {}
CGFloat x = 0;
CGFloat y = 0;
id expected_value = nil;
};
// Request id used for __gCrWeb.findElementAtPoint call.
NSString* const kRequestId = @"UNIQUE_IDENTIFIER";
......@@ -80,362 +66,6 @@ NSString* kPageContentTemplate =
namespace web {
// Test fixture to test __gCrWeb.getElementFromPoint function defined in
// context_menu.js.
class ContextMenuJsTest : public web::WebTestWithWebState {
protected:
// Verifies that __gCrWeb.getElementFromPoint returns |expected_value| for
// the given image |html|.
void ImageTesterHelper(NSString* html, NSDictionary* expected_value) {
NSString* page_content =
[NSString stringWithFormat:kPageContentTemplate, html];
TestCoordinatesAndExpectedValue test_data[] = {
// Point outside the document margins.
{0, 0, @{}},
// Point inside the <p> element.
{20, 20, expected_value},
// Point outside the <p> element.
{GetWebViewContentSize().width / 2, 50, @{}},
};
for (size_t i = 0; i < arraysize(test_data); i++) {
const TestCoordinatesAndExpectedValue& data = test_data[i];
LoadHtml(page_content);
ExecuteJavaScript(@"document.getElementsByTagName('p')"); // Force layout
id result = ExecuteGetElementFromPointJavaScript(data.x, data.y);
EXPECT_NSEQ(data.expected_value, result)
<< " in test " << i << ": (" << data.x << ", " << data.y << ")";
}
}
// Returns web view's content size from the current web state.
CGSize GetWebViewContentSize() {
return web_state()->GetWebViewProxy().scrollViewProxy.contentSize;
}
// Executes __gCrWeb.getElementFromPoint script and syncronously returns the
// result. |x| and |y| are points in web view coordinate system.
id ExecuteGetElementFromPointJavaScript(CGFloat x, CGFloat y) {
CGSize contentSize = GetWebViewContentSize();
NSString* const script = [NSString
stringWithFormat:@"__gCrWeb.getElementFromPoint(%g, %g, %g, %g)", x, y,
contentSize.width, contentSize.height];
return ExecuteJavaScript(script);
}
};
// Tests that __gCrWeb.getElementFromPoint function returns correct src.
TEST_F(ContextMenuJsTest, GetImageUrlAtPoint) {
NSString* html =
@"<img id='foo' style='width:200;height:200;' src='file:///bogus'/>";
NSDictionary* expected_value = @{
kContextMenuElementSource : @"file:///bogus",
kContextMenuElementReferrerPolicy : @"default",
};
ImageTesterHelper(html, expected_value);
}
// Tests that __gCrWeb.getElementFromPoint function returns correct title.
// TODO(crbug.com/796418): This test is flaky on devices.
#if TARGET_IPHONE_SIMULATOR
#define MAYBE_GetImageTitleAtPoint GetImageTitleAtPoint
#else
#define MAYBE_GetImageTitleAtPoint FLAKY_GetImageTitleAtPoint
#endif
TEST_F(ContextMenuJsTest, MAYBE_GetImageTitleAtPoint) {
NSString* html =
@"<img id='foo' title='Hello world!'"
"style='width:200;height:200;' src='file:///bogus'/>";
NSDictionary* expected_value = @{
kContextMenuElementSource : @"file:///bogus",
kContextMenuElementReferrerPolicy : @"default",
kContextMenuElementTitle : @"Hello world!",
};
ImageTesterHelper(html, expected_value);
}
// Tests that __gCrWeb.getElementFromPoint function returns correct href.
TEST_F(ContextMenuJsTest, GetLinkImageUrlAtPoint) {
NSString* html =
@"<a href='file:///linky'>"
"<img id='foo' style='width:200;height:200;' src='file:///bogus'/>"
"</a>";
NSDictionary* expected_value = @{
kContextMenuElementSource : @"file:///bogus",
kContextMenuElementReferrerPolicy : @"default",
kContextMenuElementHyperlink : @"file:///linky",
};
ImageTesterHelper(html, expected_value);
}
TEST_F(ContextMenuJsTest, TextAreaStopsProximity) {
NSString* html =
@"<html><body style='margin-left:10px;margin-top:10px;'>"
"<div style='width:100px;height:100px;'>"
"<img id='foo'"
" style='position:absolute;left:0px;top:0px;width:50px;height:50px'"
" src='file:///bogus' />"
"<input type='text' name='name'"
" style='position:absolute;left:5px;top:5px; "
"width:40px;height:40px'/>"
"</div></body> </html>";
NSDictionary* success = @{
kContextMenuElementSource : @"file:///bogus",
kContextMenuElementReferrerPolicy : @"default",
};
NSDictionary* failure = @{};
TestCoordinatesAndExpectedValue test_data[] = {
{2, 20, success}, {10, 10, failure},
};
for (size_t i = 0; i < arraysize(test_data); i++) {
const TestCoordinatesAndExpectedValue& data = test_data[i];
LoadHtml(html);
ExecuteJavaScript(@"document.getElementsByTagName('img')"); // Force layout
id result = ExecuteGetElementFromPointJavaScript(data.x, data.y);
EXPECT_NSEQ(data.expected_value, result)
<< " in test " << i << ": (" << data.x << ", " << data.y << ")";
}
}
// Tests the javascript of the url of the an image present in the DOM.
// TODO(crbug.com/796418): This test is flaky on devices.
#if TARGET_IPHONE_SIMULATOR
#define MAYBE_LinkOfImage LinkOfImage
#else
#define MAYBE_LinkOfImage FLAKY_LinkOfImage
#endif
TEST_F(ContextMenuJsTest, MAYBE_LinkOfImage) {
// A page with a large image surrounded by a link.
static const char image[] =
"<a href='%s'><img width=400 height=400 src='foo'></img></a>";
// A page with a link to a destination URL.
ASSERT_TRUE(LoadHtml(base::StringPrintf(image, "http://destination")));
ExecuteJavaScript(@"document.getElementsByTagName('img')"); // Force layout.
id result = ExecuteGetElementFromPointJavaScript(20, 20);
NSDictionary* expected_result = @{
kContextMenuElementSource :
[NSString stringWithFormat:@"%sfoo", BaseUrl().c_str()],
kContextMenuElementReferrerPolicy : @"default",
kContextMenuElementHyperlink : @"http://destination/",
};
EXPECT_NSEQ(expected_result, result);
// A page with a link with some JavaScript that does not result in a NOP.
ASSERT_TRUE(LoadHtml(
base::StringPrintf(image, "javascript:console.log('whatever')")));
result = ExecuteGetElementFromPointJavaScript(20, 20);
expected_result = @{
kContextMenuElementSource :
[NSString stringWithFormat:@"%sfoo", BaseUrl().c_str()],
kContextMenuElementReferrerPolicy : @"default",
kContextMenuElementHyperlink : @"javascript:console.log(",
};
EXPECT_NSEQ(expected_result, result);
// A list of JavaScripts that result in a NOP.
std::vector<std::string> nop_javascripts;
nop_javascripts.push_back(";");
nop_javascripts.push_back("void(0);");
nop_javascripts.push_back("void(0); void(0); void(0)");
for (auto js : nop_javascripts) {
// A page with a link with some JavaScript that results in a NOP.
const std::string javascript = std::string("javascript:") + js;
ASSERT_TRUE(LoadHtml(base::StringPrintf(image, javascript.c_str())));
result = ExecuteGetElementFromPointJavaScript(20, 20);
expected_result = @{
kContextMenuElementSource :
[NSString stringWithFormat:@"%sfoo", BaseUrl().c_str()],
kContextMenuElementReferrerPolicy : @"default",
};
// Make sure the returned JSON does not have an 'href' key.
EXPECT_NSEQ(expected_result, result);
}
}
// Tests context menu invoked on an image with "-webkit-touch-callout:none"
// style and parent link.
TEST_F(ContextMenuJsTest, LinkOfImageWithCalloutNone) {
// A page with an image surrounded by a link.
static const char image_html[] =
"<a href='%s'>"
"<img style='width:9;height:9;display:block;-webkit-touch-callout:none;'>"
"</a>";
// A page with a link to a destination URL.
ASSERT_TRUE(LoadHtml(base::StringPrintf(image_html, "http://destination")));
ExecuteJavaScript(@"document.getElementsByTagName('img')"); // Force layout.
id result = ExecuteGetElementFromPointJavaScript(5, 5);
NSDictionary* expected_result = @{
kContextMenuElementInnerText : @"",
kContextMenuElementReferrerPolicy : @"default",
kContextMenuElementHyperlink : @"http://destination/",
};
EXPECT_NSEQ(expected_result, result);
}
// Tests that the GetElementFromPoint script reports "never" as the referrer
// policy for pages that have an unsupported policy in a meta tag.
TEST_F(ContextMenuJsTest, UnsupportedReferrerPolicy) {
// A page with an unsupported referrer meta tag and a 400x400 image.
static const char kInvalidReferrerTag[] =
"<meta name=\"referrer\" content=\"unsupported-value\">"
"<img width=400 height=400 src='foo'></img>";
// Load the invalid meta tag
ASSERT_TRUE(LoadHtml(kInvalidReferrerTag));
ExecuteJavaScript(@"document.getElementsByTagName('img')"); // Force layout
id result = ExecuteGetElementFromPointJavaScript(20, 20);
ASSERT_TRUE([result isKindOfClass:[NSDictionary class]]);
EXPECT_NSEQ(@"never", result[kContextMenuElementReferrerPolicy]);
}
// Tests that getElementFromPoint finds an element at the bottom of a very long
// page.
// TODO(crbug.com/796418): This test is flaky on devices.
#if TARGET_IPHONE_SIMULATOR
#define MAYBE_LinkOfTextFromTallPage LinkOfTextFromTallPage
#else
#define MAYBE_LinkOfTextFromTallPage FLAKY_LinkOfTextFromTallPage
#endif
TEST_F(ContextMenuJsTest, MAYBE_LinkOfTextFromTallPage) {
const char kHtml[] =
"<html><body>"
" <div style='height:4000px'></div>"
" <div><a href='http://destination'>link</a></div>"
"</body></html>";
ASSERT_TRUE(LoadHtml(kHtml));
// Scroll the webView to the bottom to make the link accessible.
CGFloat content_height = GetWebViewContentSize().height;
CGFloat scroll_view_height =
CGRectGetHeight(web_state()->GetWebViewProxy().scrollViewProxy.frame);
CGFloat offset = content_height - scroll_view_height;
web_state()->GetWebViewProxy().scrollViewProxy.contentOffset =
CGPointMake(0.0, offset);
ExecuteJavaScript(@"document.getElementsByTagName('a')"); // Force layout.
// Link is at bottom of the page content.
id result = ExecuteGetElementFromPointJavaScript(1, content_height - 5.0);
NSDictionary* expected_result = @{
kContextMenuElementInnerText : @"link",
kContextMenuElementReferrerPolicy : @"default",
kContextMenuElementHyperlink : @"http://destination/",
};
EXPECT_NSEQ(expected_result, result);
}
// Tests that a callout information about a link is displayed when
// -webkit-touch-callout property is not specified. Please see:
// https://developer.mozilla.org/en-US/docs/Web/CSS/-webkit-touch-callout
TEST_F(ContextMenuJsTest, LinkOfTextWithoutCalloutProperty) {
const char kLinkHtml[] = "<a href='%s'>link</a>";
ASSERT_TRUE(LoadHtml(base::StringPrintf(kLinkHtml, "http://destination")));
ExecuteJavaScript(@"document.getElementsByTagName('a')"); // Force layout.
id result = ExecuteGetElementFromPointJavaScript(1, 1);
NSDictionary* expected_result = @{
kContextMenuElementInnerText : @"link",
kContextMenuElementReferrerPolicy : @"default",
kContextMenuElementHyperlink : @"http://destination/",
};
EXPECT_NSEQ(expected_result, result);
}
// Tests that a callout information about a link is displayed when
// -webkit-touch-callout property is set to default. Please see:
// https://developer.mozilla.org/en-US/docs/Web/CSS/-webkit-touch-callout
// TODO(crbug.com/796343): This test is flaky on iOS 11 device.
#if TARGET_IPHONE_SIMULATOR
#define MAYBE_LinkOfTextWithCalloutDefault LinkOfTextWithCalloutDefault
#else
#define MAYBE_LinkOfTextWithCalloutDefault FLAKY_LinkOfTextWithCalloutDefault
#endif
TEST_F(ContextMenuJsTest, MAYBE_LinkOfTextWithCalloutDefault) {
const char kLinkHtml[] =
"<a href='%s' style='-webkit-touch-callout:default;'>link</a>";
ASSERT_TRUE(LoadHtml(base::StringPrintf(kLinkHtml, "http://destination")));
ExecuteJavaScript(@"document.getElementsByTagName('a')"); // Force layout.
id result = ExecuteGetElementFromPointJavaScript(1, 1);
NSDictionary* expected_result = @{
kContextMenuElementInnerText : @"link",
kContextMenuElementReferrerPolicy : @"default",
kContextMenuElementHyperlink : @"http://destination/",
};
EXPECT_NSEQ(expected_result, result);
}
// Tests that no callout information about a link is displayed when
// -webkit-touch-callout property is set to none. Please see:
// https://developer.mozilla.org/en-US/docs/Web/CSS/-webkit-touch-callout
// TODO(crbug.com/873662): This test is flaky on iOS 11 device.
#if TARGET_IPHONE_SIMULATOR
#define MAYBE_LinkOfTextWithCalloutNone LinkOfTextWithCalloutNone
#else
#define MAYBE_LinkOfTextWithCalloutNone FLAKY_LinkOfTextWithCalloutNone
#endif
TEST_F(ContextMenuJsTest, MAYBE_LinkOfTextWithCalloutNone) {
const char kLinkHtml[] =
"<a href='%s' style='-webkit-touch-callout:none;'>link</a>";
ASSERT_TRUE(LoadHtml(base::StringPrintf(kLinkHtml, "http://destination")));
ExecuteJavaScript(@"document.getElementsByTagName('a')"); // Force layout.
id result = ExecuteGetElementFromPointJavaScript(1, 1);
EXPECT_NSEQ(@{}, result);
}
// Tests that -webkit-touch-callout property can be inherited from ancester if
// it's not specified. Please see:
// https://developer.mozilla.org/en-US/docs/Web/CSS/-webkit-touch-callout
TEST_F(ContextMenuJsTest, LinkOfTextWithCalloutFromAncester) {
const char kLinkHtml[] =
"<body style='-webkit-touch-callout: none'>"
" <a href='%s'>link</a>"
"</body>";
ASSERT_TRUE(LoadHtml(base::StringPrintf(kLinkHtml, "http://destination")));
ExecuteJavaScript(@"document.getElementsByTagName('a')"); // Force layout.
id result = ExecuteGetElementFromPointJavaScript(1, 1);
EXPECT_NSEQ(@{}, result);
}
// Tests that setting -webkit-touch-callout property can override the value
// inherited from ancester. Please see:
// https://developer.mozilla.org/en-US/docs/Web/CSS/-webkit-touch-callout
// TODO(crbug.com/873660): This test is flaky on iOS 11 device.
#if TARGET_IPHONE_SIMULATOR
#define MAYBE_LinkOfTextWithCalloutOverride LinkOfTextWithCalloutOverride
#else
#define MAYBE_LinkOfTextWithCalloutOverride FLAKY_LinkOfTextWithCalloutOverride
#endif
TEST_F(ContextMenuJsTest, MAYBE_LinkOfTextWithCalloutOverride) {
const char kLinkHtml[] =
"<body style='-webkit-touch-callout: none'>"
" <a href='%s' style='-webkit-touch-callout: default'>link</a>"
"</body>";
ASSERT_TRUE(LoadHtml(base::StringPrintf(kLinkHtml, "http://destination")));
ExecuteJavaScript(@"document.getElementsByTagName('a')"); // Force layout.
id result = ExecuteGetElementFromPointJavaScript(1, 1);
NSDictionary* expected_result = @{
kContextMenuElementInnerText : @"link",
kContextMenuElementReferrerPolicy : @"default",
kContextMenuElementHyperlink : @"http://destination/",
};
EXPECT_NSEQ(expected_result, result);
}
// Test fixture to test __gCrWeb.findElementAtPoint function defined in
// context_menu.js.
class ContextMenuJsFindElementAtPointTest : public web::WebTest {
......@@ -826,10 +456,9 @@ TEST_F(ContextMenuJsFindElementAtPointTest, UnsupportedReferrerPolicy) {
// very long page.
// TODO(crbug.com/796418): This test is flaky on devices.
#if TARGET_IPHONE_SIMULATOR
#define MAYBE_LinkOfTextFromTallPageFindElementAtPoint LinkOfTextFromTallPage
#define MAYBE_LinkOfTextFromTallPage LinkOfTextFromTallPage
#else
#define MAYBE_LinkOfTextFromTallPageFindElementAtPoint \
FLAKY_LinkOfTextFromTallPage
#define MAYBE_LinkOfTextFromTallPage FLAKY_LinkOfTextFromTallPage
#endif
TEST_F(ContextMenuJsFindElementAtPointTest, MAYBE_LinkOfTextFromTallPage) {
NSString* kHtml =
......@@ -893,11 +522,9 @@ TEST_F(ContextMenuJsFindElementAtPointTest,
// https://developer.mozilla.org/en-US/docs/Web/CSS/-webkit-touch-callout
// TODO(crbug.com/796343): This test is flaky on iOS 11 device.
#if TARGET_IPHONE_SIMULATOR
#define MAYBE_LinkOfTextWithCalloutDefaultFindElementAtPoint \
LinkOfTextWithCalloutDefault
#define MAYBE_LinkOfTextWithCalloutDefault LinkOfTextWithCalloutDefault
#else
#define MAYBE_LinkOfTextWithCalloutDefaultFindElementAtPoint \
FLAKY_LinkOfTextWithCalloutDefault
#define MAYBE_LinkOfTextWithCalloutDefault FLAKY_LinkOfTextWithCalloutDefault
#endif
TEST_F(ContextMenuJsFindElementAtPointTest,
MAYBE_LinkOfTextWithCalloutDefault) {
......
......@@ -14,59 +14,6 @@ goog.require('__crWeb.common');
/** Beginning of anonymous object */
(function() {
/**
* Returns an object representing the details of the given element.
* @param {number} x Horizontal center of the selected point in page
* coordinates.
* @param {number} y Vertical center of the selected point in page
* coordinates.
* @return {Object} An object of the same form as returned by
* {@code getResponseForLinkElement} or
* {@code getResponseForImageElement} or null if no element was
* found.
*/
__gCrWeb['getElementFromPointInPageCoordinates'] = function(x, y) {
var hitCoordinates = spiralCoordinates_(x, y);
for (var index = 0; index < hitCoordinates.length; index++) {
var coordinates = hitCoordinates[index];
var coordinateDetails = newCoordinate(coordinates.x, coordinates.y);
var element = elementsFromCoordinates(coordinateDetails);
if (!element || !element.tagName) {
// Nothing under the hit point. Try the next hit point.
continue;
}
// Also check element's ancestors. A bound on the level is used here to
// avoid large overhead when no links or images are found.
var level = 0;
while (++level < 8 && element && element != document) {
var tagName = element.tagName;
if (!tagName) continue;
tagName = tagName.toLowerCase();
if (tagName === 'input' || tagName === 'textarea' ||
tagName === 'select' || tagName === 'option') {
// If the element is a known input element, stop the spiral search and
// return empty results.
return {};
}
if (getComputedWebkitTouchCallout_(element) !== 'none') {
if (tagName === 'a' && element.href) {
return getResponseForLinkElement(element);
}
if (tagName === 'img' && element.src) {
return getResponseForImageElement(element);
}
}
element = element.parentNode;
}
}
return {};
};
/**
* Returns an object representing the details of a given link element.
* @param {HTMLElement} element The element whose details will be returned.
......
......@@ -18,7 +18,7 @@ goog.provide('__crWeb.mainFrameContextMenu');
* found element (or an empty object if no links or images are found) back to
* the application by posting a 'FindElementResultHandler' message.
* The object returned in the message is of the same form as
* {@code getElementFromPointInPageCoordinates} result.
* {@code findElementAtPointInPageCoordinates} result.
* @param {string} requestId An identifier which be returned in the result
* dictionary of this request.
* @param {number} x Horizontal center of the selected point in web view
......@@ -36,23 +36,6 @@ __gCrWeb['findElementAtPoint'] =
y * scale);
};
/**
* Returns the url of the image or link under the selected point. Returns an
* empty object if no links or images are found.
* @param {number} x Horizontal center of the selected point in web view
* coordinates.
* @param {number} y Vertical center of the selected point in web view
* coordinates.
* @param {number} webViewWidth the width of web view.
* @param {number} webViewHeight the height of web view.
* @return {!Object} An object in the same form as
* {@code getElementFromPointInPageCoordinates} result.
*/
__gCrWeb['getElementFromPoint'] = function(x, y, webViewWidth, webViewHeight) {
var scale = getPageWidth() / webViewWidth;
return __gCrWeb.getElementFromPointInPageCoordinates(x * scale, y * scale);
};
/**
* Suppresses the next click such that they are not handled by JS click
* event handlers.
......
......@@ -178,10 +178,6 @@ struct ContextMenuInfo {
_delegate = delegate;
_injectionEvaluator = injectionEvaluator;
_pendingElementFetchRequests = [[NSMutableDictionary alloc] init];
// Default to assuming all elements are from the main frame since this value
// will not be updated unless the
// |web::features::kContextMenuElementPostMessage| feature is enabled.
_contextMenuInfoForLastTouch.is_main_frame = YES;
// The system context menu triggers after 0.55 second. Add a gesture
// recognizer with a shorter delay to be able to cancel the system menu if
......@@ -219,20 +215,17 @@ struct ContextMenuInfo {
}
}
if (base::FeatureList::IsEnabled(
web::features::kContextMenuElementPostMessage)) {
// Listen for fetched element response.
web::WKWebViewConfigurationProvider& configurationProvider =
web::WKWebViewConfigurationProvider::FromBrowserState(browserState);
CRWWKScriptMessageRouter* messageRouter =
configurationProvider.GetScriptMessageRouter();
__weak CRWContextMenuController* weakSelf = self;
[messageRouter setScriptMessageHandler:^(WKScriptMessage* message) {
[weakSelf didReceiveScriptMessage:message];
}
name:kFindElementResultHandlerName
webView:webView];
// Listen for fetched element response.
web::WKWebViewConfigurationProvider& configurationProvider =
web::WKWebViewConfigurationProvider::FromBrowserState(browserState);
CRWWKScriptMessageRouter* messageRouter =
configurationProvider.GetScriptMessageRouter();
__weak CRWContextMenuController* weakSelf = self;
[messageRouter setScriptMessageHandler:^(WKScriptMessage* message) {
[weakSelf didReceiveScriptMessage:message];
}
name:kFindElementResultHandlerName
webView:webView];
}
return self;
}
......@@ -492,41 +485,20 @@ struct ContextMenuInfo {
CGFloat webViewContentWidth = webViewContentSize.width;
CGFloat webViewContentHeight = webViewContentSize.height;
NSString* formatString;
web::JavaScriptResultBlock completionHandler = nil;
if (base::FeatureList::IsEnabled(
web::features::kContextMenuElementPostMessage)) {
NSString* requestID =
base::SysUTF8ToNSString(base::UnguessableToken::Create().ToString());
HTMLElementFetchRequest* fetchRequest =
[[HTMLElementFetchRequest alloc] initWithFoundElementHandler:handler];
_pendingElementFetchRequests[requestID] = fetchRequest;
formatString =
[NSString stringWithFormat:
@"__gCrWeb.findElementAtPoint('%@', %%g, %%g, %%g, %%g);",
requestID];
} else {
formatString = @"__gCrWeb.getElementFromPoint(%g, %g, %g, %g);";
base::TimeTicks getElementStartTime = base::TimeTicks::Now();
__weak CRWContextMenuController* weakSelf = self;
completionHandler = ^(id element, NSError* error) {
[weakSelf logElementFetchDurationWithStartTime:getElementStartTime];
if (error.code == WKErrorWebContentProcessTerminated ||
error.code == WKErrorWebViewInvalidated) {
// Renderer was terminated or view deallocated.
handler(nil);
} else {
handler(base::mac::ObjCCastStrict<NSDictionary>(element));
}
};
}
NSString* requestID =
base::SysUTF8ToNSString(base::UnguessableToken::Create().ToString());
HTMLElementFetchRequest* fetchRequest =
[[HTMLElementFetchRequest alloc] initWithFoundElementHandler:handler];
_pendingElementFetchRequests[requestID] = fetchRequest;
NSString* formatString = [NSString
stringWithFormat:
@"__gCrWeb.findElementAtPoint('%@', %%g, %%g, %%g, %%g);", requestID];
NSString* getElementScript =
[NSString stringWithFormat:formatString, point.x + scrollOffset.x,
point.y + scrollOffset.y, webViewContentWidth,
webViewContentHeight];
[self executeJavaScript:getElementScript completionHandler:completionHandler];
[self executeJavaScript:getElementScript completionHandler:nil];
}
@end
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