Skip to content

Commit

Permalink
Autofill Script performance improvements (#740)
Browse files Browse the repository at this point in the history
Task/Issue URL: https://app.asana.com/0/414709148257752/1206900948756760/f
iOS PR: duckduckgo/iOS#2625
macOS PR: duckduckgo/macos-browser#2481
What kind of version bump will this require?: Patch

Description:
Greatly reduces the amount of data passed into the autofill script to improve performance and resolve occasional out of memory crashes
  • Loading branch information
amddg44 authored Mar 25, 2024
1 parent c34a4d0 commit 0509e0c
Show file tree
Hide file tree
Showing 2 changed files with 137 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

import Foundation
import Autofill
import Common

public protocol AutofillUserScriptSourceProvider {
var source: String { get }
Expand Down Expand Up @@ -101,15 +102,59 @@ public class DefaultAutofillSourceProvider: AutofillUserScriptSourceProvider {
}

private func buildReplacementsData() -> ProviderData? {
guard let userUnprotectedDomains = try? JSONEncoder().encode(privacyConfigurationManager.privacyConfig.userUnprotectedDomains),
guard let filteredPrivacyConfigData = filteredDataFrom(configData: privacyConfigurationManager.currentConfig,
keepingTopLevelKeys: ["features", "unprotectedTemporary"],
andSubKey: "autofill",
inTopLevelKey: "features"),
let userUnprotectedDomains = try? JSONEncoder().encode(privacyConfigurationManager.privacyConfig.userUnprotectedDomains),
let jsonProperties = try? JSONEncoder().encode(properties) else {
return nil
}
return ProviderData(privacyConfig: privacyConfigurationManager.currentConfig,

return ProviderData(privacyConfig: filteredPrivacyConfigData,
userUnprotectedDomains: userUnprotectedDomains,
userPreferences: jsonProperties)
}

/// `contentScope` only needs these properties from the privacy config, so creating a filtered version to improve performance
/// {
/// features: {
/// autofill: {
/// state: 'enabled',
/// exceptions: []
/// }
/// },
/// unprotectedTemporary: []
/// }
private func filteredDataFrom(configData: Data, keepingTopLevelKeys topLevelKeys: [String], andSubKey subKey: String, inTopLevelKey topLevelKey: String) -> Data? {
do {
if let jsonDict = try JSONSerialization.jsonObject(with: configData, options: []) as? [String: Any] {
var filteredDict = [String: Any]()

// Keep the specified top-level keys
for key in topLevelKeys {
if let value = jsonDict[key] {
filteredDict[key] = value
}
}

// Handle the nested dictionary for a specific top-level key to keep only the sub-key
if let nestedDict = jsonDict[topLevelKey] as? [String: Any],
let valueToKeep = nestedDict[subKey] {
filteredDict[topLevelKey] = [subKey: valueToKeep]
}

// Convert filtered dictionary back to Data
let filteredData = try JSONSerialization.data(withJSONObject: filteredDict, options: [])
return filteredData
}
} catch {
os_log(.debug, "Error during JSON serialization of privacy config: \(error.localizedDescription)")
}

return nil
}

public class Builder {
private var privacyConfigurationManager: PrivacyConfigurationManaging
private var properties: ContentScopeProperties
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,65 @@ final class AutofillUserScriptSourceProviderTests: XCTestCase {
{
"features": {
"autofill": {
"status": "enabled",
"exceptions": []
"state": "enabled",
"features": {
"credentialsSaving": {
"state": "enabled",
"minSupportedVersion": "7.74.0"
},
"credentialsAutofill": {
"state": "enabled",
"minSupportedVersion": "7.74.0"
},
"inlineIconCredentials": {
"state": "enabled",
"minSupportedVersion": "7.74.0"
},
"accessCredentialManagement": {
"state": "enabled",
"minSupportedVersion": "7.74.0"
},
"autofillPasswordGeneration": {
"state": "enabled",
"minSupportedVersion": "7.75.0"
},
"onByDefault": {
"state": "enabled",
"minSupportedVersion": "7.93.0",
"rollout": {
"steps": [
{
"percent": 1
},
{
"percent": 10
},
{
"percent": 100
}
]
}
}
},
"hash": "ffaa2e81fb2bf264cb5ce2dadac549e1"
},
"contentBlocking": {
"state": "enabled",
"exceptions": [
{
"domain": "test-domain.com"
}
],
"hash": "910e25ffe4d683b3c708a1578d097a16"
},
"voiceSearch": {
"exceptions": [],
"state": "disabled",
"hash": "728493ef7a1488e4781656d3f9db84aa"
}
},
"unprotectedTemporary": []
"unprotectedTemporary": [],
"unprotectedOtherKey": []
}
""".data(using: .utf8)!
lazy var privacyConfig = AutofillTestHelper.preparePrivacyConfig(embeddedConfig: embeddedConfig)
Expand All @@ -53,4 +107,37 @@ final class AutofillUserScriptSourceProviderTests: XCTestCase {
XCTAssertNotNil(runtimeConfiguration)
XCTAssertFalse(runtimeConfiguration!.isEmpty)
}

func testWhenBuildRuntimeConfigurationThenContentScopeContainsRequiredAutofillKeys() throws {
let runtimeConfiguration = DefaultAutofillSourceProvider.Builder(privacyConfigurationManager: privacyConfig,
properties: properties)
.build()
.buildRuntimeConfigResponse()

let jsonData = runtimeConfiguration!.data(using: .utf8)!
let json = try JSONSerialization.jsonObject(with: jsonData, options: []) as? [String: Any]
let success = json?["success"] as? [String: Any]
let contentScope = success?["contentScope"] as? [String: Any]
let features = contentScope?["features"] as? [String: Any]
XCTAssertNotNil(features?["autofill"] as? [String: Any])
XCTAssertNotNil(contentScope?["unprotectedTemporary"] as? [Any])
XCTAssertNil(features?["contentBlocking"])
}

func testWhenBuildRuntimeConfigurationThenContentScopeDoesNotContainUnnecessaryKeys() throws {
let runtimeConfiguration = DefaultAutofillSourceProvider.Builder(privacyConfigurationManager: privacyConfig,
properties: properties)
.build()
.buildRuntimeConfigResponse()

let jsonData = runtimeConfiguration!.data(using: .utf8)!
let json = try JSONSerialization.jsonObject(with: jsonData, options: []) as? [String: Any]
let success = json?["success"] as? [String: Any]
let contentScope = success?["contentScope"] as? [String: Any]
XCTAssertNil(contentScope?["unprotectedOtherKey"])

let features = contentScope?["features"] as? [String: Any]
XCTAssertNil(features?["contentBlocking"])
XCTAssertNil(features?["voiceSearch"])
}
}

0 comments on commit 0509e0c

Please sign in to comment.