Skip to content

Commit

Permalink
Merge branch 'main' into sam/remove-email-waitlist
Browse files Browse the repository at this point in the history
* main:
  Add delegated autofill isEnabled check and pixel params (#130)
  Update to GRDB 1.2.0 (#119)
  Fire pixel when autoprompt is dismissed (#124)
  Tweaks and bug fixes for #126 (#128)
  • Loading branch information
samsymons committed Aug 18, 2022
2 parents 188fd4e + 05ed8ae commit 1e8a1df
Show file tree
Hide file tree
Showing 10 changed files with 99 additions and 16 deletions.
8 changes: 4 additions & 4 deletions Package.resolved
Expand Up @@ -15,17 +15,17 @@
"repositoryURL": "https://github.com/duckduckgo/duckduckgo-autofill.git",
"state": {
"branch": null,
"revision": "27a4cff621893ef9d1b96ec62c99b137a3a73450",
"version": "4.7.1"
"revision": "d29c5abc7f473b69f3d81d8a15e137ed3aab029d",
"version": "5.0.1"
}
},
{
"package": "GRDB",
"repositoryURL": "https://github.com/duckduckgo/GRDB.swift.git",
"state": {
"branch": null,
"revision": "9efe5a515acff8b73f69a31a65fc2bce2a823219",
"version": "1.1.0"
"revision": "f465dd5c0bf590cdb9bee4e96c53b1719ee30730",
"version": "1.2.0"
}
},
{
Expand Down
4 changes: 2 additions & 2 deletions Package.swift
Expand Up @@ -14,8 +14,8 @@ let package = Package(
.library(name: "BrowserServicesKit", targets: ["BrowserServicesKit"])
],
dependencies: [
.package(name: "Autofill", url: "https://github.com/duckduckgo/duckduckgo-autofill.git", .exact("4.7.1")),
.package(name: "GRDB", url: "https://github.com/duckduckgo/GRDB.swift.git", .exact("1.1.0")),
.package(name: "Autofill", url: "https://github.com/duckduckgo/duckduckgo-autofill.git", .exact("5.0.1")),
.package(name: "GRDB", url: "https://github.com/duckduckgo/GRDB.swift.git", .exact("1.2.0")),
.package(url: "https://github.com/duckduckgo/TrackerRadarKit", .exact("1.1.1")),
.package(name: "Punycode", url: "https://github.com/gumob/PunycodeSwift.git", .exact("2.1.0")),
.package(url: "https://github.com/duckduckgo/content-scope-scripts", .exact("2.3.0"))
Expand Down
Expand Up @@ -41,6 +41,7 @@ public protocol AutofillSecureVaultDelegate: AnyObject {
completionHandler: @escaping ([SecureVaultModels.WebsiteAccount]) -> Void)
func autofillUserScript(_: AutofillUserScript, didRequestCredentialsForDomain: String,
subType: AutofillUserScript.GetAutofillDataSubType,
trigger: AutofillUserScript.GetTriggerType,
completionHandler: @escaping (SecureVaultModels.WebsiteCredentials?, RequestVaultCredentialsAction) -> Void)

func autofillUserScript(_: AutofillUserScript, didRequestCredentialsForAccount accountId: Int64,
Expand Down Expand Up @@ -329,6 +330,7 @@ extension AutofillUserScript {
struct GetAutofillDataRequest: Codable {
let mainType: GetAutofillDataMainType
let subType: GetAutofillDataSubType
let trigger: GetTriggerType
}

// https://github.com/duckduckgo/duckduckgo-autofill/blob/main/src/deviceApiCalls/schemas/getAutofillData.params.json
Expand All @@ -342,6 +344,12 @@ extension AutofillUserScript {
case username
case password
}

// https://github.com/duckduckgo/duckduckgo-autofill/blob/main/src/deviceApiCalls/schemas/getAutofillData.params.json
public enum GetTriggerType: String, Codable {
case userInitiated
case autoprompt
}

// https://github.com/duckduckgo/duckduckgo-autofill/blob/main/docs/runtime.ios.md#getautofilldatarequest
func getAutofillData(_ message: AutofillMessage, _ replyHandler: @escaping MessageReplyHandler) {
Expand All @@ -350,7 +358,7 @@ extension AutofillUserScript {
}

let domain = hostForMessage(message)
vaultDelegate?.autofillUserScript(self, didRequestCredentialsForDomain: domain, subType: request.subType) { credentials, action in
vaultDelegate?.autofillUserScript(self, didRequestCredentialsForDomain: domain, subType: request.subType, trigger: request.trigger) { credentials, action in
let response = RequestVaultCredentialsForDomainResponse.responseFromSecureVaultWebsiteCredentials(credentials, action: action)

if let json = try? JSONEncoder().encode(response), let jsonString = String(data: json, encoding: .utf8) {
Expand Down
Expand Up @@ -67,7 +67,8 @@ public class AdClickAttributionDetection {
var vendorDomain: String?
if attributionFeature.isDomainDetectionEnabled,
let adDomainParameterName = attributionFeature.attributionDomainParameterName(for: url),
let domainFromParameter = try? url.getParameter(name: adDomainParameterName) {
let domainFromParameter = try? url.getParameter(name: adDomainParameterName),
!domainFromParameter.isEmpty {

if let eTLDp1 = tld.eTLDplus1(domainFromParameter)?.lowercased() {
vendorDomain = eTLDp1
Expand Down
Expand Up @@ -37,9 +37,12 @@ public enum AdClickAttributionDebugEvents {

case adAttributionGlobalAttributedRulesDoNotExist
case adAttributionCompilationFailedForAttributedRulesList
case adAttributionLogicRequestingAttributionTimedOut
case adAttributionLogicUnexpectedStateOnInheritedAttribution
case adAttributionLogicUnexpectedStateOnRulesCompiled
case adAttributionLogicUnexpectedStateOnRulesCompilationFailed
case adAttributionLogicWrongVendorOnSuccessfulCompilation
case adAttributionLogicWrongVendorOnFailedCompilation
case adAttributionDetectionInvalidDomainInParameter
case adAttributionDetectionHeuristicsDidNotMatchDomain

Expand Down
Expand Up @@ -57,6 +57,8 @@ public class AdClickAttributionLogic {
public private(set) var state = State.noAttribution
private var registerFirstActivity = false

private var attributionTimeout: DispatchWorkItem?

public weak var delegate: AdClickAttributionLogicDelegate?

public init(featureConfig: AdClickAttributing,
Expand Down Expand Up @@ -181,6 +183,7 @@ public class AdClickAttributionLogic {
}
guard expectedVendor == vendor else {
os_log(.debug, log: log, "Attributed Rules received for wrong vendor")
errorReporting?.fire(.adAttributionLogicWrongVendorOnSuccessfulCompilation)
return
}
state = .activeAttribution(vendor: vendor, session: session, rules: rules)
Expand All @@ -198,6 +201,7 @@ public class AdClickAttributionLogic {
return
}
guard expectedVendor == vendor else {
errorReporting?.fire(.adAttributionLogicWrongVendorOnFailedCompilation)
return
}
state = .noAttribution
Expand All @@ -222,7 +226,10 @@ public class AdClickAttributionLogic {
state = .preparingAttribution(vendor: vendorHost,
session: SessionInfo(start: attributionStartedAt),
completionBlocks: completionBlocks)

scheduleTimeout(forVendor: vendorHost)
rulesProvider.requestAttribution(forVendor: vendorHost) { [weak self] rules in
self?.cancelTimeout()
if let rules = rules {
self?.onAttributedRulesCompiled(forVendor: vendorHost, rules)
} else {
Expand All @@ -231,6 +238,24 @@ public class AdClickAttributionLogic {
}
}

private func scheduleTimeout(forVendor vendor: String) {
let timeoutWorkItem = DispatchWorkItem { [weak self] in
self?.onAttributedRulesCompilationFailed(forVendor: vendor)
self?.attributionTimeout = nil

self?.errorReporting?.fire(.adAttributionLogicRequestingAttributionTimedOut)
}
self.attributionTimeout?.cancel()
self.attributionTimeout = timeoutWorkItem
DispatchQueue.main.asyncAfter(deadline: .now() + 4.0,
execute: timeoutWorkItem)
}

private func cancelTimeout() {
attributionTimeout?.cancel()
attributionTimeout = nil
}

/// Respond to new requests for attribution
private func onAttributionRequested(forVendor vendorHost: String) {

Expand Down
Expand Up @@ -88,6 +88,7 @@ public class AdClickAttributionRulesProvider: AdClickAttributionRulesProviding {
guard let globalAttributionRules = compiledRulesSource.currentAttributionRules else {
errorReporting?.fire(.adAttributionGlobalAttributedRulesDoNotExist)
os_log(.error, log: log, "Global attribution list does not exist")
completion(nil)
return
}

Expand Down
10 changes: 9 additions & 1 deletion Sources/BrowserServicesKit/SecureVault/SecureVaultManager.swift
Expand Up @@ -35,12 +35,15 @@ public struct AutofillData {
}

public protocol SecureVaultManagerDelegate: SecureVaultErrorReporting {

func secureVaultManagerIsEnabledStatus(_: SecureVaultManager) -> Bool

func secureVaultManager(_: SecureVaultManager, promptUserToStoreAutofillData data: AutofillData)

func secureVaultManager(_: SecureVaultManager,
promptUserToAutofillCredentialsForDomain domain: String,
withAccounts accounts: [SecureVaultModels.WebsiteAccount],
withTrigger trigger: AutofillUserScript.GetTriggerType,
completionHandler: @escaping (SecureVaultModels.WebsiteAccount?) -> Void)

func secureVaultManagerShouldAutomaticallyUpdateCredentialsWithoutUsername(_: SecureVaultManager) -> Bool
Expand Down Expand Up @@ -74,6 +77,10 @@ extension SecureVaultManager: AutofillSecureVaultDelegate {
[SecureVaultModels.CreditCard]) -> Void) {

do {
guard let delegate = delegate, delegate.secureVaultManagerIsEnabledStatus(self) else {
completionHandler([], [], [])
return
}
let vault = try self.vault ?? SecureVaultFactory.default.makeVault(errorReporter: self.delegate)
let accounts = try vault.accountsFor(domain: domain)
let identities = try vault.identities()
Expand Down Expand Up @@ -127,6 +134,7 @@ extension SecureVaultManager: AutofillSecureVaultDelegate {
public func autofillUserScript(_: AutofillUserScript,
didRequestCredentialsForDomain domain: String,
subType: AutofillUserScript.GetAutofillDataSubType,
trigger: AutofillUserScript.GetTriggerType,
completionHandler: @escaping (SecureVaultModels.WebsiteCredentials?, RequestVaultCredentialsAction) -> Void) {
do {
let vault = try self.vault ?? SecureVaultFactory.default.makeVault(errorReporter: self.delegate)
Expand All @@ -146,7 +154,7 @@ extension SecureVaultManager: AutofillSecureVaultDelegate {
return
}

delegate?.secureVaultManager(self, promptUserToAutofillCredentialsForDomain: domain, withAccounts: accounts) { account in
delegate?.secureVaultManager(self, promptUserToAutofillCredentialsForDomain: domain, withAccounts: accounts, withTrigger: trigger) { account in

guard let accountID = account?.id else {
completionHandler(nil, .none)
Expand Down
Expand Up @@ -353,11 +353,21 @@ class AutofillVaultUserScriptTests: XCTestCase {
var body = encryptedMessagingParams
body["mainType"] = "credentials"
body["subType"] = "username"
body["trigger"] = "userInitiated"

let mockWebView = MockWebView()
let message = MockAutofillMessage(name: "getAutofillData", body: body, host: "example.com", webView: mockWebView)

userScript.processMessage(userContentController, didReceive: message)

let predicate = NSPredicate(block: { _, _ -> Bool in
return !delegate.receivedCallbacks.isEmpty
})

let expectation = XCTNSPredicateExpectation(predicate: predicate, object: delegate.receivedCallbacks)

wait(for: [expectation], timeout: 5)

XCTAssertEqual(delegate.lastSubtype, AutofillUserScript.GetAutofillDataSubType.username)
}

Expand All @@ -369,6 +379,7 @@ class AutofillVaultUserScriptTests: XCTestCase {
var body = encryptedMessagingParams
body["mainType"] = "creditCards" // <- unsupported main type
body["subType"] = "username"
body["trigger"] = "userInitiated"

let mockWebView = MockWebView()
let message = MockAutofillMessage(name: "getAutofillData", body: body, host: "example.com", webView: mockWebView)
Expand All @@ -385,6 +396,7 @@ class AutofillVaultUserScriptTests: XCTestCase {
var body = encryptedMessagingParams
body["mainType"] = "credentials"
body["subType"] = "anything_else"
body["trigger"] = "userInitiated"

let mockWebView = MockWebView()
let message = MockAutofillMessage(name: "getAutofillData", body: body, host: "example.com", webView: mockWebView)
Expand All @@ -396,35 +408,43 @@ class AutofillVaultUserScriptTests: XCTestCase {

class MockSecureVaultDelegate: AutofillSecureVaultDelegate {

enum CallbackType {
case didRequestPasswordManagerForDomain
case didRequestStoreDataForDomain
case didRequestAccountsForDomain
case didRequestCredentialsForDomain
}

var receivedCallbacks: [CallbackType] = []

var lastDomain: String?
var lastUsername: String?
var lastPassword: String?
var lastSubtype: AutofillUserScript.GetAutofillDataSubType?

func autofillUserScript(_: AutofillUserScript, didRequestPasswordManagerForDomain domain: String) {
lastDomain = domain
receivedCallbacks.append(.didRequestPasswordManagerForDomain)
}

func autofillUserScript(_: AutofillUserScript, didRequestStoreDataForDomain domain: String, data: AutofillUserScript.DetectedAutofillData) {
lastDomain = domain
lastUsername = data.credentials?.username
lastPassword = data.credentials?.password
receivedCallbacks.append(.didRequestStoreDataForDomain)
}

func autofillUserScript(_: AutofillUserScript,
didRequestAccountsForDomain domain: String,
completionHandler: @escaping ([SecureVaultModels.WebsiteAccount]) -> Void) {
lastDomain = domain
receivedCallbacks.append(.didRequestAccountsForDomain)
}

func autofillUserScript(_: AutofillUserScript,
didRequestCredentialsForAccount accountId: Int64,
completionHandler: @escaping (SecureVaultModels.WebsiteCredentials?) -> Void) {
}

func autofillUserScript(_: AutofillUserScript, didRequestCredentialsForDomain: String,
completionHandler: @escaping (SecureVaultModels.WebsiteCredentials?, RequestVaultCredentialsAction) -> Void) {
}

func autofillUserScript(_: AutofillUserScript,
didRequestAutoFillInitDataForDomain domain: String,
Expand All @@ -446,8 +466,12 @@ class MockSecureVaultDelegate: AutofillSecureVaultDelegate {
func autofillUserScript(_ script: AutofillUserScript,
didRequestCredentialsForDomain: String,
subType: AutofillUserScript.GetAutofillDataSubType,
trigger: AutofillUserScript.GetTriggerType,
completionHandler: @escaping (SecureVaultModels.WebsiteCredentials?, RequestVaultCredentialsAction) -> Void) {
lastSubtype = subType
receivedCallbacks.append(.didRequestCredentialsForDomain)

completionHandler(nil, .none)
}
}

Expand Down

0 comments on commit 1e8a1df

Please sign in to comment.