-
Notifications
You must be signed in to change notification settings - Fork 920
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
Import Data from Safari iOS #26914
base: master
Are you sure you want to change the base?
Import Data from Safari iOS #26914
Conversation
The security team is monitoring all repositories for certain keywords. This PR includes the word(s) "password" and so security team members have been added as reviewers to take a look. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Quick glance feedback (haven't looked at implementation details yet):
- Needs unit tests
- Everything under
ios/browser/api
should be Obj-C wrappers, please move all of the actual importer logic to separate directory/directories. Try to match Chromium's directory structure in this regard - Is it possible to share this logic with desktop so that they can get Safari import via exported zip as well? (Perhaps with a layered component)
ea6722c
to
f1a9c7d
Compare
} | ||
|
||
size_t field_idx = 0; | ||
CSVFieldParser parser(row); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems that the parser here considers the grammar simple enough that it doesn't require using a memory safe language or separate process.
I believe this the solves for the rule of 2 issue here as well.
in general this seems ok to me, but I think it would be useful for @stoletheminerals to give this one a deeper look. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not clear if we need a header in DEPS files, but for now ++
f1a9c7d
to
67c2c7b
Compare
c21ba1c
to
da8e55e
Compare
66c8b79
to
c72538a
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
requires test
ios/brave-ios/Sources/Brave/Frontend/Sync/BraveCore/ZipImporter/ZipImporter.swift
Outdated
Show resolved
Hide resolved
caddb70
to
fa9e252
Compare
b890b94
to
5c92a3a
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Couple more notes
|
||
public static let personalImportTitle = NSLocalizedString( | ||
"personalImportTitle", | ||
tableName: "DataImporter", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You don't need a table name for all these strings, since its in a separate target
if kind == .passwordConflict { | ||
Button { | ||
dismiss() | ||
} label: { | ||
Label { | ||
Text(Strings.close) | ||
} icon: { | ||
Image(braveSystemName: "leo.close") | ||
.foregroundColor(Color(braveSystemName: .iconDefault)) | ||
.padding(8) | ||
} | ||
.labelStyle(.iconOnly) | ||
} | ||
.background(Color(braveSystemName: .materialSeparator), in: Circle()) | ||
.frame(maxWidth: .infinity, alignment: .trailing) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This shouldn't be part of the VStack, it should be in a overlay(alignment: .topTrailing)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It also probably shouldn't be part of the ScrollView
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This view doesn't have a scroll-view and isn't embedded in one.
@ObservedObject | ||
private var model = DataImportModel() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be a @StateObject
if you're going to create it in the View
@State | ||
private var showSuccessView = false | ||
|
||
@State | ||
private var showFailureView = false | ||
|
||
@State | ||
private var showLoadingView = false | ||
|
||
@State | ||
private var showMainView = true |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As per DM these should all be removed
@ViewBuilder | ||
private func mainView() -> some View { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Doesn't need to be a func, can just be private var mainView: some View
.sheet( | ||
isPresented: $model.isLoadingProfiles, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These sheets are explicitly associated with the main view only it seems? Is that correct? If for instance the import state is any causing any of the other views to appear the sheets will be dismissed, should these go on the container view instead?
.osAvailabilityModifiers({ | ||
if #available(iOS 16.4, *) { | ||
} else { | ||
$0.background(.thickMaterial) | ||
} | ||
}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As Per DM you can't leave one condition empty, need to return the view provided as the closure is a ViewBuilder
@@ -0,0 +1,3 @@ | |||
include_rules = [ | |||
"+brave/components/password_manager/core/browser/import/safari_password_importer.h" | |||
] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
EOF newline missing
if model.importState == .success { | ||
DataImporterStateView(kind: .success, model: model) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's something weird here about creating an entire switch across import states all to display the same View (DataImporterStateView
) with almost identical modifiers with the only thing changing is Buttons? You also seem to combine both making this reusable and a "View only" kind of component and also passing in the model which means you're now checking specific things related to the logic of the feature.
Either you should have one view and you only pass in the model and adjust inside directly based on importState
, or you don't pass in the model and make sure you can pass in all necessary info from here.
You also probably want to find a way to do this without multiple if-else causing the creation of completely separate views for each state
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Switched them all to separate views instead of one shared view.
Task.delayed(bySeconds: 1.0) { @MainActor in | ||
self.importState = .dataConflict | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're already in an async method, just do try await Task.sleep(...)
directly in it
self.importState = .dataConflict | ||
Task.delayed(bySeconds: 1.0) { @MainActor in | ||
self.importState = .dataConflict | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same as above
[puLL-Merge] - brave/brave-core@26914 DescriptionThis PR adds support for importing Safari data into Brave on iOS. It includes functionality to import bookmarks, browsing history, and passwords from Safari export files, with careful handling of conflicts and data validation. The change also includes a new UI for guiding users through the import process. Security Hotspots
ChangesChangesBy filename: New Components:
iOS Changes:
Build System:
sequenceDiagram
participant User
participant UI
participant Importer
participant CSVParser
participant Storage
User->>UI: Initiates import
UI->>Importer: Import request
Importer->>CSVParser: Parse Safari export
CSVParser-->>Importer: Parsed data
alt Has Conflicts
Importer->>UI: Show conflict resolution
User->>UI: Resolve conflicts
UI->>Importer: Resolution choice
end
Importer->>Storage: Store imported data
Storage-->>UI: Import complete
UI->>User: Show success/failure
|
Add Password Importer Add History Importer Signed-off-by: Brandon <[email protected]>
Signed-off-by: Brandon <[email protected]>
Add bottom sheet and hook up importer Add Importer View Add FilePicker Add Conflict handling Add Conflict View Add Success View Add Error View Finish all UI and refactor all code to its own package. Refactor Brave-Core.
Fix scrollViews, sheet backgrounds, layouts. Remove BraveSheet.
…ansparency and padding on conflicts view.
987e4e5
to
f05f0d1
Compare
Security Review
Summary
Resolves brave/brave-browser#42727
Submitter Checklist:
QA/Yes
orQA/No
;release-notes/include
orrelease-notes/exclude
;OS/...
) to the associated issuenpm run test -- brave_browser_tests
,npm run test -- brave_unit_tests
wikinpm run presubmit
wiki,npm run gn_check
,npm run tslint
git rebase master
(if needed)Reviewer Checklist:
gn
After-merge Checklist:
changes has landed on
Test Plan: