Skip to content

Commit

Permalink
Merge branch 'main' into sparaipan/fxios-8029-Firefox-Suggest-settings
Browse files Browse the repository at this point in the history
  • Loading branch information
PARAIPAN9 committed Feb 20, 2024
2 parents 599324a + b5a40f0 commit 345b084
Show file tree
Hide file tree
Showing 49 changed files with 53,492 additions and 452 deletions.
3 changes: 2 additions & 1 deletion .swiftlint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,8 @@ excluded: # paths to ignore during linting. Takes precedence over `included`.
- BrowserKit/Package.swift
- BrowserKit/.build/
- firefox-ios/Client/ContentBlocker/ContentBlockerGenerator/Package.swift
- Package.swift
- Package.swift
- firefox-ios/Build/Intermediates.noindex/Client.build/Fennec-iphoneos/WidgetKitExtension.build/DerivedSources/IntentDefinitionGenerated/WidgetIntents/*

included:
- /Users/vagrant/git
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,16 +48,17 @@ public class SecondaryRoundedButton: ResizableButton, ThemeApplicable {
}
updatedConfiguration.baseForegroundColor = foregroundColor

updatedConfiguration.titleTextAttributesTransformer = UIConfigurationTextAttributesTransformer { incoming in
let transformer = UIConfigurationTextAttributesTransformer { [weak foregroundColor] incoming in
var container = incoming

container.foregroundColor = updatedConfiguration.baseForegroundColor
container.foregroundColor = foregroundColor
container.font = DefaultDynamicFontHelper.preferredBoldFont(
withTextStyle: .callout,
size: UX.buttonFontSize
)
return container
}
updatedConfiguration.titleTextAttributesTransformer = transformer

configuration = updatedConfiguration
}
Expand Down
6 changes: 3 additions & 3 deletions bitrise.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2116,17 +2116,17 @@ app:
BITRISE_NIGHTLY_VERSION: '9000'
- opts:
is_expand: false
BITRISE_RELEASE_VERSION: '124.0'
BITRISE_RELEASE_VERSION: '125.0'
- opts:
is_expand: false
BITRISE_BETA_VERSION: '124.0'
BITRISE_BETA_VERSION: '125.0'

trigger_map:
- push_branch: main
pipeline: pipeline_build_and_test
- push_branch: epic-branch/*
pipeline: pipeline_build_and_test
- push_branch: release/v124
- push_branch: release/v125
pipeline: pipeline_build_and_test
- pull_request_target_branch: main
pipeline: pipeline_build_and_test
Expand Down
7 changes: 6 additions & 1 deletion firefox-ios/Client.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,7 @@
394CF6CF1BAA493C00906917 /* DefaultSuggestedSites.swift in Sources */ = {isa = PBXBuildFile; fileRef = 394CF6CE1BAA493C00906917 /* DefaultSuggestedSites.swift */; };
3964B09A1EA8F06F00F2EEF4 /* FeatureSwitch.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3964B0991EA8F06F00F2EEF4 /* FeatureSwitch.swift */; };
3964B09C1EA8F32C00F2EEF4 /* FeatureSwitchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3964B09B1EA8F32C00F2EEF4 /* FeatureSwitchTests.swift */; };
39673BC12B6D82F400653F4A /* FxNimbusMessaging.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39673BC02B6D82F400653F4A /* FxNimbusMessaging.swift */; };
396CDB55203C5B870034A3A3 /* TabTrayController+KeyCommands.swift in Sources */ = {isa = PBXBuildFile; fileRef = 396CDB54203C5B870034A3A3 /* TabTrayController+KeyCommands.swift */; };
396E38CC1EE0816C00CC180F /* Profile.swift in Sources */ = {isa = PBXBuildFile; fileRef = D34DC84D1A16C40C00D49B7B /* Profile.swift */; };
396E38DD1EE081DA00CC180F /* SyncDisplayState.swift in Sources */ = {isa = PBXBuildFile; fileRef = E60D03171D511398002FE3F6 /* SyncDisplayState.swift */; };
Expand Down Expand Up @@ -2585,6 +2586,7 @@
3964B0991EA8F06F00F2EEF4 /* FeatureSwitch.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FeatureSwitch.swift; sourceTree = "<group>"; };
3964B09B1EA8F32C00F2EEF4 /* FeatureSwitchTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FeatureSwitchTests.swift; sourceTree = "<group>"; };
3964F5FB2656D2B500065278 /* initial_experiments.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; name = initial_experiments.json; path = Client/Experiments/initial_experiments.json; sourceTree = SOURCE_ROOT; };
39673BC02B6D82F400653F4A /* FxNimbusMessaging.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FxNimbusMessaging.swift; sourceTree = "<group>"; };
396CDB54203C5B870034A3A3 /* TabTrayController+KeyCommands.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TabTrayController+KeyCommands.swift"; sourceTree = "<group>"; };
397848DB1ED86605004C0C0B /* NotificationService.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = NotificationService.appex; sourceTree = BUILT_PRODUCTS_DIR; };
397848DD1ED86605004C0C0B /* NotificationService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationService.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -10140,6 +10142,7 @@
isa = PBXGroup;
children = (
455880A027B42F720078DEBB /* FxNimbus.swift */,
39673BC02B6D82F400653F4A /* FxNimbusMessaging.swift */,
C86E4F702493BA8E0087BFD9 /* Metrics.swift */,
);
path = Generated;
Expand Down Expand Up @@ -12906,6 +12909,7 @@
);
outputPaths = (
"$(SRCROOT)/Client/Generated/FxNimbus.swift",
"$(SRCROOT)/Client/Generated/FxNimbusMessaging.swift",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
Expand Down Expand Up @@ -13714,6 +13718,7 @@
CA7FC7D324A6A9B70012F347 /* PasswordManagerDataSourceHelper.swift in Sources */,
43AB6FA425DC53D30016B015 /* LabelButtonHeaderView.swift in Sources */,
43D16B7C29831CD0009F8279 /* CreditCardItemRow.swift in Sources */,
39673BC12B6D82F400653F4A /* FxNimbusMessaging.swift in Sources */,
965C3C942933A860006499ED /* LaunchSessionProvider.swift in Sources */,
C8163851268A0899004C7160 /* AddCredentialViewController.swift in Sources */,
8A19ACB22A3290AE001C2147 /* ClearPrivateDataSetting.swift in Sources */,
Expand Down Expand Up @@ -21036,7 +21041,7 @@
repositoryURL = "https://github.com/mozilla/rust-components-swift.git";
requirement = {
kind = exactVersion;
version = 124.0.20240215050333;
version = 125.0.20240216050339;
};
};
435C85EE2788F4D00072B526 /* XCRemoteSwiftPackageReference "glean-swift" */ = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,8 +95,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/mozilla/rust-components-swift.git",
"state" : {
"revision" : "dc8e209f5a191ab1fa8c8f5e91f4b6014e8459a8",
"version" : "124.0.20240215050333"
"revision" : "8d6e0c0d2e65e9bddf9e3430162ce570789ea158",
"version" : "125.0.20240216050339"
}
},
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ class LaunchScreenViewModel {
weak var delegate: LaunchFinishedLoadingDelegate?

init(profile: Profile = AppContainer.shared.resolve(),
messageManager: GleanPlumbMessageManagerProtocol = GleanPlumbMessageManager.shared,
messageManager: GleanPlumbMessageManagerProtocol = Experiments.messaging,
onboardingModel: OnboardingViewModel = NimbusOnboardingFeatureLayer().getOnboardingModel(for: .upgrade)) {
self.introScreenManager = IntroScreenManager(prefs: profile.prefs)
let telemetryUtility = OnboardingTelemetryUtility(with: onboardingModel)
Expand Down
6 changes: 6 additions & 0 deletions firefox-ios/Client/Experiments/Experiments.swift
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,12 @@ enum Experiments {
}
}

extension Experiments {
public static var messaging: GleanPlumbMessageManagerProtocol = {
GleanPlumbMessageManager()
}()
}

private extension AppBuildChannel {
var nimbusString: String {
switch self {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ struct GleanPlumbMessage {
metadata.isExpired || metadata.impressions >= style.maxDisplayCount
}

var isInteractedWith: Bool {
metadata.isExpired || metadata.dismissals > 0
}

var buttonLabel: String? {
data.buttonLabel
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,6 @@ class GleanPlumbMessageManager: GleanPlumbMessageManagerProtocol {
typealias MessagingKey = TelemetryWrapper.EventExtraKey

private enum CreateMessageError: Error {
case expired
case malformed
}

Expand All @@ -75,7 +74,7 @@ class GleanPlumbMessageManager: GleanPlumbMessageManagerProtocol {
messagingUtility: NimbusMessagingEvaluationUtility = NimbusMessagingEvaluationUtility(),
messagingStore: GleanPlumbMessageStoreProtocol = GleanPlumbMessageStore(),
applicationHelper: ApplicationHelper = DefaultApplicationHelper(),
messagingFeature: FeatureHolder<Messaging> = FxNimbus.shared.features.messaging
messagingFeature: FeatureHolder<Messaging> = FxNimbusMessaging.shared.features.messaging
) {
self.helperUtility = helperUtility
self.evaluationUtility = messagingUtility
Expand All @@ -99,40 +98,39 @@ class GleanPlumbMessageManager: GleanPlumbMessageManagerProtocol {

public func getNextMessage(
for surface: MessageSurfaceId,
availableMessages: [GleanPlumbMessage]
availableMessages messages: [GleanPlumbMessage]
) -> GleanPlumbMessage? {
let availableMessages = messages.filter {
$0.data.surface == surface
}.filter {
!$0.isExpired && !$0.isInteractedWith
}
// If `NimbusMessagingHelper` creation fails, we cannot continue with this
// feature! For that reason, return `nil`. We need to recreate the helper
// for each request to get a message because device context can change.
guard let messagingHelper = helperUtility.createNimbusMessagingHelper() else { return nil }
var jexlCache = [String: Bool]()

var excluded: Set<String> = []
return getNextMessage(for: surface,
availableMessages: availableMessages,
excluded: &excluded,
messagingHelper: messagingHelper,
jexlCache: &jexlCache)
messagingHelper: messagingHelper)
}

// TODO: inout removal ticket https://mozilla-hub.atlassian.net/browse/FXIOS-6572
private func getNextMessage(
for surface: MessageSurfaceId,
availableMessages: [GleanPlumbMessage],
excluded: inout Set<String>,
messagingHelper: NimbusMessagingHelperProtocol,
jexlCache: inout [String: Bool]
messagingHelper: NimbusMessagingHelperProtocol
) -> GleanPlumbMessage? {
let feature = messagingFeature.value()
let message = availableMessages.first { message in
guard message.surface == surface &&
!excluded.contains(message.id) else {
if excluded.contains(message.id) {
return false
}
do {
return try evaluationUtility.isMessageEligible(message,
messageHelper: messagingHelper,
jexlCache: &jexlCache)
messageHelper: messagingHelper)
} catch {
return false
}
Expand All @@ -155,16 +153,15 @@ class GleanPlumbMessageManager: GleanPlumbMessageManagerProtocol {
// because they're not displayed.
messagingStore.onMessageDisplayed(message)

switch feature.onControl {
switch messagingFeature.value().onControl {
case .showNone:
return nil
case .showNextMessage:
excluded.insert(message.id)
return getNextMessage(for: surface,
availableMessages: availableMessages,
excluded: &excluded,
messagingHelper: messagingHelper,
jexlCache: &jexlCache)
messagingHelper: messagingHelper)
}
}

Expand Down Expand Up @@ -290,24 +287,24 @@ class GleanPlumbMessageManager: GleanPlumbMessageManagerProtocol {
message: MessageData,
lookupTables: Messaging
) -> Result<GleanPlumbMessage, CreateMessageError> {
// Guard against a message with a blank `text` property.
guard !message.text.isEmpty else { return .failure(.malformed) }
var action: String
if !message.isControl {
// Guard against a message with a blank `text` property.
guard !message.text.isEmpty else { return .failure(.malformed) }

// The message action should be either from the lookup table OR a URL.
guard let safeAction = sanitizeAction(message.action, table: lookupTables.actions) else {
return .failure(.malformed)
}
action = safeAction
} else {
action = "CONTROL_ACTION"
}

// Ascertain a Message's style, to know priority and max impressions.
guard let style = lookupTables.styles[message.style] else { return .failure(.malformed) }
guard let style = sanitizeStyle(message.style, table: lookupTables.styles) else { return .failure(.malformed) }

// The message action should be either from the lookup table OR a URL.
let action = lookupTables.actions[message.action] ?? message.action
guard action.contains("://") else { return .failure(.malformed) }

let triggers = message.trigger.compactMap { trigger in
lookupTables.triggers[trigger]
}

// Be sure the count on `triggers` and `message.triggers` are equal.
// If these mismatch, that means a message contains a trigger not in the triggers lookup table.
// JEXLS can only be evaluated on supported triggers. Otherwise, consider the message malformed.
if triggers.count != message.trigger.count {
guard let triggers = sanitizeTriggers(message.trigger, table: lookupTables.triggers) else {
return .failure(.malformed)
}

Expand All @@ -322,6 +319,28 @@ class GleanPlumbMessageManager: GleanPlumbMessageManagerProtocol {
)
}

private func sanitizeAction(_ unsafeAction: String, table: [String: String]) -> String? {
let action = table[unsafeAction] ?? unsafeAction
if action.contains("://") {
return action
} else {
return nil
}
}

private func sanitizeTriggers(_ unsafeTriggers: [String], table: [String: String]) -> [String]? {
var triggers = [String]()
for unsafeTrigger in unsafeTriggers {
guard let safeTrigger = table[unsafeTrigger] else { return nil }
triggers.append(safeTrigger)
}
return triggers
}

private func sanitizeStyle(_ unsafeStyle: String, table: [String: StyleData]) -> StyleData? {
return table[unsafeStyle]
}

private func baseTelemetryExtras(using message: GleanPlumbMessage) -> [String: String] {
return [MessagingKey.messageKey.rawValue: message.id,
MessagingKey.messageSurface.rawValue: message.surface.rawValue]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,44 +14,28 @@ class NimbusMessagingEvaluationUtility {
/// Checks whether a message is eligible to be show by evaluating message JEXLs.
func isMessageEligible(
_ message: GleanPlumbMessage,
messageHelper: NimbusMessagingHelperProtocol,
jexlCache: inout [String: Bool]
messageHelper: NimbusMessagingHelperProtocol
) throws -> Bool {
return try isNimbusElementEligible(checking: message.triggers,
using: messageHelper,
and: &jexlCache)
using: messageHelper)
}

/// Checks whether an object with a generic ``[String]`` lookup table of valid
/// JEXLs is eligible to be show by evaluating those JEXLs.
func doesObjectMeet(
verificationRequirements lookupTable: [String],
using helper: NimbusMessagingHelperProtocol,
and jexlCache: inout [String: Bool]
using helper: NimbusMessagingHelperProtocol
) throws -> Bool {
return try isNimbusElementEligible(checking: lookupTable,
using: helper,
and: &jexlCache)
using: helper)
}

private func isNimbusElementEligible(
checking triggers: [String],
using helper: NimbusMessagingHelperProtocol,
and jexlCache: inout [String: Bool]
using helper: NimbusMessagingHelperProtocol
) throws -> Bool {
return try triggers.reduce(true) { accumulator, trigger in
guard accumulator else { return false }

// Check the jexlCache for the `Bool`, in the case we already
// evaluated it. Otherwise, perform an expensive Foreign Function
// Interface (FFI) operation once for the trigger.
guard let evaluation = jexlCache[trigger] else {
let evaluation = try helper.evalJexl(expression: trigger)
jexlCache[trigger] = evaluation
return evaluation
}

return evaluation
return try accumulator && (try helper.evalJexl(expression: trigger))
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ class FakespotViewController: UIViewController,
let uuid = windowUUID
store.subscribe(self, transform: {
$0.select({ appState in
return BrowserViewControllerState(windowUUID: uuid)
return BrowserViewControllerState(appState: appState, uuid: uuid)
})
})
}
Expand Down
Loading

0 comments on commit 345b084

Please sign in to comment.