mozilla-b2g/gaia — pull request #23230 by jrburke (2014-08-22 23:36:54+00:00)
Title: Bug 1005446 - [email] try custom elements for cards

There was an earlier pass at this change in the [transformer branch/pull request](https://github.com/mozilla-b2g/gaia/pull/18909), but enough code drift occurred that I redid the work, and expanded on it.

## Goals

* Use custom elements for each card. Nothing fancy like using a Shadow DOM or constructing individual pieces of the cards from other components, just do a baseline switch. This will help our cards module and the individual cards because we do not need the `this` vs `this.domNode` disparity. `this` is now the root node.
* Use a custom element approach that uses mixins that help with auto-wiring DOM elements in the template to the custom element and for binding events to those sub-elements. So now the cards should not need to do domNode.getElementsByClassName or addEventListener for the simple cases.
* Use a version of alameda that uses platform Promises, which reduces the size of alameda by a little bit.
* Make sure evt async notifies listeners on an emit, instead of synchronously notifying. This seems more correct and guarantees next turn expectations when calling emit.

## Implementation

The custom element mixin approach uses the one from [jrburke/element](https://github.com/jrburke/element): an `element` and `template` loader plugin that handle mixing in objects and a template to form the final prototype that is given to document.registerElement. See that other repo for more information.

Also, I removed prim from alameda and use the native Promise directly. With that, I converted `evt`'s emit() to use Promise.resolve() to dispatch the emit, so that we get a proper async notification to listeners.

With these changes I broke up mail_common into specific modules for each piece. So, no more mail_common.

I converted all the cards over to this approach, and it appears to be working well. I put up an app zip here if you want to try it out without fetching the branch:

http://jrburke.com/work/gaia/email-element.zip

## Stats

Brief testing with WebIDE Monitor memory usage for message_list startup, waiting a bit to let it settle to a steady state, seems to be similar before and after, in the low 20s range (MB).

Zip file size when created with GAIA_OPTIMIZE=1:
old: 579778 bytes
new: 580983 bytes

I am having trouble with `make test-perf` today, it is erroring out with a 'couldn't collect mem usage' message, so I will try a bit later for that. It does not feel slower though, and if we notice any decrease in numbers it could be because I disabled the caching of the new account card in mail_app. We could renable that option, but I want to collect numbers first before deciding to do that.

## Notes

* I also took the time to fix any jshint errors for all but one of the non-test JS files. This adds to the diff, but given that enough of the files are changing anyway, it seemed like the right time to fix these up.
* `Cards` is now `cards` and `Toast` is now `toast`, to try to match general JS convention that only constructors should start with an upper-case character.
* Some code was removed if it was not called by anyone. Example is `Cards._createMaskForNode`.
* The `mix` module and its test was removed since it was just used to get the mixing before for card prototype, and the `element!` loader plugin does that now. We may need to add it later, but in the spirit of only keeping what is used, it is removed.
* The CSS files changed mostly to move to referencing custom element names as tag names instead of CSS class names.
* `template!` resources are inlined in builds like `tmpl!` ones were before, so similar optimization outcome. `tmpl!` is still kept around for the simple HTML templates used for some sub-parts of the UI. As we get more comfortable with making custom elements out of other custom elements, that may change over time.

### cards

It no longer has a "mode" and "tray" concept. `mode` can now be expressed by creating a variation of a custom element by using a base custom element as a mixin. This is how the search view for message list works now.

The `_cardStack` now just needs to track the DOM node used for the card, not cardImpl and domNode, since they are both the same. The cardDef can be derived by using the tag name to access it in `this._cardDefs`.

The old card approach passed arguments to some of the constructors. Now that we do not have access to passing arguments to the constructor. It is now just the basic element constructor made by the platform. An `onArgs` method convention is used by the cards to pass data to the cards after initial construction.

Module names are converted to custom element tag names by replacing any non lowercase or dash symbol to a dash. So `cards/some-thing` becomes `cards-some-thing`. This should work out when we start to consume `gaia-something` elements, if we place them in the `gaia/` module ID namespace. This should be doable via loader config.

The HTML cache still works, the `template` mixin does not insert a template if data-cache is set. This means our cacheable HTML elements are limited to non-shadow DOM types for now. It may not be so bad later for ones that do have shadow DOM/content insertion, it just might mean our cached HTML may look a bit silly at first. That, or we look at really streamlining loading to get the custom elements in there first. That may be hard to do generically though. In any case, HTML cache still works.

### Individual cards

Removed the outermost HTML element, as that is now the custom element. This meant an overall outdent of the HTML templates.

`extraClasses` is used now in cards if they want specific other classes added to the root element. For instance classes to control the type of card transition.

data-prop and data-event are added to the HTML templates to wire up instance properties and event handling automatically. This noticeably reduces the amount of boilerplate in the `createdCallback` functions.

For the JS definitions of the cards:

No more named contructor, that work is done in the `createdCallback` [custom element lifecycle event](https://github.com/jrburke/element#element-lifecycle-background). The `onArgs` method is used to receive data from `cards` during the time of construction.

The card now just exports an array of properties that are mixed in by the `element!` loader plugin to create the final prototype for the custom element. On the whole, that leads to a general indent of the code from before, which accounts for some of the changes.

`this.domNode` is now just `this`.

### cards/base

Holds some utility functions that were used by two or more cards.

### mail_app

I changed it to not save account settings card in the cookie cache, since that card is fairly simple, and it is not a card that is hit enough to warrant the optimization. The message list card is the big event, and it is still cached.

### model

This one changed a bit because `evt` not notifies listeners async. This caused a change in expectations from the point of getting the `api` module, and letting `mail_app` register things like `onbadlogin` and then the start of slice creation. The `onApi` function was introduced so that the slice creation would be added to the end of the listener queue for 'api', to maintain the behavior that was previously expected.

Fortunately this change was indicated by the model_acctsslice_startup_test.js test file -- the first pass at this pull request failed due to not considering this impact. With model's onApi internal function adaptation the test passes now.

### test/unit/evt_test

Changed because emits are now async.

### test/unit/setup_manual_config_protocol_dropdown_test

The port selection tests were converted to `test.skip` because of trouble getting the custom change event to fire. See the notes in the file as to suspicions on why. Longer term it would be great to sort out why, but it may mean dealing with some of how the test infrastructure works.