Skip to content

Commit

Permalink
Merge branch 'develop' into brindy/theme-designer
Browse files Browse the repository at this point in the history
  • Loading branch information
brindy committed Oct 31, 2023
2 parents b826e58 + ff096da commit 18056bb
Show file tree
Hide file tree
Showing 22 changed files with 500 additions and 26 deletions.
1 change: 0 additions & 1 deletion Core/Core.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@


#import <UIKit/UIKit.h>
#import "BloomFilterWrapper.h"


//! Project version number for Core.
Expand Down
7 changes: 5 additions & 2 deletions Core/DefaultVariantManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ extension FeatureName {
// public static let experimentalFeature = FeatureName(rawValue: "experimentalFeature")

public static let fixedUserAgent = FeatureName(rawValue: "fixedUserAgent")
public static let closestUserAgent = FeatureName(rawValue: "closestUserAgent")
}

public struct VariantIOS: Variant {
Expand Down Expand Up @@ -61,8 +62,10 @@ public struct VariantIOS: Variant {
VariantIOS(name: "sc", weight: doNotAllocate, isIncluded: When.always, features: []),
VariantIOS(name: "sd", weight: doNotAllocate, isIncluded: When.always, features: []),
VariantIOS(name: "se", weight: doNotAllocate, isIncluded: When.always, features: []),
VariantIOS(name: "me", weight: 1, isIncluded: When.always, features: []),
VariantIOS(name: "mf", weight: 1, isIncluded: When.always, features: [.fixedUserAgent]),
VariantIOS(name: "me", weight: doNotAllocate, isIncluded: When.always, features: []),
VariantIOS(name: "mf", weight: doNotAllocate, isIncluded: When.always, features: []),
VariantIOS(name: "mg", weight: 1, isIncluded: When.always, features: [.fixedUserAgent]),
VariantIOS(name: "mh", weight: 1, isIncluded: When.always, features: [.closestUserAgent]),
returningUser
]

Expand Down
2 changes: 2 additions & 0 deletions Core/UserAgentManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,8 @@ struct UserAgent {

if DefaultVariantManager().isSupported(feature: .fixedUserAgent) {
return ddgFixedLogic(forUrl: url, isDesktop: isDesktop, privacyConfig: privacyConfig)
} else if DefaultVariantManager().isSupported(feature: .closestUserAgent) {
return closestLogic(forUrl: url, isDesktop: isDesktop, privacyConfig: privacyConfig)
}

switch defaultPolicy(forConfig: privacyConfig) {
Expand Down
34 changes: 34 additions & 0 deletions Core/UserDefaults+NetworkProtection.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
//
// UserDefaults+NetworkProtection.swift
// DuckDuckGo
//
// Copyright © 2023 DuckDuckGo. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

#if NETWORK_PROTECTION

import Foundation

public extension UserDefaults {
static var networkProtectionGroupDefaults: UserDefaults {
let suiteName = "\(Global.groupIdPrefix).netp"
guard let defaults = UserDefaults(suiteName: suiteName) else {
fatalError("Failed to create netP UserDefaults")
}
return defaults
}
}

#endif
40 changes: 29 additions & 11 deletions DuckDuckGo.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -766,6 +766,11 @@
EE8594992A44791C008A6D06 /* NetworkProtectionTunnelController.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE8594982A44791C008A6D06 /* NetworkProtectionTunnelController.swift */; };
EE8E568A2A56BCE400F11DCA /* NetworkProtection in Frameworks */ = {isa = PBXBuildFile; productRef = EE8E56892A56BCE400F11DCA /* NetworkProtection */; };
EE9D68D12AE00CF300B55EF4 /* NetworkProtectionVPNSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE9D68D02AE00CF300B55EF4 /* NetworkProtectionVPNSettingsView.swift */; };
EE9D68D52AE1526600B55EF4 /* NetworkProtectionVPNNotificationsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE9D68D42AE1526600B55EF4 /* NetworkProtectionVPNNotificationsView.swift */; };
EE9D68D82AE15AD600B55EF4 /* UIApplicationExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE9D68D72AE15AD600B55EF4 /* UIApplicationExtension.swift */; };
EE9D68DA2AE1659F00B55EF4 /* NetworkProtectionVPNNotificationsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE9D68D92AE1659F00B55EF4 /* NetworkProtectionVPNNotificationsViewModel.swift */; };
EE9D68DC2AE16AE100B55EF4 /* NotificationsAuthorizationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE9D68DB2AE16AE100B55EF4 /* NotificationsAuthorizationController.swift */; };
EE9D68DE2AE2A65600B55EF4 /* UserDefaults+NetworkProtection.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE9D68DD2AE2A65600B55EF4 /* UserDefaults+NetworkProtection.swift */; };
EEDFE2DA2AC6ED4F00F0E19C /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = EEDFE2DC2AC6ED4F00F0E19C /* Localizable.strings */; };
EEEB80A32A421CE600386378 /* NetworkProtectionPacketTunnelProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEEB80A22A421CE600386378 /* NetworkProtectionPacketTunnelProvider.swift */; };
EEF0F8CC2ABC832300630031 /* NetworkProtectionDebugFeatures.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEF0F8CB2ABC832200630031 /* NetworkProtectionDebugFeatures.swift */; };
Expand Down Expand Up @@ -1298,7 +1303,6 @@
6AC6DAB228804F97002723C0 /* BarsAnimator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BarsAnimator.swift; sourceTree = "<group>"; };
6AC98418288055C1005FA9CA /* BarsAnimatorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BarsAnimatorTests.swift; sourceTree = "<group>"; };
6FB030C7234331B400A10DB9 /* Configuration.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Configuration.xcconfig; path = Configuration/Configuration.xcconfig; sourceTree = "<group>"; };
7B5E1F9D2AB9E1E900DA1172 /* NetworkProtectionDebugFeatures.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkProtectionDebugFeatures.swift; sourceTree = "<group>"; };
83004E7F2193BB8200DA013C /* WKNavigationExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WKNavigationExtension.swift; sourceTree = "<group>"; };
83004E832193E14C00DA013C /* UIAlertControllerExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = UIAlertControllerExtension.swift; path = ../Core/UIAlertControllerExtension.swift; sourceTree = "<group>"; };
83004E852193E5ED00DA013C /* TabViewControllerBrowsingMenuExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabViewControllerBrowsingMenuExtension.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -2336,6 +2340,11 @@
EE7A92862AC6DE4700832A36 /* NetworkProtectionNotificationIdentifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkProtectionNotificationIdentifier.swift; sourceTree = "<group>"; };
EE8594982A44791C008A6D06 /* NetworkProtectionTunnelController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkProtectionTunnelController.swift; sourceTree = "<group>"; };
EE9D68D02AE00CF300B55EF4 /* NetworkProtectionVPNSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkProtectionVPNSettingsView.swift; sourceTree = "<group>"; };
EE9D68D42AE1526600B55EF4 /* NetworkProtectionVPNNotificationsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkProtectionVPNNotificationsView.swift; sourceTree = "<group>"; };
EE9D68D72AE15AD600B55EF4 /* UIApplicationExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIApplicationExtension.swift; sourceTree = "<group>"; };
EE9D68D92AE1659F00B55EF4 /* NetworkProtectionVPNNotificationsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkProtectionVPNNotificationsViewModel.swift; sourceTree = "<group>"; };
EE9D68DB2AE16AE100B55EF4 /* NotificationsAuthorizationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationsAuthorizationController.swift; sourceTree = "<group>"; };
EE9D68DD2AE2A65600B55EF4 /* UserDefaults+NetworkProtection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UserDefaults+NetworkProtection.swift"; sourceTree = "<group>"; };
EEB8FDB92A990AEE00EBEDCF /* Configuration-Alpha.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = "Configuration-Alpha.xcconfig"; path = "Configuration/Configuration-Alpha.xcconfig"; sourceTree = "<group>"; };
EEDFE2DB2AC6ED4F00F0E19C /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = "<group>"; };
EEDFE2DD2AC6ED5B00F0E19C /* bg */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = bg; path = bg.lproj/Localizable.strings; sourceTree = "<group>"; };
Expand Down Expand Up @@ -3537,7 +3546,6 @@
83ED3B8D1FA8E63700B47556 /* README.md */,
83ED3B8C1FA8E61D00B47556 /* ManualTestsScript.md */,
85A313962028E78A00327D00 /* release_notes.txt */,
EEF0F8CA2ABC82E100630031 /* Recovered References */,
);
sourceTree = "<group>";
};
Expand Down Expand Up @@ -4351,6 +4359,7 @@
children = (
EE0153E02A6EABE0002A8B26 /* NetworkProtectionConvenienceInitialisers.swift */,
EE458D0C2AB1DA4600FC651A /* EventMapping+NetworkProtectionError.swift */,
EE9D68DB2AE16AE100B55EF4 /* NotificationsAuthorizationController.swift */,
);
name = Helpers;
sourceTree = "<group>";
Expand Down Expand Up @@ -4414,6 +4423,7 @@
isa = PBXGroup;
children = (
EE7A92862AC6DE4700832A36 /* NetworkProtectionNotificationIdentifier.swift */,
EE9D68DD2AE2A65600B55EF4 /* UserDefaults+NetworkProtection.swift */,
);
name = NetworkProtection;
sourceTree = "<group>";
Expand All @@ -4426,9 +4436,19 @@
name = VPNSettings;
sourceTree = "<group>";
};
EE9D68D62AE1527F00B55EF4 /* VPNNotifications */ = {
isa = PBXGroup;
children = (
EE9D68D42AE1526600B55EF4 /* NetworkProtectionVPNNotificationsView.swift */,
EE9D68D92AE1659F00B55EF4 /* NetworkProtectionVPNNotificationsViewModel.swift */,
);
name = VPNNotifications;
sourceTree = "<group>";
};
EECD94B22A28B8580085C66E /* NetworkProtection */ = {
isa = PBXGroup;
children = (
EE9D68D62AE1527F00B55EF4 /* VPNNotifications */,
EE9D68CF2AE00CE000B55EF4 /* VPNSettings */,
EE458D122ABB651500FC651A /* Debug */,
EE0153E22A6FE031002A8B26 /* Root */,
Expand All @@ -4449,14 +4469,6 @@
name = Status;
sourceTree = "<group>";
};
EEF0F8CA2ABC82E100630031 /* Recovered References */ = {
isa = PBXGroup;
children = (
7B5E1F9D2AB9E1E900DA1172 /* NetworkProtectionDebugFeatures.swift */,
);
name = "Recovered References";
sourceTree = "<group>";
};
EEFD562D2A65B68B00DAEC48 /* Invite */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -5111,6 +5123,7 @@
F1DE78591E5CD2A70058895A /* UIViewExtension.swift */,
F1F5337B1F26A9EF00D80D4F /* UserText.swift */,
986DA94924884B18004A7E39 /* WebViewTransition.swift */,
EE9D68D72AE15AD600B55EF4 /* UIApplicationExtension.swift */,
);
name = UserInterface;
sourceTree = "<group>";
Expand Down Expand Up @@ -6117,6 +6130,7 @@
F1DE78581E5CAE350058895A /* TabViewGridCell.swift in Sources */,
984D035824ACCC6F0066CFB8 /* TabViewListCell.swift in Sources */,
B6BA95C328891E33004ABA20 /* BrowsingMenuAnimator.swift in Sources */,
EE9D68DC2AE16AE100B55EF4 /* NotificationsAuthorizationController.swift in Sources */,
AA3D854923DA1DFB00788410 /* AppIcon.swift in Sources */,
8590CB612684D0600089F6BF /* CookieDebugViewController.swift in Sources */,
319A37152829A55F0079FBCE /* AutofillListItemTableViewCell.swift in Sources */,
Expand All @@ -6139,6 +6153,7 @@
1E8AD1C727BE9B2900ABA377 /* DownloadsListDataSource.swift in Sources */,
3157B43527F497F50042D3D7 /* SaveLoginViewController.swift in Sources */,
853C5F6121C277C7001F7A05 /* global.swift in Sources */,
EE9D68D82AE15AD600B55EF4 /* UIApplicationExtension.swift in Sources */,
F13B4BD31F1822C700814661 /* Tab.swift in Sources */,
F1BE54581E69DE1000FCF649 /* TutorialSettings.swift in Sources */,
1EE52ABB28FB1D6300B750C1 /* UIImageExtension.swift in Sources */,
Expand All @@ -6160,6 +6175,7 @@
C1B7B529289420830098FD6A /* RemoteMessaging.xcdatamodeld in Sources */,
986B16C425E92DF0007D23E8 /* BrowsingMenuViewController.swift in Sources */,
988AC355257E47C100793C64 /* RequeryLogic.swift in Sources */,
EE9D68D52AE1526600B55EF4 /* NetworkProtectionVPNNotificationsView.swift in Sources */,
1E4F4A5A297193DE00625985 /* MainViewController+CookiesManaged.swift in Sources */,
8586A10D24CBA7070049720E /* FindInPageActivity.swift in Sources */,
1E1626072968413B0004127F /* ViewExtension.swift in Sources */,
Expand Down Expand Up @@ -6444,6 +6460,7 @@
8586A11024CCCD040049720E /* TabsBarViewController.swift in Sources */,
F1D796F41E7C2A410019D451 /* BookmarksDelegate.swift in Sources */,
C1B7B52428941F2A0098FD6A /* RemoteMessageRequest.swift in Sources */,
EE9D68DA2AE1659F00B55EF4 /* NetworkProtectionVPNNotificationsViewModel.swift in Sources */,
1E8AD1D727C2E24E00ABA377 /* DownloadsListRowViewModel.swift in Sources */,
C1B0F6422AB08BE9001EAF05 /* MockPrivacyConfiguration.swift in Sources */,
1E865AF0272042DB001C74F3 /* TextSizeSettingsViewController.swift in Sources */,
Expand Down Expand Up @@ -6703,6 +6720,7 @@
CB2A7EF4285383B300885F67 /* AppLastCompiledRulesStore.swift in Sources */,
4B75EA9226A266CB00018634 /* PrintingUserScript.swift in Sources */,
37445F972A155F7C0029F789 /* SyncDataProviders.swift in Sources */,
EE9D68DE2AE2A65600B55EF4 /* UserDefaults+NetworkProtection.swift in Sources */,
CB258D1F29A52B2500DEBA24 /* Configuration.swift in Sources */,
9847C00027A2DDBB00DB07AA /* AppPrivacyConfigurationDataProvider.swift in Sources */,
F143C3281E4A9A0E00CFDE3A /* StringExtension.swift in Sources */,
Expand Down Expand Up @@ -8982,7 +9000,7 @@
repositoryURL = "https://github.com/DuckDuckGo/BrowserServicesKit";
requirement = {
kind = exactVersion;
version = 82.0.0;
version = 82.1.0;
};
};
C14882EB27F211A000D59F0C /* XCRemoteSwiftPackageReference "SwiftSoup" */ = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@
"repositoryURL": "https://github.com/DuckDuckGo/BrowserServicesKit",
"state": {
"branch": null,
"revision": "dd595d952e0076a7a01d086ed2424838dcd985af",
"version": "82.0.0"
"revision": "71e916d070cedcba9ccb3ce9431797260bf5cbea",
"version": "82.1.0"
}
},
{
Expand Down Expand Up @@ -51,8 +51,8 @@
"repositoryURL": "https://github.com/duckduckgo/duckduckgo-autofill.git",
"state": {
"branch": null,
"revision": "6dd7d696d4e666cedb2f1890a46fe53615226646",
"version": "8.4.2"
"revision": "c8e895c8fd50dc76e8d8dc827a636ad77b7f46ff",
"version": "9.0.0"
}
},
{
Expand Down Expand Up @@ -156,7 +156,7 @@
},
{
"package": "TrackerRadarKit",
"repositoryURL": "https://github.com/duckduckgo/TrackerRadarKit.git",
"repositoryURL": "https://github.com/duckduckgo/TrackerRadarKit",
"state": {
"branch": null,
"revision": "4684440d03304e7638a2c8086895367e90987463",
Expand Down
58 changes: 56 additions & 2 deletions DuckDuckGo/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ import Networking
import DDGSync
import SyncDataProviders

#if NETWORK_PROTECTION
import NetworkProtection
#endif

// swiftlint:disable file_length
// swiftlint:disable type_body_length

Expand All @@ -45,6 +49,10 @@ class AppDelegate: UIResponder, UIApplicationDelegate {

private struct ShortcutKey {
static let clipboard = "com.duckduckgo.mobile.ios.clipboard"

#if NETWORK_PROTECTION
static let openVPNSettings = "com.duckduckgo.mobile.ios.vpn.open-settings"
#endif
}

private var testing = false
Expand Down Expand Up @@ -329,6 +337,11 @@ class AppDelegate: UIResponder, UIApplicationDelegate {

syncService.scheduler.notifyAppLifecycleEvent()
fireFailedCompilationsPixelIfNeeded()
refreshShortcuts()
}

func applicationWillResignActive(_ application: UIApplication) {
refreshShortcuts()
}

private func fireAppLaunchPixel() {
Expand Down Expand Up @@ -591,11 +604,20 @@ class AppDelegate: UIResponder, UIApplicationDelegate {

private func handleShortCutItem(_ shortcutItem: UIApplicationShortcutItem) {
os_log("Handling shortcut item: %s", log: .generalLog, type: .debug, shortcutItem.type)
mainViewController?.clearNavigationStack()

autoClear?.applicationWillMoveToForeground()
if shortcutItem.type == ShortcutKey.clipboard, let query = UIPasteboard.general.string {

if shortcutItem.type == ShortcutKey.clipboard, let query = UIPasteboard.general.string {
mainViewController?.clearNavigationStack()
mainViewController?.loadQueryInNewTab(query)
return
}

#if NETWORK_PROTECTION
if shortcutItem.type == ShortcutKey.openVPNSettings {
presentNetworkProtectionStatusSettingsModal()
}
#endif
}

private func removeEmailWaitlistState() {
Expand Down Expand Up @@ -623,6 +645,25 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
private var mainViewController: MainViewController? {
return window?.rootViewController as? MainViewController
}

func refreshShortcuts() {
#if NETWORK_PROTECTION
guard NetworkProtectionKeychainTokenStore().isFeatureActivated else {
return
}

let items = [
UIApplicationShortcutItem(type: ShortcutKey.openVPNSettings,
localizedTitle: UserText.netPOpenVPNQuickAction,
localizedSubtitle: nil,
icon: UIApplicationShortcutIcon(templateImageName: "VPN-16"),
userInfo: nil)
]

UIApplication.shared.shortcutItems = items
#endif
}

}

extension AppDelegate: BlankSnapshotViewRecoveringDelegate {
Expand Down Expand Up @@ -708,6 +749,19 @@ extension AppDelegate: UNUserNotificationCenterDelegate {
private func presentSettings(with viewController: UIViewController) {
guard let window = window, let rootViewController = window.rootViewController as? MainViewController else { return }

if let navigationController = rootViewController.presentedViewController as? UINavigationController {
if let lastViewController = navigationController.viewControllers.last, lastViewController.isKind(of: type(of: viewController)) {
// Avoid presenting dismissing and re-presenting the view controller if it's already visible:
return
} else {
// Otherwise, replace existing view controllers with the presented one:
navigationController.popToRootViewController(animated: false)
navigationController.pushViewController(viewController, animated: false)
return
}
}

// If the previous checks failed, make sure the nav stack is reset and present the view controller from scratch:
rootViewController.clearNavigationStack()

// Give the `clearNavigationStack` call time to complete.
Expand Down
15 changes: 15 additions & 0 deletions DuckDuckGo/Assets.xcassets/VPN-16.imageset/Contents.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"images" : [
{
"filename" : "VPN-16.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"template-rendering-intent" : "template"
}
}
Binary file not shown.
1 change: 1 addition & 0 deletions DuckDuckGo/DuckDuckGo.entitlements
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
<string>$(GROUP_ID_PREFIX).statistics</string>
<string>$(GROUP_ID_PREFIX).database</string>
<string>$(GROUP_ID_PREFIX).apptp</string>
<string>$(GROUP_ID_PREFIX).netp</string>
</array>
</dict>
</plist>
10 changes: 10 additions & 0 deletions DuckDuckGo/NetworkProtectionConvenienceInitialisers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -58,4 +58,14 @@ extension NetworkProtectionCodeRedemptionCoordinator {
}
}

extension NetworkProtectionVPNNotificationsViewModel {
convenience init() {
let notificationsSettingsStore = NetworkProtectionNotificationsSettingsUserDefaultsStore(userDefaults: .networkProtectionGroupDefaults)
self.init(
notificationsAuthorization: NotificationsAuthorizationController(),
notificationsSettingsStore: notificationsSettingsStore
)
}
}

#endif
Loading

0 comments on commit 18056bb

Please sign in to comment.