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-9627-[Native Error Page] Initial changes for manager support #22613

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
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
18 changes: 16 additions & 2 deletions firefox-ios/Client.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@
0AFF7F6D2C7C7BBA00265214 /* CertificatesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AFF7F6A2C7C7BB900265214 /* CertificatesViewController.swift */; };
0B11AF022CB412D100AD51D5 /* Metrics.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B11AF002CB412D100AD51D5 /* Metrics.swift */; };
0B11AF042CB4130F00AD51D5 /* Metrics.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B11AF032CB4130F00AD51D5 /* Metrics.swift */; };
0AFF7F6E2C7C7BBA00265214 /* CertificatesModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AFF7F6B2C7C7BB900265214 /* CertificatesModel.swift */; };
0B305E1B1E3A98A900BE0767 /* BookmarksTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B305E1A1E3A98A900BE0767 /* BookmarksTests.swift */; };
0B3D670E1E09B90B00C1EFC7 /* AuthenticationTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B3D670D1E09B90B00C1EFC7 /* AuthenticationTest.swift */; };
0B3F8C5E2CA4471C00DB5367 /* EditBookmarkViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B3F8C5D2CA4471C00DB5367 /* EditBookmarkViewModel.swift */; };
Expand Down Expand Up @@ -583,7 +582,11 @@
60D71AEC26AAF45E00355588 /* UIColorExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 60D71AEB26AAF45E00355588 /* UIColorExtension.swift */; };
630FE1342C7FB42500D9D6B2 /* NativeErrorPageMockModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 630FE1302C7FB42500D9D6B2 /* NativeErrorPageMockModel.swift */; };
630FE1352C7FB42500D9D6B2 /* NativeErrorPageViewControllerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 630FE1322C7FB42500D9D6B2 /* NativeErrorPageViewControllerTests.swift */; };
631A369F2CC0A4FE0044DFEB /* NativeErrorPageMiddleware.swift in Sources */ = {isa = PBXBuildFile; fileRef = 631A369E2CC0A4FE0044DFEB /* NativeErrorPageMiddleware.swift */; };
631A36A32CC0B2470044DFEB /* NativeErrorPageHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 631A36A22CC0B2470044DFEB /* NativeErrorPageHelper.swift */; };
63306D3921103EAE00F25400 /* LegacySavedTab.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63306D3821103EAE00F25400 /* LegacySavedTab.swift */; };
63B213282CCA796D00A466DB /* NativeErrorPageAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63B213272CCA796D00A466DB /* NativeErrorPageAction.swift */; };
63B2132A2CCA797400A466DB /* NativeErrorPageState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63B213292CCA797400A466DB /* NativeErrorPageState.swift */; };
63F7A9AA2C7529ED005846F5 /* NativeErrorPageModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63F7A9A92C7529ED005846F5 /* NativeErrorPageModel.swift */; };
63F7A9AC2C752BB0005846F5 /* NativeErrorPageViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63F7A9AB2C752BB0005846F5 /* NativeErrorPageViewController.swift */; };
6669B5E2211418A200CA117B /* WebsiteDataSearchResultsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6669B5E1211418A200CA117B /* WebsiteDataSearchResultsViewController.swift */; };
Expand Down Expand Up @@ -2276,7 +2279,6 @@
0AFF7F6A2C7C7BB900265214 /* CertificatesViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CertificatesViewController.swift; sourceTree = "<group>"; };
0B11AF002CB412D100AD51D5 /* Metrics.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Metrics.swift; sourceTree = "<group>"; };
0B11AF032CB4130F00AD51D5 /* Metrics.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Metrics.swift; sourceTree = "<group>"; };
0AFF7F6B2C7C7BB900265214 /* CertificatesModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CertificatesModel.swift; sourceTree = "<group>"; };
0B305E1A1E3A98A900BE0767 /* BookmarksTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BookmarksTests.swift; sourceTree = "<group>"; };
0B3D670D1E09B90B00C1EFC7 /* AuthenticationTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AuthenticationTest.swift; sourceTree = "<group>"; };
0B3F8C5D2CA4471C00DB5367 /* EditBookmarkViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditBookmarkViewModel.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -6502,9 +6504,13 @@
63094229AA6EC744599B77A4 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/ClearPrivateData.strings; sourceTree = "<group>"; };
630FE1302C7FB42500D9D6B2 /* NativeErrorPageMockModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NativeErrorPageMockModel.swift; sourceTree = "<group>"; };
630FE1322C7FB42500D9D6B2 /* NativeErrorPageViewControllerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NativeErrorPageViewControllerTests.swift; sourceTree = "<group>"; };
631A369E2CC0A4FE0044DFEB /* NativeErrorPageMiddleware.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NativeErrorPageMiddleware.swift; sourceTree = "<group>"; };
631A36A22CC0B2470044DFEB /* NativeErrorPageHelper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NativeErrorPageHelper.swift; sourceTree = "<group>"; };
63306D3821103EAE00F25400 /* LegacySavedTab.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LegacySavedTab.swift; sourceTree = "<group>"; };
634148899F41CAA3BCF71E8B /* or */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = or; path = or.lproj/Localizable.strings; sourceTree = "<group>"; };
63B04BE882C41584580E0E59 /* fa */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fa; path = "fa.lproj/Default Browser.strings"; sourceTree = "<group>"; };
63B213272CCA796D00A466DB /* NativeErrorPageAction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NativeErrorPageAction.swift; sourceTree = "<group>"; };
63B213292CCA797400A466DB /* NativeErrorPageState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NativeErrorPageState.swift; sourceTree = "<group>"; };
63F7A9A92C7529ED005846F5 /* NativeErrorPageModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NativeErrorPageModel.swift; sourceTree = "<group>"; };
63F7A9AB2C752BB0005846F5 /* NativeErrorPageViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NativeErrorPageViewController.swift; sourceTree = "<group>"; };
63FE433D87018CDDFCF592E3 /* sk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sk; path = sk.lproj/ErrorPages.strings; sourceTree = "<group>"; };
Expand Down Expand Up @@ -10527,6 +10533,10 @@
isa = PBXGroup;
children = (
63F7A9A92C7529ED005846F5 /* NativeErrorPageModel.swift */,
63B213292CCA797400A466DB /* NativeErrorPageState.swift */,
63B213272CCA796D00A466DB /* NativeErrorPageAction.swift */,
631A369E2CC0A4FE0044DFEB /* NativeErrorPageMiddleware.swift */,
631A36A22CC0B2470044DFEB /* NativeErrorPageHelper.swift */,
63F7A9AB2C752BB0005846F5 /* NativeErrorPageViewController.swift */,
);
path = NativeErrorPage;
Expand Down Expand Up @@ -15718,6 +15728,7 @@
DFACBF81277B916B003D5F41 /* ConfigurableGradientView.swift in Sources */,
7482205C1DBAB56300EEEA72 /* MailProviders.swift in Sources */,
8A93F87029D3A597004159D9 /* SceneCoordinator.swift in Sources */,
631A36A32CC0B2470044DFEB /* NativeErrorPageHelper.swift in Sources */,
C88E7A602A05551B0072E638 /* NimbusOnboardingFeatureLayerProtocol.swift in Sources */,
8CFD56882AAF057D003157A6 /* SwitchFakespotProduction.swift in Sources */,
C40046FA1CF8E0B200B08303 /* BackForwardListAnimator.swift in Sources */,
Expand Down Expand Up @@ -15765,6 +15776,7 @@
2386E4E624F8358E0072EF17 /* HomepageMessageCard.swift in Sources */,
21618A8C2A438A0900A5189E /* ActiveScreenAction.swift in Sources */,
E15DE7C2293A7AED00B32667 /* PhotonActionSheetLineSeparator.swift in Sources */,
631A369F2CC0A4FE0044DFEB /* NativeErrorPageMiddleware.swift in Sources */,
8CE1E43A2B8C76C80026530B /* LoginListView.swift in Sources */,
8A832A9429DC99BA0025D5DD /* LaunchScreenViewController.swift in Sources */,
21E77E4E2AA8BA5200FABA10 /* TabTrayViewController.swift in Sources */,
Expand Down Expand Up @@ -15844,6 +15856,7 @@
0E77DF0F2CB8220000B80BA1 /* GeneratedPasswordStorage.swift in Sources */,
219A0FDB2ACCCFFC009A6D1A /* InactiveTabsSectionManager.swift in Sources */,
B2981F8A2B71AD7A00132C1B /* AutofillAccessoryViewButtonItem.swift in Sources */,
63B213282CCA796D00A466DB /* NativeErrorPageAction.swift in Sources */,
211F00AC27F4D918001D9189 /* HistoryPanel+Search.swift in Sources */,
8AF347E02CADD1C300624036 /* HomepageAction.swift in Sources */,
96EB6C3827D821B800A9D159 /* HistoryPanelViewModel.swift in Sources */,
Expand Down Expand Up @@ -16197,6 +16210,7 @@
8C4B0F5D2C076B12008B3E74 /* UpdatableAddressFields+Decodable.swift in Sources */,
C8B0F5F6283B7CCE007AE65D /* PocketStory.swift in Sources */,
C81AC6B626160091007800C5 /* LegacyTabTrayViewModel.swift in Sources */,
63B2132A2CCA797400A466DB /* NativeErrorPageState.swift in Sources */,
8ADAE4222A33A113007BF926 /* SendAnonymousUsageDataSetting.swift in Sources */,
E170CA542B72C07A0082EFC5 /* FakespotActionFooterView.swift in Sources */,
C8EDDBF229DF1159003A4C07 /* URLScanner.swift in Sources */,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -793,9 +793,7 @@ class BrowserCoordinator: BaseCoordinator,

func showNativeErrorPage(overlayManager: OverlayModeManager) {
// TODO: FXIOS-9641 #21239 Integration with Redux - presenting view
let errorPageModel = ErrorPageModel(errorTitle: "", errorDecription: "", errorCode: "")
let errorpageController = NativeErrorPageViewController(model: errorPageModel,
windowUUID: windowUUID,
let errorpageController = NativeErrorPageViewController(windowUUID: windowUUID,
overlayManager: overlayManager)
guard browserViewController.embedContent(errorpageController) else {
logger.log("Unable to embed private homepage", level: .debug, category: .coordinator)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -756,8 +756,24 @@ extension BrowserViewController: WKNavigationDelegate {
return
}

if let url = error.userInfo[NSURLErrorFailingURLErrorKey] as? URL {
ErrorPageHelper(certStore: profile.certStore).loadPage(error, forUrl: url, inWebView: webView)
if isNativeErrorPageEnabled {
guard var errorURLpath = URLComponents(string: "\(InternalURL.baseUrl)/\(ErrorPageHandler.path)" ) else { return }
errorURLpath.queryItems = [URLQueryItem(
name: InternalURL.Param.url.rawValue,
value: webView.url?.absoluteString
)]
guard let errorPageURL = errorURLpath.url else { return }
let action = NativeErrorPageAction(networkError: error,
windowUUID: windowUUID,
actionType: NativeErrorPageActionType.receivedError
)
store.dispatch(action)

webView.load(PrivilegedRequest(url: errorPageURL) as URLRequest)
} else {
if let url = error.userInfo[NSURLErrorFailingURLErrorKey] as? URL {
ErrorPageHelper(certStore: profile.certStore).loadPage(error, forUrl: url, inWebView: webView)
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,11 @@ class BrowserViewController: UIViewController,
var isToolbarNavigationHintEnabled: Bool {
return featureFlags.isFeatureEnabled(.toolbarNavigationHint, checking: .buildOnly)
}

var isNativeErrorPageEnabled: Bool {
return featureFlags.isFeatureEnabled(.nativeErrorPage, checking: .buildOnly)
}

private var browserViewControllerState: BrowserViewControllerState?

// Header stack view can contain the top url bar, top reader mode, top ZoomPageBar
Expand Down Expand Up @@ -1467,10 +1472,6 @@ class BrowserViewController: UIViewController,

// MARK: - Native Error Page

private func isNativeErrorPage() -> Bool {
featureFlags.isFeatureEnabled(.nativeErrorPage, checking: .buildOnly)
}

func showEmbeddedNativeErrorPage() {
// TODO: FXIOS-9641 #21239 Implement Redux for Native Error Pages
browserDelegate?.showNativeErrorPage(overlayManager: overlayManager)
Expand Down Expand Up @@ -1518,7 +1519,7 @@ class BrowserViewController: UIViewController,

if isAboutHomeURL {
showEmbeddedHomepage(inline: true, isPrivate: tabManager.selectedTab?.isPrivate ?? false)
} else if isErrorURL && isNativeErrorPage() {
} else if isErrorURL && isNativeErrorPageEnabled {
showEmbeddedNativeErrorPage()
} else {
showEmbeddedWebview()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// 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 Redux
import Common

final class NativeErrorPageAction: Action {
let networkError: NSError?
let nativePageErrorModel: ErrorPageModel?
init(
networkError: NSError? = nil,
nativePageErrorModel: ErrorPageModel? = nil,
windowUUID: WindowUUID,
actionType: any ActionType
) {
self.networkError = networkError
self.nativePageErrorModel = nativePageErrorModel
super.init(windowUUID: windowUUID, actionType: actionType)
}
}

enum NativeErrorPageActionType: ActionType {
case receivedError
}

enum NativeErrorPageMiddlewareActionType: ActionType {
case initialize
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// 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
import GCDWebServers
import Shared
import Storage

class NativeErrorPageHelper {
enum NetworkErrorType {
case noInternetConnection
}

var error: NSError

var errorDescriptionItem: String {
return error.localizedDescription
}

init(error: NSError) {
self.error = error
}

func parseErrorDetails() -> ErrorPageModel {
var title = ""
var description = ""
switch error.code {
Copy link
Contributor

Choose a reason for hiding this comment

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

Space for readibility

case Int(CFNetworkErrors.cfurlErrorNotConnectedToInternet.rawValue):
title = .NativeErrorPage.NoInternetConnection.TitleLabel
description = .NativeErrorPage.NoInternetConnection.Description
default:
break
}

let model = ErrorPageModel(errorTitle: title, errorDescription: description)
return model
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// 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
import Redux
import Shared
import Common

final class NativeErrorPageMiddleware {
lazy var nativeErrorPageProvider: Middleware<AppState> = { state, action in
let windowUUID = action.windowUUID
print(action.actionType)
Copy link
Contributor

Choose a reason for hiding this comment

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

no need for print statements in prod

switch action.actionType {
Copy link
Contributor

Choose a reason for hiding this comment

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

spppaaaaaaccccceeeee :P

case NativeErrorPageActionType.receivedError:
guard let action = action as? NativeErrorPageAction, let error = action.networkError else {return}
self.initializeNativeErrorPage(windowUUID: windowUUID, error: error)
break
default:
break
}
}

private func initializeNativeErrorPage(windowUUID: WindowUUID, error: NSError) {
let model = NativeErrorPageHelper(error: error).parseErrorDetails()
let newAction = NativeErrorPageAction(nativePageErrorModel: model,
windowUUID: windowUUID,
actionType: NativeErrorPageMiddlewareActionType.initialize
)
store.dispatch(newAction)
Comment on lines +26 to +30
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
let newAction = NativeErrorPageAction(nativePageErrorModel: model,
windowUUID: windowUUID,
actionType: NativeErrorPageMiddlewareActionType.initialize
)
store.dispatch(newAction)
store.dispatch(
NativeErrorPageAction(nativePageErrorModel: model,
windowUUID: windowUUID,
actionType: NativeErrorPageMiddlewareActionType.initialize
)
)

nit: alternative to consider that doesn't require assignment.

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@

import Foundation

struct ErrorPageModel {
struct ErrorPageModel: Equatable {
let errorTitle: String
let errorDecription: String
let errorCode: String
let errorDescription: String
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
// 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 Redux
import Shared
import Common

struct NativeErrorPageState: ScreenState, Equatable {
var windowUUID: WindowUUID
var title: String?
var description: String?

init(appState: AppState, uuid: WindowUUID) {
guard let nativeErrorPageState = store.state.screenState(
NativeErrorPageState.self,
for: .nativeErrorPage,
window: uuid
) else {
self.init(windowUUID: uuid)
return
}

self.init(
windowUUID: nativeErrorPageState.windowUUID,
title: nativeErrorPageState.title,
description: nativeErrorPageState.description
)
}

init(
windowUUID: WindowUUID,
title: String? = nil,
description: String? = nil
) {
self.windowUUID = windowUUID
self.title = title
self.description = description
}

static let reducer: Reducer<Self> = { state, action in
print(action.actionType)
Copy link
Contributor

Choose a reason for hiding this comment

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

we shouldn't have these statements in production

guard action.windowUUID == .unavailable || action.windowUUID == state.windowUUID else { return NativeErrorPageState(
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: return can be on a separate line here

windowUUID: state.windowUUID,
title: state.title,
description: state.description
)}
switch action.actionType {
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: leave some space so it's easier to read. Vertical space is free, we may as well take advantage!

case NativeErrorPageMiddlewareActionType.initialize:
guard let action = action as? NativeErrorPageAction, let model = action.nativePageErrorModel else {
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: spread the multiple let conditions over multiple lines for easy reading

return NativeErrorPageState(
windowUUID: state.windowUUID,
title: state.title,
description: state.description
)
}
return NativeErrorPageState(
windowUUID: state.windowUUID,
title: model.errorTitle,
description: model.errorDescription
)
default:
return NativeErrorPageState(
windowUUID: state.windowUUID,
title: state.title,
description: state.description
)
}
}
}
Loading