title: Restartless add-on upgrade: reload XBL element in CustomizableUI widget
body: <p>I've made a restartless add-on that is a <code>CustomizableUI</code> widget based on custom <code>XBL</code> elements. For an upcoming version I have altered the main <code>XBL</code> element <code>&lt;slyzoom&gt;</code>.</p>
  
  <p>However, when I test the upgrade and inspect the markup with the DOM Inspector, the main <code>XBL</code> element is not refreshed in currently opened windows — only in newly opened windows the <code>XBL</code> element is expressed as intended.</p>
  
  <p>I'm not entirely sure whether the problem lies with <code>CustomizableUI</code> not refreshing properly, or with the <code>XBL</code> bindings not refreshing properly.</p>
  
  <p>And, although I don't have to restart Firefox, opening a new window for upgrade changes to take effect still kind of defeats the purpose of having a restartless add-on, for me.</p>
  
  <p><a href="https://stackoverflow.com/questions/24403667/dynamic-way-to-unbind-dynamically-binded-xbl">Another question, by Noitidart</a>, seems to hint at a similar problem, although Noitidart seems to use a slightly different approach of loading bindings rules, so I'm not entirely sure my problem is exactly the same.</p>
  
  <p>Can you shed a light on where the problem most likely lies?</p>
  
  <p>My <a href="https://addons.mozilla.org/firefox/files/browse/297874/file/bootstrap.js" rel="nofollow"><code>bootstrap.js</code></a><sup>1</sup> file is fairly involved, so I'll only put a simplified, slimmed down version here, with only <code>startup()</code> and <code>shutdown()</code> and with what I think are the most important aspects of getting it to work (see inline comments for additional commentary):</p>
  
  <pre class="lang-js prettyprint-override"><code>const Cc = Components.classes;
  const Ci = Components.interfaces;
  const Cu = Components.utils;
  
  const NSXUL            = 'http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul';
  const URI_CSS_BINDINGS = 'chrome://slyzoom/content/bindings.css';
  
  let ioService      = Cc[ '@mozilla.org/network/io-service;1' ].getService( Ci.nsIIOService );
  let windowMediator = Cc[ '@mozilla.org/appshell/window-mediator;1' ].getService( Ci.nsIWindowMediator );
  
  let styleSheetUri = ioService.newURI( URI_CSS_BINDINGS, null, null );
  
  function startup( data, reason ) {
  
    let CustomizableUI = Cu.import( 'resource:///modules/CustomizableUI.jsm', {} ).CustomizableUI;
  
    // create the widget, this all works fine
    CustomizableUI.createWidget( {
      id: 'slyzoom',
      type: 'custom',
      defaultArea: null,
      onBuild: function( document ) {
        let window      = document.defaultView;
        let windowUtils = window.getInterface( Ci.nsIDOMWindowUtils );
  
        // load the XBL bindings stylesheet in the window where my widget is being build
        windowUtils.loadSheet( styleSheetUri, Ci.nsIDOMWindowUtils.AUTHOR_SHEET );
  
        let toolBarItemAttributes = {
          id: 'slyzoom',
          label: 'SlyZoom',
          title: 'SlyZoom',
          removable: 'true',
          overflows: false
        }
        let toolBarItem = document.createElementNS( NSXUL, 'toolbaritem' );
        let slyZoom     = document.createElementNS( NSXUL, 'slyzoom' );
  
        for( let attributeName in toolBarItemAttributes ) {
          toolBarItem.setAttribute( attributeName, toolBarItemAttributes[ attributeName ] );
        }
        toolBarItem.appendChild( slyZoom );
  
        let listener = {
          onWidgetInstanceRemoved: function( id, widgetDocument ) {
            if( id == this.id &amp;&amp; widgetDocument == document ) {
              CustomizableUI.removeListener( listener );
  
              // unload the XBL bindings stylesheet from the window where my widget is being removed
              windowUtils.removeSheet( styleSheetUri, Ci.nsIDOMWindowUtils.AUTHOR_SHEET );
            }
          }.bind( this ),
        };
        CustomizableUI.addListener( listener );
  
        return toolBarItem;
      }
    } );
  }
  
  
  // I thought everything in here would be enough to force a reload in startup(), but apparently it isn't
  function shutdown( data, reason ) {
  
    if( APP_SHUTDOWN == reason ) {
      return;
    }
  
    let CustomizableUI = Cu.import( 'resource:///modules/CustomizableUI.jsm', {} ).CustomizableUI;
  
    // I thought this would destroy any DOM instance of my widget/XBL elements
    // and it actually does; I know this because I log messages
    // in the destructor of the XBL elements,
    // but this doesn't seem to be enough
    CustomizableUI.destroyWidget( 'slyzoom' );
  
    // unload the XBL bindings stylesheet from the every navigator window;
    // this doesn't do the job I expected it to do either
    let domWindows = windowMediator.getEnumerator( 'navigator:browser' );
    while( domWindows.hasMoreElements() ) {
      let domWindow   = domWindows.getNext();
      let windowUtils = domWindow.getInterface( Ci.nsIDOMWindowUtils );
      windowUtils.removeSheet( styleSheetUri, Ci.nsIDOMWindowUtils.AUTHOR_SHEET );
    }
  }
  </code></pre>
  
  <p><sup>1) follow the link to view the complete version</sup></p>