Hi all, To prevent the growth of one component being buried in the long history of bugs and comments, I gave my word to summary them regularly. However, the easiest thing is usually the toughest thing, so it's a long while since the last time I did that. Anyway, I think it's time to take a snapshot of current architecture of screen locker, and try to explain why it looks so odd. I also hope this is helpful for people unfortunately involved in this component.
Note: maybe I can find all bugs of the points I mention, but I'm not a historian and too much details will blur the big picture. So if I can find the bugs, I will list them here But don't expect it too much. Another important thing is I can only list things since I devoted to this component at 1.2. Things before the day are apparently blank to me. ## Controllers Current LockScreen related functions are in fact with multiple major controllers (in files): system/lockscreen/js * lockscreen.js * lockscreen_notifications.js * lockscreen_inputpad.js * lockscreen_state_manager.js * lockscreen_media_playback.js * lockscreen_charging.js system/js * secure_window_manager.js * lock_screen_window_manager.js shared/js * lockscreen_connection_info_manager.js * lockscreen_slide.js I don't list FindMyDevice since it's major code are in a standalone app. These are major controllers of each function. Besides that, there are also lots of helpers of the component, and they all rely on these controllers. For example, the states named as 'lockscreen_state_*' are all controlled by 'lockscreen_state_manager.js'; and 'lockscreen_notification_builder.js' is used by 'lockscreen_notifications'. I call these 'controllers' is because they're the listeners of events and mozSetting changes. Other files should be simple controllees, although in technical details this is not true. ## What are they for? The 'lockscreen.js' now still controls most of functions from unlocking to wallpaper. However, in the recent patch I'm working on (Bug 1189641), I prepare to turn it as a pure controllee, so the responsibility of deciding whether the screen should be unlocked will move to the 'lockscreen_state_manager.js'. The state manager now is mainly for UI changes, and for its own design defects and the following requirements after the implementation make us re-designed a new version of state machine. But for now we could only first migrate to this mid-version state manager. Anyway, after the bug, we can at least make sure there are only one controller of UI changes and unlocking. And if there are any new requirements of controlling UI changes on the screen, they should be implemented in those 'states' of the state manager. Besides that, since the 'lockscreen.js' has been as a monolithic and complicated controller so long, we shouldn't stockpile any new features on it. If new release features ask us to add new "lockscreen" functions, please create new files under the lockscreen directory and implement them as standalone components. Component still can call functions or read info from 'lockscreen.js', but its important to not put states or new functions on that. The "keyboard" to input passcode of LockScreen is in fact a DIV controlled by 'lockscreen_inputpad.js'. And 'lock_screen_window_manager.js' control this 'window' as we have a real keyboard frame as other apps. This is a perquisite of enabling real keyboard app for LockScreen, which is also a feature request since long ago. However, although we once evaluated whether it's not too difficult to implement that, we still need to overcome the issue of animation sync and how to implement the special UI of such keyboard. Some files as 'lockscreen_media_playback.js' and 'lockscreen_charging.js' are 'widgets' on the screen. They sometimes require info from 'lockscreen.js', and things like 'lockscreen_notification.js' even need to control it. All of these communications are done by custom events. The 'secure_window_manager.js' controls "secure apps" like secure camera and emergency call. They show up upon LockScreen. At last, 'lock_screen_window_manager.js' controls the 'lock_screen_window.js', which is an attempt to treat LockScreen as an ordinary app window. It's also one part of our effort to make LockScreen as an app. So now, only after 'lock_screen_window_manager.js' call the window to create itself, the bootstrapping procedure of 'lockscreen_bootstrap.js' will start itself. These include those lazyloading files. Also, people may remember it has been chaotic to look up if now device is locked in System app. At 2.1+ versions, it is provided by 'Service.locked', which depends on whether the lockscreen window is opened. ## Why? ** important: things happened, so I just list them with no blames, no matter to me or anybody ** The most incredible thing is all of these were spawned from one single file: the 'lockscreen.js', which grew as 2K lines between our 1.2 ~ 1.3 releases, and soon became the second largest file in System app (the #1 is window manger). That's why I pulled the sliding feature from it to 'lockscreen_slide.js' in the shared directory. And if it looks weird today, it's because the CallScreen (allow you to receive phone call when the screen is locked) uses the same sliding component to provide almost the same UI. You can call it's an experimental "widget" or "web component" before we have real widgets and Gaia components, and it indeed worked well, especially compare to the notification screen (mentioned later). So as you can see, LockScreen is in fact a widget manager, which needs to install those heterogeneous "widgets" on its panels, and also need to control them to show up or be hidden. It's in fact also a tiny window manager, if you consider we control the secure apps (ex: emergency call & secure camera) and the "input pad" for the passcode by our own. All of these were controlled by the monolithic 'lockscreen.js'. In some cases, this omnipotent component even needed to handle very detailed events as which key on the keypad had been pressed, since at that time we didn't pull it as a standalone 'window' yet. In the long term, it was impossible to maintain all things in one single file like this while we were stockpiling new features on that again and again. So we tried to decouple it and make the architecture more reasonable. However, we actually faced at least four seriously issues that were difficult to solve: 1. LockScreen was (and sadly, is) not an app. In fact, it's even not an iframe 2. Notification screen treats LockScreen as another "screen", not a standalone component 3. On CI server, Gip, Gij and even unit tests all tried to avoid device locked, but they used some brutal ways to do that 4. Internal components rely on LockScreen make it must be 'initialized' before lots of components; some of them were even initialized at the the script loaded moment And other minor issues hindered us to decouple it. The first thing was almost the root cause of all evils. Due to it's not an app or iframe, when other components in System app need to read or write info of LockScreen, they chose the shortest path: grab the instance and directly manipulated it. Although this anti-pattern is greatly abandoned now (thanks everybody), at past it caused lots of problems from reasoning how to detect if screen is locked to 'shared' the same notification screen on both UtilityTray and LockScreen (the issue#2). The most regrettable thing is we once had a great chance to change that (Bug 898348) before all the following features landed, but when we faced the dilemma of which path we should chose, we chose to refactor the code first. After that, even I've tried to make it as an app again, I just encountered serious CI issues so to continue it will only delay the release features (I remember it's v2.0), therefore I gave it up. One serious consequence caused by this failure is we're hardly to implement replacable screen locker, since all the things are belong to System all and in the same iframe. So it's almost impossible to allow 3rd-party app developers change things from its UI to behaviors. Another minor issue is we're difficult to stop growing up System by all *.js files and HTML content of LockScreen. Although I finally pulled the HTML part from 'index.html' to another file, and wrote a build script to merge them at build time, this still caused some mysterious failures only on b2g-inbound, which blocked Alive's new bootstrapping and only be workarounded at all. Back to the case: although we were failed to put things in an iframe, we at least should clarify all the internal dependencies of LockScreen and others in System app. And apparently, the most complicated one is Notification Screen. In the past version, namely with the much simpler versions of LockScreen, it seemed reasonable to append one notification to two places at the same time: one for the utility tray and another for screen locker. That's why you now still can see there are some function depends on the LockScreen instance. The result of this refactoring are 'lockscreen_notification.js' and 'lockscreen_notification_builder.js', which were also for the new UX feature to ask LockScreen own different looks & feel from Utility Tray, and the clickable notifications on the screen. These changes were landed from v2.0 to v2.1. And another earlier but more successful change is we implemented the 'lock_screen_window_manager' at v1.4, to make it follow the ordinary path of launching and closing an app window, although there was no real iframe for that at all. This also benefited us to make a new way to detect whether LockScreen is on: if the window is opened, Service.locked should be true. Otherwise, it's false. The similar and even earlier change is at v1.3, we landed the 'secure_window_manager.js' to control all secure app window. Both of them are important to keep reducing the size and items from the original 'lockscreen.js'. Before these two managers, all of such "windows" were controlled only by the monolithic controller. Now let's talk about the state manger. State manager was landed to solve Bug 1043892 at v2.0. It tried to solve the problem that we relied on lots of variables and even CSS classes to manage panel switching, and these were all managed in the 'lockscreen.js'. The major issue of this was the relation of inputs and the transitioning were unclear, since they were scattered about different state changes. Therefore, the original design of state manager was to collect all of these inputs (ex: events and mozSettings) in one place, and we could decide which state (and panel) we should transfer to with clear rules and reasons. Basically, this worked under such premises and assumptions: 1. LockScreen and the inner states had been initialized when the manager were launched 2. At beginning It should only control the panel UI, but not how and when unlocking the screen 3. There was only one state at one time: this should prevent data racing of writing and reading 4. Each state could output some new data as the next input. This was for continuing state transferring, like to draw how three states transfer from the first to the last one However we've found these premises and assumptions were failed one by one. The #4 failed since the sliced the logic of describing how a continuing process to become some vague input changes, rather than directly indicate which is the next state it need to transfer to. And #3 failed because state transferring usually takes time to perform animation, although this was considered in the original design and enhanced by adding a 'transferOut' method. The #2 failed was because more and more bugs proved that before we revoke all the listeners of legacy LockScreen, racings couldn't be eliminated. Finally, #1 failed because some pending readings were covered by some "default" values, plus we now ask components to be available for user as soon as possible, so to block UI before we get all the info is impossible. As a result, we spent time to design a new state machine according these experiences and other existing designs in Gaia, which was completed at v2.1. However, to migrate it from the existing two legacy designs was not easy, so we experimentally migrated the clock widget on LockScreen to the new design. And the result was not bad, so I tried to re-write the LockScreenConnInfo widget as the second attempt in Feb 2015 (since 2.2 is so long I mark the month). The end of the story is simple: people told me LockScreen would be cancelled in the fancy v3, so it seemed worthless to continue these efforts to migrating to the new design, although I have continued the state machine and released it as an open framework (see? I still did some substantial things). That's the major reason why now you feel the whole code are so strange and mixed up, and it wasn't changed so long. Anyway, after the time of troubles as we all know, it looks like in v2.5 we don't cancel it after all. Therefore I need to work on it again. At least, I need to fix those covered security bugs now revealed by the new bootstrapping of System app, and intermittent tests. My current task queues are: 1. Fix recent security bugs almost by the same cause; for the old and current branches 2. Remove all listeners of important values from 'lockscreen.js' to the state manager 3. Fix intermittent error of media widget Gij tests 4. Fix test failures for greening the Mulet That's all. If there are anything incorrect, please patch it in this thread. Happy Monday! -- <http://about.me/snowmantw>Greg Weng http://about.me/snowmantw *Understand y f = f [ y f ] ; lose last remaining non-major friend* * -- Anonymous*
_______________________________________________ dev-b2g mailing list dev-b2g@lists.mozilla.org https://lists.mozilla.org/listinfo/dev-b2g