-
-
Notifications
You must be signed in to change notification settings - Fork 103
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
Make bookmark-service less browser-specific, enabling more browsers (including mobile) #282
base: master
Are you sure you want to change the base?
Conversation
change isNativeBookmarkInToolbarContainer to isNativeBookmarkIdOfToolbarContainer remove the nativeToolbarContainerId parameter from createNativeBookmarkTree utilite isNativeBookmarkIdOfToolbarContainer in createNativeSeparator()
…ners enum for list of necessary containers
…bext- and chromium- bookmark.service
…veBookmarksFromBookmarks this functionality was originally hard-coded for menu/mobile containers and I forgot to implement it
return this.settingsSvc.syncBookmarksToolbar().then((syncBookmarksToolbar) => { | ||
const populatePromises = []; | ||
|
||
for (const containerEnumVal of Object.keys(BookmarkContainer)) { |
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.
The linter does not like this..
return this.settingsSvc.syncBookmarksToolbar().then((syncBookmarksToolbar) => { | ||
const getBookmarkPromises = new Array<Promise<[BookmarkContainer, Array<Bookmark>]>>(); | ||
|
||
for (const containerEnumVal of Object.keys(BookmarkContainer)) { |
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.
The linter does not like this..
return this.settingsSvc.syncBookmarksToolbar().then((syncBookmarksToolbar) => { | ||
const getBookmarkPromises = new Array<Promise<BookmarkIdMapping[]>>(); | ||
|
||
for (const containerEnumVal of Object.keys(BookmarkContainer)) { |
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.
The linter does not like this..
return this.settingsSvc.syncBookmarksToolbar().then((syncBookmarksToolbar) => { | ||
const clearPromises = []; | ||
|
||
for (const containerEnumVal of Object.keys(BookmarkContainer)) { |
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.
The linter does not like this..
otherBookmarksNodeTitle = 'Other bookmarks'; | ||
toolbarBookmarksNodeTitle = 'Bookmarks bar'; | ||
menuBookmarksNodeTitle = 'Menu bookmarks'; | ||
mobileBookmarksNodeTitle = 'Mobile bookmarks'; |
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 will break on any non-english locale. You have to use the id to detect the container. Note that even this is not perfect (as ids can change under some circumstances) and I have discussed with the chromium team - there is no API or "supported" way of retrieving containers.
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.
Got it! What about about letting the user specify the right folder during the setup?
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.
Ids work 99% of the time. Implementing a UI just to select these folders is a pretty large change for little gain.
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.
Ye... I guess implementing opera/kiwi specific version that just have different getNativeContainersIds would be the best approach then. It seems that Kiwi mobile bookmarks have id 3 (following chromiums id 1 and 2). Not sure what to do about opera, I read here something about the IDs not being static?
Thanks for the PR @michkot, I appreciate the effort. The chromium implementation is intended to work with standard chromium, browsers are different and have different functionality hence the separate classes. I'm not convinced that attempting to shoehorn all different chromium-based browsers together is a good idea. Frankly I'd rather keep them separate for now at least. Can you create sub classes for opera/kiwi where they differ from chromium and revert the changes to chromium classes? Edit: If you subclass |
I will see how much time there will be in the next days, I hope I'll be able to refresh this PR before I forget all the details :) |
No worries, adding a new platform is a complex task. Not only coding but lots of testing to ensure that other browsers properly sync the data for the browser you've added! It won't make it in time for the next release, so let's not rush it. |
Respecting that opinion would be crucial if the changes are to get merged, but it got me thinking. What if, instead, there would be a single small "place" (DI service, a big switch statement, whatever technical solution would be best) that would provide all the necessary browser-specific information for the same extension package to work across different chromium- or firefox- based browsers? And when I say that, the only thing that comes to my mind is the list of natively supported bookmark containers and their IDs. Creating separate .crx files for various platforms might get quite messy, especially since many people probably get addons for Opera/Kiwi via the chrome store. Slightly unrelated observation: I have tried the 1.6.0 on my desktop Firefox, and the Mobile bookmarks are nowhere to be found via the browsers bookmark manager. |
I am more then willing to remove the hacks that will break with non-english locale etc. However, I would love to keep these changes 8a4fdaa (+ the later reuse of the same code for firefox too b29ad13), because it eliminated the dependency between the browser configuration (the set of natively supported bookmarks root nodes/containers in a particular browser) and the code gluing the native and extension bookmarks together. Maintaining the same code in two places might have worked, but forking it into 3 or 4 copies seems highly error pronse |
And I would need a small help app/src/modules/webext/chromium/shared/chromium-bookmark/chromium-bookmark.service.ts Lines 464 to 473 in 317e697
What is the purpose of this code? With the code there, the return value of the getNativeContainerIds() can vary. Without the cache, it will always return a map of entries that always have a defined value. With the cache, it might return entries with null values for unsupported containers. Was there any gain in that (before my changes, I mean), when there is already the unsupportedContainers field? edit: I see that there is a place in the code, which depends on this logic: app/src/modules/webext/shared/webext-bookmark/webext-bookmark.service.ts Lines 1033 to 1044 in 317e697
and the same check that's in the method is used directly in processChangeTypeMoveOnBookmarks at app/src/modules/webext/shared/webext-bookmark/webext-bookmark.service.ts Lines 681 to 690 in 317e697
|
That's exactly what xBrowserSync is doing. You understand angular code right? These classes are instantiated via angular's dependency injection, hence different subclass implementations (where necessary) for different browsers where their native implementations differ. Taking bookmarks as an example, all desktop implementations descend from
Lol I didn't do it because I like making my life difficult!! You don't understand, chromium/chrome, opera and kiwi (and firefox, brave, edge etc) are different browsers, you cannot lump them together with one implementation. xBrowserSync handles syncing across multiple browsers where they all have different bookmark containers (i.e. the root folders). If a user creates a sync with Firefox and adds a bookmark in "Menu Bookmarks" then what happens to that bookmark when they subsequently sync to Chrome? xBrowserSync handles this by creating folders for unsupported containers, and it can only do that because I've separated the implementations with subclasses. For example, look at Opera's native containers. Run this in xBrowserSync background console:
the result is:
Compare that to Chrome:
How are you going to handle syncing all those extra unsupported containers? The current architecture solves this problem after much real world trial and error. This is a very complex project trying to solve a very complex problem, you can't take shortcuts with it. And that's just bookmarks! When you start looking at other custom/different aspects of browser runtime/api implementations then things can get even more crazy.
Mobile bookmarks only appear when a user enables native sync with a mobile device. I am actually thinking about deprecating syncing of mobile bookmarks since xBrowserSync does not support native bookmark sync and most users won't have them. |
|
Before I go on with the technical chitchat and reply to the previous messages (I hope you do not mind that the chat takes places here in the PR, I am happy to discuss whatever needs to be discussed in order to improve the extension in maintainable way), I have a question: How did you handle addition of new containers into the extension in the past? Or has the current set (other, toolbar, menu, mobile) never been different before?. (Its relevant to my question about |
In the original code, the return value of the getNativeContainerIds() can vary. If sync was not enabled (or synced bookmarks we not loaded yet) it would return a map of entries that always have a defined value. With the synced bookmarks present, it would returns entires with value=undefined for unsupported containers which did not currently have a mount-point bookmark folder (for example, due to the folder being deleted or renamed via the browser UI). This logic was used in app/src/modules/webext/shared/webext-bookmark/webext-bookmark.service.ts Lines 1033 to 1044 in 317e697 method wasContainerChanged, which was called in processChangeType{Add,Modify.Remove}OnBookmarks methods and also directly in processChangeTypeMoveOnBookmarks. The code there checked whether the getNativeContainerIds had any undefined values, as a signal that a previously mapped natively unsupported container was somehow modified and not being detected by the getNativeContainerIds() any more. Now, getNativeContainers() returns only entires for containers for which a native bookmark node id was discovered. The code of wasContainerChanged mehod does the same check as before, but on its own - the logic (for now) remains equal to the old one! It looks for any (synced)bookmarks.children (all containers in the current syncdata) that are not present in the result of getNativeContainerIds(). Also, wasContainerChanged() is now also used in processChangeTypeMoveOnBookmarks (unifying it with the other three processChange* calls)
… build process (still leaving strictNullChecks to default=false)
…t- impl. Created two new abstract methods: getNativeContainerInfo() -> returns browser-specific info (id + throwIfNotFound) for a given BookmarkContainer getDefaultNativeContainerCandidates() -> returns the candidates for being the default containers for creation of unsupported containers Field unsupportedContainers was refactored into getUnsupportedContainers() method. Added getSupportedContainers(). NativeContainersInfo.platformDefaultBookmarksNodeId renamed to .defaultNativeContainerId Refactored and moved ensureContainersExist(...) to webext- impl. It uses the getSupportedContainers() Created a new webext- impl. method identifySupportedContainers(). This method initializes the supported containers cache. This method must be called at least once after the extension is loaded, before a call to getSupportedContainers(), getUnsupportedContainers() or ensureContainersExist (...) is attempted. Currently, identifySupportedContainers() is called before any process*Sync() in bookmark-sync-provider can take place, but should be probably moved to some module/extension init code. Maybe the whole "supported-containers" logic should be moved to different (new?) service.
So, what are the latest changes about:
The change details are summarized in the commit messages (sorry for the TYPOs, I am reading it now again and its horrible sometimes) |
I am not really familiar with old Angular (Angular.JS nowadays) but since I was not adding stuff, general DI understanding was enough for me :).
If I did offend you by that message, I am sorry 😓. I can imagine that browser development can be really hard due their specifics quirks. I understood the concept of the supported/unsupported containers after a few hours of toying with the code. What I am trying to achieve, in order to make adding more browsers less error-prone, is code deduplication in the bookmark-service code. Obviously, if some new obscure browser is to be added, some code might need to get un-shared again. I will not try to paraphrase the before/after change of the bookmark-service code, in a slightly humorous way (had a bad day, so will try to relax a bit while writing this, feel free to skip it :D ): chromium code before:We known that we support native toolbar (id:1) and other(id:2) bookmarks containers, and menu and mobile using "virtual containers" in the "other bookmarks" container. First, let's write the createNativeBookmarks-ish method - write the code fragment for one of the containers, copy paste it three times, then add special "virtual containers" handling stuff for the "other bookmarks" copy and special "is toolbar sync enabled" to the "bookmark bark" copy and special "I am not a real native container" handling to the "menu" and "mobile" containers. Repeat the whole thing 2 or 3 times for other methods. firefox code before:We support all four containers!. It's like chrome, but simpler, because there are no virtual containers and no special convertSeparatorsCall. (( actually, this is one of the TODOs in my code I am hoping to get your help with later). chrome and firefox code after:Oh, we are free of having to solve that container-related stuff, and we don't even care how many different containers you want us to support! We can focus on our browser-specific quirks, that we have hooks for in the generic webext implementation! webext code after:Uhgghhh... I got kinda heavy in the last week... Did I eat too much pizza-code from the restaurant next doors 🤨? But now, I can handle all the native<->extension containers converting stuff for both of my kids! Also, I learned to dynamically find out what containers does the current browser support via asking my children, so I don't have to write it down everywhere. I can ask the kids about particular BookmarkContainer whenever I need to know, whether it is natively supported or not right now, and whether I should blow-up with an exception if I cant find a specific container. --------- end of the hopefully funny part ---------
Yes, I was well aware of that, scouted the issues on this project ;)
I admit there is one behaviour I did not consider yet: what does the current implementation do, if the browser has more native containers that are extension-supported now by the BookmarkContainer enum -- specifically, what happens if I move bookmarks between extension-supported and extension-unsupported bookmark containers, will it result to deletion/creation every time, or will it crash? But I believe/hope this can be also resolved in a browser-agnostic way 😃 .
Sure... I am not expecting my changes to be "shipped immediately", but I think I can do (and already did) some decent manual testing and there could be people willing to try out an alfa version on a before-unsupported browser? But I might be too optimistic with that, I guess.
For know, I had to use one Opera-specific bookmark method. Are you aware of any other chromium-based browser that would require something more then base/chromium API? I am willing to look into those to consider the impact of providing support for them too 👍
Thanks for the explanation! That's rather unfortunate, because while syncing and accessibility of Mobile bookmarks between my Kiwi-phone and Chrome/Opera is flawless right now, Mobile bookmarks can not be access from Firefox-UI... |
Right now, the code compiles, but I need to do more testing and there is a lot of code-quaility TODOs that I would like to resolve before asking you to merge this. (Unless you state that you really don't like the direction this is going -- which is perfectly legit variant -- I would probably abandon this PR and keep it for personal-use only). Tasklist (I could copy it to the PR description and update it regularly)
|
Again I appreciate the effort, and I'm not offended by suggesting alternate approaches. It's nice to have someone interested enough to even have a discussion!! So thank you very much. However, I really think making such sweeping changes in one go is not the right approach. You have to understand, this is a very complex codebase that has been refined over years of encountering and fixing edge case bugs. You're making changes without testing them extensively as you go and you're going to find that many of those bugs return. The architecture is as it is in order to get multiple platforms (i.e. Chromium and Firefox) syncing together without issue. Unsupported containers are handled carefully to avoid many bugs that users have discovered over the years. There's also the lack of automated tests which makes this process more difficult - on a side note, if you're willing to help me add unit tests I would really appreciate it! I don't think I am willing merge such a huge PR, however I do think we can add the extra containers to the Just for info, the platform architecture of the code looks like this: So Chromium-based browsers can share more code, but Firefox and Chromium can only share the WebExt code. At this point, you can either carry with the work you're doing here but keep the code bases separate, or start a new PR tackling the above task of updating the |
I use a local xBrowserSync service (running in docker) for testing, easy enough to provision. |
Never mind, I think I got it working, probably some copy paste errors. I ended up with a trimmed down compose file for the full setup. It might be nice to provide a docker compose without the reverse proxy, if you need to run it in a Let's encrypt unfriendly env ;) |
Yep, already figured it out from the code. Actually I really like the structure!
To put in bullets:
|
As per #126 (comment) I have refactored the code of chromium version of the bookmarm-service in order to make it compatible with more browsers (specifically tested myself on Opera and Kiwi).
I have then inspected the differences between the original chromium and firefox versions and upon the inspection moved the refactored code into the webext base class (with addition of getNativeBookmarksWithSeparators to the webext interface -- this could/should be resolved differently).
Currently, there are some notes and TODOs that should be resolved before merging into the upstream. The getNativeContainerIds() should be still reworked for chromium (and possibly for firefox too).