Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add FXIOS-8087 [v124] find in page web engine #18622

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion BrowserKit/Sources/WebEngine/EngineSession.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import Foundation
/// Protocol representing a single engine session. In browsers usually a session corresponds to a tab.
public protocol EngineSession {
var delegate: EngineSessionDelegate? { get set }
var findInPageDelegate: FindInPageHelperDelegate? { get set }

/// The engine received a request to load a URL.
/// - Parameter url: The URL string that is requested
Expand All @@ -24,9 +25,18 @@ public protocol EngineSession {
/// Navigates forward in the history of this session.
func goForward()

/// Scroll the session to the top
/// Scroll the session to the top.
func scrollToTop()

/// Find a text selection in this session.
/// - Parameters:
/// - text: The text to search
/// - function: The functionality the find in page should search with
func findInPage(text: String, function: FindInPageFunction)

/// Called whenever the find in page session should be ended.
func findInPageDone()

/// Navigates to the specified index in the history of this session. The current index of
/// this session's history will be updated but the items within it will be unchanged.
/// Invalid index values are ignored.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/

import Common
import Foundation
import WebKit

public enum FindInPageFunction: String {
/// Find all the occurences of this text in the page
case find

/// Find the next occurence of this text in the page
case findNext

/// Find the previous occurence of this text in the page
case findPrevious
}

public protocol FindInPageHelperDelegate: AnyObject {
func findInPageHelper(didUpdateCurrentResult currentResult: Int)
func findInPageHelper(didUpdateTotalResults totalResults: Int)
}

class FindInPageContentScript: WKContentScript {
weak var delegate: FindInPageHelperDelegate?
private var logger: Logger

init(logger: Logger = DefaultLogger.shared) {
self.logger = logger
}

class func name() -> String {
return "FindInPage"
}

func scriptMessageHandlerNames() -> [String] {
return ["findInPageHandler"]
}

func userContentController(
didReceiveMessage message: Any
) {
guard let parameters = message as? [String: Int] else {
// TODO: FXIOS-6463 - Integrate message handler check
logger.log("FindInPage.js sent wrong type of message",
level: .warning,
category: .webview)
return
}

if let currentResult = parameters["currentResult"] {
delegate?.findInPageHelper(didUpdateCurrentResult: currentResult)
}

if let totalResults = parameters["totalResults"] {
delegate?.findInPageHelper(didUpdateTotalResults: totalResults)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,7 @@ protocol WKContentScript {

func scriptMessageHandlerNames() -> [String]

func userContentController(
_ userContentController: WKUserContentController,
didReceiveScriptMessage message: WKScriptMessage
)
func userContentController(didReceiveMessage message: Any)

func prepareForDeinit()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import WebKit

/// Manager used to add and remove scripts inside a WKEngineSession
protocol WKContentScriptManager: WKScriptMessageHandler {
var scripts: [String: WKContentScript] { get }

func addContentScript(_ script: WKContentScript,
name: String,
forSession session: WKEngineSession)
Expand All @@ -19,12 +21,12 @@ protocol WKContentScriptManager: WKScriptMessageHandler {
}

class DefaultContentScriptManager: NSObject, WKContentScriptManager {
var scripts = [String: WKContentScript]()
private(set) var scripts = [String: WKContentScript]()

func addContentScript(_ script: WKContentScript,
name: String,
forSession session: WKEngineSession) {
// If a script already exists on a tab, skip adding this duplicate.
// If a script already exists on a session, skip adding this duplicate.
guard scripts[name] == nil else { return }

scripts[name] = script
Expand Down Expand Up @@ -58,7 +60,7 @@ class DefaultContentScriptManager: NSObject, WKContentScriptManager {
func userContentController(_ userContentController: WKUserContentController,
didReceive message: WKScriptMessage) {
for script in scripts.values where script.scriptMessageHandlerNames().contains(message.name) {
script.userContentController(userContentController, didReceiveScriptMessage: message)
script.userContentController(didReceiveMessage: message.body)
return
}
}
Expand Down
26 changes: 26 additions & 0 deletions BrowserKit/Sources/WebEngine/WKWebview/WKEngineSession.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,14 @@ class WKEngineSession: NSObject,
WKNavigationDelegate,
WKEngineWebViewDelegate {
weak var delegate: EngineSessionDelegate?
weak var findInPageDelegate: FindInPageHelperDelegate? {
didSet {
let script = contentScriptManager.scripts[FindInPageContentScript.name()]
guard let findInPage = script as? FindInPageContentScript else { return }
findInPage.delegate = findInPageDelegate
}
}

private(set) var webView: WKEngineWebView
private var logger: Logger
var sessionData: WKEngineSessionData
Expand Down Expand Up @@ -45,6 +53,7 @@ class WKEngineSession: NSObject,
webView.navigationDelegate = self
webView.delegate = self
userScriptManager.injectUserScriptsIntoWebView(webView)
addContentScripts()
}

// TODO: FXIOS-7903 #17648 no return from this load(url:), we need a way to recordNavigationInTab
Expand Down Expand Up @@ -116,6 +125,15 @@ class WKEngineSession: NSObject,
webView.engineScrollView.setContentOffset(CGPoint.zero, animated: true)
}

func findInPage(text: String, function: FindInPageFunction) {
let sanitizedInput = text.replacingOccurrences(of: "\\", with: "\\\\").replacingOccurrences(of: "\"", with: "\\\"")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just OOC is this something being brought over from the existing client? Wondering if there is a more elegant way for us to avoid how we're escaping these slashes but I'm not super-familiar with this code.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah it's brought from the existing code 🤔 It's from here exactly

webView.evaluateJavascriptInDefaultContentWorld("__firefox__.\(function.rawValue)(\"\(sanitizedInput)\")")
}

func findInPageDone() {
webView.evaluateJavascriptInDefaultContentWorld("__firefox__.findDone()")
}

func goToHistory(index: Int) {
// TODO: FXIOS-7907 #17651 Handle goToHistoryIndex in WKEngineSession (equivalent to goToBackForwardListItem)
}
Expand Down Expand Up @@ -221,6 +239,14 @@ class WKEngineSession: NSObject,
}
}

// MARK: - Content scripts

private func addContentScripts() {
contentScriptManager.addContentScript(FindInPageContentScript(),
name: FindInPageContentScript.name(),
forSession: self)
}

// MARK: - WKUIDelegate

func webView(_ webView: WKWebView,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/

import XCTest
@testable import WebEngine

final class FindInPageContentScriptTests: XCTestCase {
private var findInPageDelegate: MockFindInPageHelperDelegate!

override func setUp() {
super.setUp()
findInPageDelegate = MockFindInPageHelperDelegate()
}

override func tearDown() {
super.tearDown()
findInPageDelegate = nil
}

func testDidReceiveMessageGivenEmptyMessageThenNoDelegateCalled() {
let subject = FindInPageContentScript()
subject.delegate = findInPageDelegate

subject.userContentController(didReceiveMessage: [])

XCTAssertEqual(findInPageDelegate.didUpdateCurrentResultCalled, 0)
XCTAssertEqual(findInPageDelegate.didUpdateTotalResultsCalled, 0)
}

func testDidReceiveMessageGivenStringMessageThenNoDelegateCalled() {
let subject = FindInPageContentScript()
subject.delegate = findInPageDelegate

subject.userContentController(didReceiveMessage: ["": ""])

XCTAssertEqual(findInPageDelegate.didUpdateCurrentResultCalled, 0)
XCTAssertEqual(findInPageDelegate.didUpdateTotalResultsCalled, 0)
}

func testDidReceiveMessageGivenCurrentResultMessageThenDelegateCalled() {
let subject = FindInPageContentScript()
subject.delegate = findInPageDelegate
let currentResult = 1

subject.userContentController(didReceiveMessage: ["currentResult": currentResult])

XCTAssertEqual(findInPageDelegate.didUpdateCurrentResultCalled, 1)
XCTAssertEqual(findInPageDelegate.savedCurrentResult, currentResult)
XCTAssertEqual(findInPageDelegate.didUpdateTotalResultsCalled, 0)
}

func testDidReceiveMessageGivenTotalResultMessageThenDelegateCalled() {
let subject = FindInPageContentScript()
subject.delegate = findInPageDelegate
let totalResult = 10

subject.userContentController(didReceiveMessage: ["totalResults": totalResult])

XCTAssertEqual(findInPageDelegate.didUpdateCurrentResultCalled, 0)
XCTAssertEqual(findInPageDelegate.didUpdateTotalResultsCalled, 1)
XCTAssertEqual(findInPageDelegate.savedTotalResults, totalResult)
}

func testDidReceiveMessageGivenTotalAndCurrentResultsMessageThenDelegateCalled() {
let subject = FindInPageContentScript()
subject.delegate = findInPageDelegate
let totalResult = 15
let currentResult = 20

subject.userContentController(didReceiveMessage: ["totalResults": totalResult, "currentResult": currentResult])

XCTAssertEqual(findInPageDelegate.didUpdateCurrentResultCalled, 1)
XCTAssertEqual(findInPageDelegate.savedCurrentResult, currentResult)
XCTAssertEqual(findInPageDelegate.didUpdateTotalResultsCalled, 1)
XCTAssertEqual(findInPageDelegate.savedTotalResults, totalResult)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/

import Foundation
@testable import WebEngine

class MockFindInPageHelperDelegate: FindInPageHelperDelegate {
var didUpdateCurrentResultCalled = 0
var didUpdateTotalResultsCalled = 0
var savedCurrentResult = 0
var savedTotalResults = 0

func findInPageHelper(didUpdateCurrentResult currentResult: Int) {
savedCurrentResult = currentResult
didUpdateCurrentResultCalled += 1
}

func findInPageHelper(didUpdateTotalResults totalResults: Int) {
savedTotalResults = totalResults
didUpdateTotalResultsCalled += 1
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
// file, You can obtain one at http://mozilla.org/MPL/2.0/

import Foundation
import WebKit
@testable import WebEngine

class MockWKContentScript: WKContentScript {
Expand All @@ -20,8 +19,7 @@ class MockWKContentScript: WKContentScript {
return ["MockWKContentScriptHandler"]
}

func userContentController(_ userContentController: WKUserContentController,
didReceiveScriptMessage message: WKScriptMessage) {
func userContentController(didReceiveMessage message: Any) {
userContentControllerCalled += 1
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,28 @@ import WebKit
@testable import WebEngine

class MockWKContentScriptManager: NSObject, WKContentScriptManager {
var scripts = [String: WKContentScript]()
var addContentScriptCalled = 0
var addContentScriptToPageCalled = 0
var uninstallCalled = 0
var userContentControllerCalled = 0

var savedContentScriptNames = [String]()
var savedContentScriptPageNames = [String]()

func addContentScript(_ script: WKContentScript,
name: String,
forSession session: WKEngineSession) {
scripts[name] = script
savedContentScriptNames.append(name)
addContentScriptCalled += 1
}

func addContentScriptToPage(_ script: WKContentScript,
name: String,
forSession session: WKEngineSession) {
scripts[name] = script
savedContentScriptPageNames.append(name)
addContentScriptToPageCalled += 1
}

Expand Down
10 changes: 10 additions & 0 deletions BrowserKit/Tests/WebEngineTests/Mock/MockWKEngineWebView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ class MockWKEngineWebView: UIView, WKEngineWebView {
var removeFromSuperviewCalled = 0
var addObserverCalled = 0
var removeObserverCalled = 0
var evaluateJavaScriptCalled = 0
var savedJavaScript: String?

var loadFileReadAccessURL: URL?

Expand Down Expand Up @@ -105,4 +107,12 @@ class MockWKEngineWebView: UIView, WKEngineWebView {
forKeyPath keyPath: String) {
removeObserverCalled += 1
}

func evaluateJavaScript(_ javaScript: String,
in frame: WKFrameInfo?,
in contentWorld: WKContentWorld,
completionHandler: ((Result<Any, Error>) -> Void)?) {
evaluateJavaScriptCalled += 1
savedJavaScript = javaScript
}
}
Loading