Commit 911f6362 authored by Tommy Martino's avatar Tommy Martino Committed by Commit Bot

[SH iOS] Hooking browser into JS

This creates an end-to-end connection from the navigation stack into the
new third-party JS lib which will handle the highlighting/scrolling in
the page.

Change-Id: I95d2a0e89387708d573bfab4617fa5ff34f516c8
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2354605
Commit-Queue: Tommy Martino <tmartino@chromium.org>
Reviewed-by: default avatarEugene But <eugenebut@chromium.org>
Cr-Commit-Position: refs/heads/master@{#798300}
parent efade016
......@@ -584,11 +584,13 @@ js_compile_bundle("main_frame_web_bundle") {
closure_entry_point = "__crWeb.mainFrameWebBundle"
sources = [
"//third_party/text-fragments-polyfill/src/src/text-fragment-utils.js",
"web_state/js/resources/error.js",
"web_state/js/resources/main_frame_context_menu.js",
"web_state/js/resources/main_frame_web_bundle.js",
"web_state/js/resources/navigation.js",
"web_state/js/resources/scroll_workaround.js",
"web_state/js/resources/text_fragments.js",
]
}
......
......@@ -24,6 +24,7 @@
#import "ios/web/navigation/navigation_context_impl.h"
#import "ios/web/navigation/navigation_manager_impl.h"
#include "ios/web/navigation/navigation_manager_util.h"
#import "ios/web/navigation/text_fragment_utils.h"
#import "ios/web/navigation/web_kit_constants.h"
#import "ios/web/navigation/wk_back_forward_list_item_holder.h"
#import "ios/web/navigation/wk_navigation_action_policy_util.h"
......@@ -1150,6 +1151,10 @@ void ReportOutOfSyncURLInDidStartProvisionalNavigation(
}
}
if (context && web::AreTextFragmentsAllowed(context)) {
web::HandleTextFragments(self.webStateImpl);
}
[self.navigationStates setState:web::WKNavigationState::FINISHED
forNavigation:navigation];
......
......@@ -12,6 +12,7 @@ class GURL;
namespace web {
class NavigationContext;
class WebState;
// This file contains helper functions relating to Text Fragments, which are
// appended to the reference fragment in the URL and instruct the user agent
......@@ -25,7 +26,7 @@ bool AreTextFragmentsAllowed(NavigationContext* context);
// Checks the destination URL for Text Fragments. If found, searches the DOM for
// matching text, highlights the text, and scrolls the first into view.
void HandleTextFragments(NavigationContext* context);
void HandleTextFragments(WebState* state);
// Exposed for testing only.
namespace internal {
......@@ -33,7 +34,7 @@ namespace internal {
// Checks the fragment portion of the URL for Text Fragments. Returns zero or
// more dictionaries containing the parsed parameters used by the fragment-
// finding algorithm, as defined in the spec.
std::vector<base::Value> ParseTextFragments(const GURL& url);
base::Value ParseTextFragments(const GURL& url);
// Extracts the text fragments, if any, from a ref string.
std::vector<std::string> ExtractTextFragments(std::string ref_string);
......
......@@ -6,7 +6,10 @@
#include <cstring.h>
#include "base/json/json_writer.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "ios/web/common/features.h"
#import "ios/web/public/navigation/navigation_context.h"
#import "ios/web/public/web_state.h"
......@@ -38,26 +41,35 @@ bool AreTextFragmentsAllowed(NavigationContext* context) {
return context->HasUserGesture() && !context->IsSameDocument();
}
void HandleTextFragments(NavigationContext* context) {
// TODO(crbug.com/1099268): Parse URL fragment, execute JS using passed
// params.
void HandleTextFragments(WebState* state) {
std::string fragment_param;
base::JSONWriter::Write(
internal::ParseTextFragments(state->GetLastCommittedURL()),
&fragment_param);
std::string script = base::ReplaceStringPlaceholders(
"__gCrWeb.textFragments.handleTextFragments($1, $2)",
{fragment_param, /* scroll = */ "true"},
/* offsets= */ nullptr);
state->ExecuteJavaScript(base::UTF8ToUTF16(script));
}
namespace internal {
std::vector<base::Value> ParseTextFragments(const GURL& url) {
base::Value ParseTextFragments(const GURL& url) {
if (!url.has_ref())
return {};
std::vector<std::string> fragments = ExtractTextFragments(url.ref());
if (fragments.empty())
return {};
std::vector<base::Value> parsed;
base::Value parsed(base::Value::Type::LIST);
for (const std::string& fragment : fragments) {
base::Value parsed_fragment = TextFragmentToValue(fragment);
if (parsed_fragment.type() == base::Value::Type::NONE)
continue;
parsed.push_back(std::move(parsed_fragment));
parsed.Append(std::move(parsed_fragment));
}
return parsed;
......
......@@ -70,16 +70,16 @@ TEST_F(TextFragmentUtilsTest, AreTextFragmentsAllowed) {
TEST_F(TextFragmentUtilsTest, ParseTextFragments) {
GURL url_with_fragment(
"https://www.example.com/#idFrag:~:text=text%201&text=text%202");
std::vector<base::Value> result =
internal::ParseTextFragments(url_with_fragment);
ASSERT_EQ(2u, result.size());
EXPECT_EQ("text%201", result[0].FindKey(kTextStartKey)->GetString());
EXPECT_EQ("text%202", result[1].FindKey(kTextStartKey)->GetString());
base::Value result = internal::ParseTextFragments(url_with_fragment);
ASSERT_EQ(2u, result.GetList().size());
EXPECT_EQ("text%201",
result.GetList()[0].FindKey(kTextStartKey)->GetString());
EXPECT_EQ("text%202",
result.GetList()[1].FindKey(kTextStartKey)->GetString());
GURL url_no_fragment("www.example.com");
std::vector<base::Value> empty_result =
internal::ParseTextFragments(url_no_fragment);
EXPECT_TRUE(empty_result.empty());
base::Value empty_result = internal::ParseTextFragments(url_no_fragment);
EXPECT_TRUE(empty_result.is_none());
}
TEST_F(TextFragmentUtilsTest, ExtractTextFragments) {
......
......@@ -11,3 +11,4 @@ goog.require('__crWeb.error');
goog.require('__crWeb.mainFrameContextMenu');
goog.require('__crWeb.navigation');
goog.require('__crWeb.scrollWorkaround');
goog.require('__crWeb.textFragments');
// 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.
goog.module('__crWeb.textFragments');
goog.module.declareLegacyNamespace();
const utils = goog.require(
'googleChromeLabs.textFragmentPolyfill.textFragmentUtils');
/**
* @fileoverview Interface used for Chrome/WebView to call into the
* text-fragments-polyfill lib, which handles finding text fragments provided
* by the navigation layer, highlighting them, and scrolling them into view.
*/
(function() {
__gCrWeb['textFragments'] = {};
/**
* Attempts to identify and highlight the given text fragments and,
* optionally, scroll them into view.
*/
__gCrWeb.textFragments.handleTextFragments = function(fragments, scroll) {
if (document.readyState === "complete" ||
document.readyState === "interactive") {
doHandleTextFragments(fragments, scroll);
} else {
document.addEventListener("DOMContentLoaded", () => {
doHandleTextFragments(fragments, scroll);
});
}
}
/**
* Does the actual work for handleTextFragments.
*/
const doHandleTextFragments = function(fragments, scroll) {
let marks = [];
for (const fragment of fragments) {
let newMarks = utils.processTextFragmentDirective(fragment);
if (Array.isArray(newMarks))
marks.push(...newMarks);
}
if (scroll && marks.length > 0)
utils.scrollElementIntoView(marks[0]);
// TODO(crbug.com/1099268): Count successes/failures above and log metrics
// TODO(crbug.com/1099268): Make marks disappear on user interaction
}
})();
......@@ -20,7 +20,6 @@
#import "ios/web/navigation/navigation_context_impl.h"
#import "ios/web/navigation/navigation_item_impl.h"
#import "ios/web/navigation/session_storage_builder.h"
#import "ios/web/navigation/text_fragment_utils.h"
#import "ios/web/navigation/wk_based_navigation_manager_impl.h"
#import "ios/web/navigation/wk_navigation_util.h"
#include "ios/web/public/browser_state.h"
......@@ -859,10 +858,6 @@ void WebStateImpl::OnNavigationFinished(web::NavigationContextImpl* context) {
observer.FaviconUrlUpdated(this, cached_favicon_urls_);
}
}
if (AreTextFragmentsAllowed(context)) {
HandleTextFragments(context);
}
}
#pragma mark - NavigationManagerDelegate implementation
......
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