blob: c1bb9722ee710faee50975e00f8390bad0b8bcc4 [file] [log] [blame]
// Copyright 2017 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.
#include <vector>
#import <CoreGraphics/CoreGraphics.h>
#import <Foundation/Foundation.h>
#include "base/macros.h"
#include "base/strings/stringprintf.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"
#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
// Unit tests for ios/web/web_state/js/resources/context_menu.js.
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;
};
} // namespace
namespace web {
// Test fixture to test 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_template =
@"<html><body style='margin-left:10px;margin-top:10px;'>"
"<div style='width:100px;height:100px;'>"
" <p style='position:absolute;left:25px;top:25px;"
" width:50px;height:50px'>"
"%@"
" Chrome rocks!"
" </p></div></body></html>";
NSString* page_content =
[NSString stringWithFormat:page_content_template, 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);
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 = @{
@"src" : @"file:///bogus",
@"referrerPolicy" : @"default",
};
ImageTesterHelper(html, expected_value);
}
// Tests that __gCrWeb.getElementFromPoint function returns correct title.
TEST_F(ContextMenuJsTest, GetImageTitleAtPoint) {
NSString* html = @"<img id='foo' title='Hello world!'"
"style='width:200;height:200;' src='file:///bogus'/>";
NSDictionary* expected_value = @{
@"src" : @"file:///bogus",
@"referrerPolicy" : @"default",
@"title" : @"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 = @{
@"src" : @"file:///bogus",
@"referrerPolicy" : @"default",
@"href" : @"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 = @{
@"src" : @"file:///bogus",
@"referrerPolicy" : @"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);
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.
TEST_F(ContextMenuJsTest, 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.
LoadHtml(base::StringPrintf(image, "http://destination"));
id result = ExecuteGetElementFromPointJavaScript(20, 20);
NSDictionary* expected_result = @{
@"src" : [NSString stringWithFormat:@"%sfoo", BaseUrl().c_str()],
@"referrerPolicy" : @"default",
@"href" : @"http://destination/",
};
EXPECT_NSEQ(expected_result, result);
// A page with a link with some JavaScript that does not result in a NOP.
LoadHtml(base::StringPrintf(image, "javascript:console.log('whatever')"));
result = ExecuteGetElementFromPointJavaScript(20, 20);
expected_result = @{
@"src" : [NSString stringWithFormat:@"%sfoo", BaseUrl().c_str()],
@"referrerPolicy" : @"default",
@"href" : @"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;
LoadHtml(base::StringPrintf(image, javascript.c_str()));
result = ExecuteGetElementFromPointJavaScript(20, 20);
expected_result = @{
@"src" : [NSString stringWithFormat:@"%sfoo", BaseUrl().c_str()],
@"referrerPolicy" : @"default",
};
// Make sure the returned JSON does not have an 'href' key.
EXPECT_NSEQ(expected_result, result);
}
}
} // namespace web