| // 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 |