Bug 1018062 - Rip the sandbox code out of FeedWriter.js. r=Mano
authorBlake Kaplan <mrbkap@gmail.com>
Thu, 17 Sep 2015 10:43:55 -0700
changeset 295822 ed463b28311302f386dbae7869bf5e817a61761b
parent 295821 26529dbf90ad751ff7026bf734cc27c8d7c72436
child 295823 080075cb97f41e06810cdc8e429f776ddf28477b
push id5245
push userraliiev@mozilla.com
push dateThu, 29 Oct 2015 11:30:51 +0000
treeherdermozilla-beta@dac831dc1bd0 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersMano
bugs1018062
milestone43.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1018062 - Rip the sandbox code out of FeedWriter.js. r=Mano
browser/components/feeds/FeedWriter.js
--- a/browser/components/feeds/FeedWriter.js
+++ b/browser/components/feeds/FeedWriter.js
@@ -128,32 +128,37 @@ function getPrefReaderForType(t) {
  * Converts a number of bytes to the appropriate unit that results in a
  * number that needs fewer than 4 digits
  *
  * @return a pair: [new value with 3 sig. figs., its unit]
   */
 function convertByteUnits(aBytes) {
   var units = ["bytes", "kilobyte", "megabyte", "gigabyte"];
   let unitIndex = 0;
- 
+
   // convert to next unit if it needs 4 digits (after rounding), but only if
   // we know the name of the next unit
   while ((aBytes >= 999.5) && (unitIndex < units.length - 1)) {
     aBytes /= 1024;
     unitIndex++;
   }
- 
+
   // Get rid of insignificant bits by truncating to 1 or 0 decimal points
   // 0 -> 0; 1.2 -> 1.2; 12.3 -> 12.3; 123.4 -> 123; 234.5 -> 235
   aBytes = aBytes.toFixed((aBytes > 0) && (aBytes < 100) ? 1 : 0);
- 
+
   return [aBytes, units[unitIndex]];
 }
 
-function FeedWriter() {}
+function FeedWriter() {
+  this._selectedApp = undefined;
+  this._selectedAppMenuItem = null;
+  this._defaultHandlerMenuItem = null;
+}
+
 FeedWriter.prototype = {
   _mimeSvc      : Cc["@mozilla.org/mime;1"].
                   getService(Ci.nsIMIMEService),
 
   _getPropertyAsBag: function FW__getPropertyAsBag(container, property) {
     return container.fields.getProperty(property).
                      QueryInterface(Ci.nsIPropertyBag2);
   },
@@ -163,99 +168,54 @@ FeedWriter.prototype = {
       return container.fields.getPropertyAsAString(property);
     }
     catch (e) {
     }
     return "";
   },
 
   _setContentText: function FW__setContentText(id, text) {
-    this._contentSandbox.element = this._document.getElementById(id);
-    this._contentSandbox.textNode = text.createDocumentFragment(this._contentSandbox.element);
-    var codeStr =
-      "while (element.hasChildNodes()) " +
-      "  element.removeChild(element.firstChild);" +
-      "element.appendChild(textNode);";
+    var element = this._document.getElementById(id);
+    var textNode = text.createDocumentFragment(element);
+    while (element.hasChildNodes())
+      element.removeChild(element.firstChild);
+    element.appendChild(textNode);
     if (text.base) {
-      this._contentSandbox.spec = text.base.spec;
-      codeStr += "element.setAttributeNS('" + XML_NS + "', 'base', spec);";
+      element.setAttributeNS(XML_NS, 'base', text.base.spec);
     }
-    Cu.evalInSandbox(codeStr, this._contentSandbox);
-    this._contentSandbox.element = null;
-    this._contentSandbox.textNode = null;
   },
 
   /**
    * Safely sets the href attribute on an anchor tag, providing the URI 
    * specified can be loaded according to rules. 
    * @param   element
    *          The element to set a URI attribute on
    * @param   attribute
    *          The attribute of the element to set the URI to, e.g. href or src
    * @param   uri
    *          The URI spec to set as the href
    */
-  _safeSetURIAttribute: 
+  _safeSetURIAttribute:
   function FW__safeSetURIAttribute(element, attribute, uri) {
     var secman = Cc["@mozilla.org/scriptsecuritymanager;1"].
-                 getService(Ci.nsIScriptSecurityManager);    
+                 getService(Ci.nsIScriptSecurityManager);
     const flags = Ci.nsIScriptSecurityManager.DISALLOW_INHERIT_PRINCIPAL;
     try {
+      // TODO Is this necessary?
       secman.checkLoadURIStrWithPrincipal(this._feedPrincipal, uri, flags);
       // checkLoadURIStrWithPrincipal will throw if the link URI should not be
       // loaded, either because our feedURI isn't allowed to load it or per
       // the rules specified in |flags|, so we'll never "linkify" the link...
     }
     catch (e) {
       // Not allowed to load this link because secman.checkLoadURIStr threw
       return;
     }
 
-    this._contentSandbox.element = element;
-    this._contentSandbox.uri = uri;
-    var codeStr = "element.setAttribute('" + attribute + "', uri);";
-    Cu.evalInSandbox(codeStr, this._contentSandbox);
-  },
-
-  /**
-   * Use this sandbox to run any dom manipulation code on nodes which
-   * are already inserted into the content document.
-   */
-  __contentSandbox: null,
-  get _contentSandbox() {
-    // This whole sandbox setup is totally archaic. It was introduced in bug
-    // 360529, presumably before the existence of a solid security membrane,
-    // since all of the manipulation of content here should be made safe by
-    // Xrays. And now that anonymous content is no longer content-accessible,
-    // manipulating the xml stylesheet content can't be done from content
-    // anymore.
-    //
-    // The right solution would be to rip out all of this sandbox junk and
-    // manipulate the DOM directly. But that's a big yak to shave, so for now,
-    // we just give the sandbox an nsExpandedPrincipal with []. This has the
-    // effect of giving it Xrays, and making it same-origin with the XBL scope,
-    // thereby letting it manipulate anonymous content.
-    if (!this.__contentSandbox)
-      this.__contentSandbox = new Cu.Sandbox([this._window],
-                                             {sandboxName: 'FeedWriter'});
-
-    return this.__contentSandbox;
-  },
-
-  /**
-   * Calls doCommand for a given XUL element within the context of the
-   * content document.
-   *
-   * @param aElement
-   *        the XUL element to call doCommand() on.
-   */
-  _safeDoCommand: function FW___safeDoCommand(aElement) {
-    this._contentSandbox.element = aElement;
-    Cu.evalInSandbox("element.doCommand();", this._contentSandbox);
-    this._contentSandbox.element = null;
+    element.setAttribute(attribute, uri);
   },
 
   __faviconService: null,
   get _faviconService() {
     if (!this.__faviconService)
       this.__faviconService = Cc["@mozilla.org/browser/favicon-service;1"].
                               getService(Ci.nsIFaviconService);
 
@@ -289,44 +249,39 @@ FeedWriter.prototype = {
 
       node = node.nextSibling;
     }
 
     return null;
   },
 
   _setCheckboxCheckedState: function FW__setCheckboxCheckedState(aCheckbox, aValue) {
-    // see checkbox.xml, xbl bindings are not applied within the sandbox!
-    this._contentSandbox.checkbox = aCheckbox;
-    var codeStr;
+    // see checkbox.xml, xbl bindings are not applied within the sandbox! TODO
     var change = (aValue != (aCheckbox.getAttribute('checked') == 'true'));
     if (aValue)
-      codeStr = "checkbox.setAttribute('checked', 'true'); ";
+      aCheckbox.setAttribute('checked', 'true');
     else
-      codeStr = "checkbox.removeAttribute('checked'); ";
+      aCheckbox.removeAttribute('checked');
 
     if (change) {
-      this._contentSandbox.document = this._document;
-      codeStr += "var event = document.createEvent('Events'); " +
-                 "event.initEvent('CheckboxStateChange', true, true);" +
-                 "checkbox.dispatchEvent(event);"
+      var event = this._document.createEvent('Events');
+      event.initEvent('CheckboxStateChange', true, true);
+      aCheckbox.dispatchEvent(event);
     }
-
-    Cu.evalInSandbox(codeStr, this._contentSandbox);
   },
 
    /**
    * Returns a date suitable for displaying in the feed preview. 
    * If the date cannot be parsed, the return value is "false".
    * @param   dateString
    *          A date as extracted from a feed entry. (entry.updated)
    */
   _parseDate: function FW__parseDate(dateString) {
     // Convert the date into the user's local time zone
-    dateObj = new Date(dateString);
+    var dateObj = new Date(dateString);
 
     // Make sure the date we're given is valid.
     if (!dateObj.getTime())
       return false;
 
     var dateService = Cc["@mozilla.org/intl/scriptabledateformat;1"].
                       getService(Ci.nsIScriptableDateFormat);
     return dateService.FormatDateTime("", dateService.dateFormatLong, dateService.timeFormatNoSeconds,
@@ -373,62 +328,52 @@ FeedWriter.prototype = {
    * Writes the feed title into the preview document.
    * @param   container
    *          The feed container
    */
   _setTitleText: function FW__setTitleText(container) {
     if (container.title) {
       var title = container.title.plainText();
       this._setContentText(TITLE_ID, container.title);
-      this._contentSandbox.document = this._document;
-      this._contentSandbox.title = title;
-      var codeStr = "document.title = title;"
-      Cu.evalInSandbox(codeStr, this._contentSandbox);
+      this._document.title = title;
     }
 
     var feed = container.QueryInterface(Ci.nsIFeed);
     if (feed && feed.subtitle)
       this._setContentText(SUBTITLE_ID, container.subtitle);
   },
 
   /**
    * Writes the title image into the preview document if one is present.
    * @param   container
    *          The feed container
    */
   _setTitleImage: function FW__setTitleImage(container) {
     try {
       var parts = container.image;
-      
+
       // Set up the title image (supplied by the feed)
       var feedTitleImage = this._document.getElementById("feedTitleImage");
       this._safeSetURIAttribute(feedTitleImage, "src", 
                                 parts.getPropertyAsAString("url"));
 
       // Set up the title image link
       var feedTitleLink = this._document.getElementById("feedTitleLink");
 
       var titleText = this._getFormattedString("linkTitleTextFormat", 
                                                [parts.getPropertyAsAString("title")]);
-      this._contentSandbox.feedTitleLink = feedTitleLink;
-      this._contentSandbox.titleText = titleText;
-      this._contentSandbox.feedTitleText = this._document.getElementById("feedTitleText");
-      this._contentSandbox.titleImageWidth = parseInt(parts.getPropertyAsAString("width")) + 15;
+      var feedTitleText = this._document.getElementById("feedTitleText");
+      var titleImageWidth = parseInt(parts.getPropertyAsAString("width")) + 15;
 
       // Fix the margin on the main title, so that the image doesn't run over
       // the underline
-      var codeStr = "feedTitleLink.setAttribute('title', titleText); " +
-                    "feedTitleText.style.marginRight = titleImageWidth + 'px';";
-      Cu.evalInSandbox(codeStr, this._contentSandbox);
-      this._contentSandbox.feedTitleLink = null;
-      this._contentSandbox.titleText = null;
-      this._contentSandbox.feedTitleText = null;
-      this._contentSandbox.titleImageWidth = null;
+      feedTitleLink.setAttribute('title', titleText);
+      feedTitleText.style.marginRight = titleImageWidth + 'px';
 
-      this._safeSetURIAttribute(feedTitleLink, "href", 
+      this._safeSetURIAttribute(feedTitleLink, "href",
                                 parts.getPropertyAsAString("link"));
     }
     catch (e) {
       LOG("Failed to set Title Image (this is benign): " + e);
     }
   },
 
   /**
@@ -437,18 +382,17 @@ FeedWriter.prototype = {
    *          The container of entries in the feed
    */
   _writeFeedContent: function FW__writeFeedContent(container) {
     // Build the actual feed content
     var feed = container.QueryInterface(Ci.nsIFeed);
     if (feed.items.length == 0)
       return;
 
-    this._contentSandbox.feedContent =
-      this._document.getElementById("feedContent");
+    var feedContent = this._document.getElementById("feedContent");
 
     for (var i = 0; i < feed.items.length; ++i) {
       var entry = feed.items.queryElementAt(i, Ci.nsIFeedEntry);
       entry.QueryInterface(Ci.nsIFeedContainer);
 
       var entryContainer = this._document.createElementNS(HTML_NS, "div");
       entryContainer.className = "entry";
 
@@ -505,29 +449,22 @@ FeedWriter.prototype = {
       body.className = "feedEntryContent";
       entryContainer.appendChild(body);
 
       if (entry.enclosures && entry.enclosures.length > 0) {
         var enclosuresDiv = this._buildEnclosureDiv(entry);
         entryContainer.appendChild(enclosuresDiv);
       }
 
-      this._contentSandbox.entryContainer = entryContainer;
-      this._contentSandbox.clearDiv =
-        this._document.createElementNS(HTML_NS, "div");
-      this._contentSandbox.clearDiv.style.clear = "both";
-      
-      var codeStr = "feedContent.appendChild(entryContainer); " +
-                     "feedContent.appendChild(clearDiv);"
-      Cu.evalInSandbox(codeStr, this._contentSandbox);
+      var clearDiv = this._document.createElementNS(HTML_NS, "div");
+      clearDiv.style.clear = "both";
+
+      feedContent.appendChild(entryContainer);
+      feedContent.appendChild(clearDiv);
     }
-
-    this._contentSandbox.feedContent = null;
-    this._contentSandbox.entryContainer = null;
-    this._contentSandbox.clearDiv = null;
   },
 
   /**
    * Takes a url to a media item and returns the best name it can come up with.
    * Frequently this is the filename portion (e.g. passing in 
    * http://example.com/foo.mpeg would return "foo.mpeg"), but in more complex
    * cases, this will return the entire url (e.g. passing in
    * http://example.com/somedirectory/ would return 
@@ -622,34 +559,32 @@ FeedWriter.prototype = {
     }
 
     return enclosuresDiv;
   },
 
   /**
    * Gets a valid nsIFeedContainer object from the parsed nsIFeedResult.
    * Displays error information if there was one.
-   * @param   result
-   *          The parsed feed result
    * @returns A valid nsIFeedContainer object containing the contents of
    *          the feed.
    */
-  _getContainer: function FW__getContainer(result) {
-    var feedService = 
+  _getContainer: function FW__getContainer() {
+    var feedService =
         Cc["@mozilla.org/browser/feeds/result-service;1"].
         getService(Ci.nsIFeedResultService);
 
     try {
-      var result = 
+      var result =
         feedService.getFeedResult(this._getOriginalURI(this._window));
     }
     catch (e) {
       LOG("Subscribe Preview: feed not available?!");
     }
-    
+
     if (result.bozo) {
       LOG("Subscribe Preview: feed result is bozo?!");
     }
 
     try {
       var container = result.doc;
     }
     catch (e) {
@@ -703,22 +638,20 @@ FeedWriter.prototype = {
    * Helper method to set the selected application and system default
    * reader menuitems details from a file object
    *   @param aMenuItem
    *          The menuitem on which the attributes should be set
    *   @param aFile
    *          The menuitem's associated file
    */
   _initMenuItemWithFile: function(aMenuItem, aFile) {
-    this._contentSandbox.menuitem = aMenuItem;
-    this._contentSandbox.label = this._getFileDisplayName(aFile);
-    this._contentSandbox.image = this._getFileIconURL(aFile);
-    var codeStr = "menuitem.setAttribute('label', label); " +
-                  "menuitem.setAttribute('image', image);"
-    Cu.evalInSandbox(codeStr, this._contentSandbox);
+    var label = this._getFileDisplayName(aFile);
+    var image = this._getFileIconURL(aFile);
+    aMenuitem.setAttribute('label', label);
+    aMenuitem.setAttribute('image', image);
   },
 
   /**
    * Helper method to get an element in the XBL binding where the handler
    * selection UI lives
    */
   _getUIElement: function FW__getUIElement(id) {
     return this._document.getAnonymousElementByAttribute(
@@ -744,23 +677,22 @@ FeedWriter.prototype = {
 #expand             if (fp.file.leafName != "__MOZ_APP_NAME__.exe") {
 #else
 #ifdef XP_MACOSX
 #expand             if (fp.file.leafName != "__MOZ_MACBUNDLE_NAME__") {
 #else
 #expand             if (fp.file.leafName != "__MOZ_APP_NAME__-bin") {
 #endif
 #endif
-              this._initMenuItemWithFile(this._contentSandbox.selectedAppMenuItem,
+              this._initMenuItemWithFile(this._selectedAppMenuItem,
                                          this._selectedApp);
 
               // Show and select the selected application menuitem
-              let codeStr = "selectedAppMenuItem.hidden = false;" +
-                            "selectedAppMenuItem.doCommand();"
-              Cu.evalInSandbox(codeStr, this._contentSandbox);
+              this._selectedAppMenuItem.hidden = false;
+              this._selectedAppMenuItem.doCommand();
               if (aCallback) {
                 aCallback(true);
                 return;
               }
             }
           }
         }
         if (aCallback) {
@@ -798,21 +730,19 @@ FeedWriter.prototype = {
         stringLabel = "subscribeVideoPodcastUsing";
         break;
 
       case Ci.nsIFeed.TYPE_AUDIO:
         stringLabel = "subscribeAudioPodcastUsing";
         break;
     }
 
-    this._contentSandbox.subscribeUsing =
-      this._getUIElement("subscribeUsingDescription");
-    this._contentSandbox.label = this._getString(stringLabel);
-    var codeStr = "subscribeUsing.setAttribute('value', label);"
-    Cu.evalInSandbox(codeStr, this._contentSandbox);
+    var subscribeUsing = this._getUIElement("subscribeUsingDescription");
+    var label = this._getString(stringLabel);
+    subscribeUsing.setAttribute('value', label);
   },
 
   _setAlwaysUseLabel: function FW__setAlwaysUseLabel() {
     var checkbox = this._getUIElement("alwaysUse");
     if (checkbox) {
       if (this._handlersMenuList) {
         var handlerName = this._getSelectedItemFromMenulist(this._handlersMenuList)
                               .getAttribute("label");
@@ -822,21 +752,19 @@ FeedWriter.prototype = {
             stringLabel = "alwaysUseForVideoPodcasts";
             break;
 
           case Ci.nsIFeed.TYPE_AUDIO:
             stringLabel = "alwaysUseForAudioPodcasts";
             break;
         }
 
-        this._contentSandbox.checkbox = checkbox;
-        this._contentSandbox.label = this._getFormattedString(stringLabel, [handlerName]);
-        
-        var codeStr = "checkbox.setAttribute('label', label);";
-        Cu.evalInSandbox(codeStr, this._contentSandbox);
+        var label = this._getFormattedString(stringLabel, [handlerName]);
+
+        checkbox.setAttribute('label', label);
       }
     }
   },
 
   // nsIDomEventListener
   handleEvent: function(event) {
     if (event.target.ownerDocument != this._document) {
       LOG("FeedWriter.handleEvent: Someone passed the feed writer as a listener to the events of another document!");
@@ -870,17 +798,17 @@ FeedWriter.prototype = {
           break;
         default:
           this._setAlwaysUseLabel();
       }
     }
   },
 
   _setSelectedHandler: function FW__setSelectedHandler(feedType) {
-    var prefs =   
+    var prefs =
         Cc["@mozilla.org/preferences-service;1"].
         getService(Ci.nsIPrefBranch);
 
     var handler = "bookmarks";
     try {
       handler = prefs.getCharPref(getPrefReaderForType(feedType));
     }
     catch (ex) { }
@@ -897,77 +825,74 @@ FeedWriter.prototype = {
           }
           var handlers =
             this._handlersMenuList.getElementsByAttribute("webhandlerurl", url);
           if (handlers.length == 0) {
             LOG("FeedWriter._setSelectedHandler: selected web handler isn't in the menulist")
             return;
           }
 
-          this._safeDoCommand(handlers[0]);
+          handlers[0].doCommand();
         }
         break;
       }
       case "client": {
         try {
           this._selectedApp =
             prefs.getComplexValue(getPrefAppForType(feedType), Ci.nsILocalFile);
         }
         catch(ex) {
           this._selectedApp = null;
         }
 
         if (this._selectedApp) {
-          this._initMenuItemWithFile(this._contentSandbox.selectedAppMenuItem,
+          this._initMenuItemWithFile(this._selectedAppMenuItem,
                                      this._selectedApp);
-          var codeStr = "selectedAppMenuItem.hidden = false; " +
-                        "selectedAppMenuItem.doCommand(); ";
+          this._selectedAppMenuItem.hidden = false;
+          this._selectedAppMenuItem.doCommand();
 
           // Only show the default reader menuitem if the default reader
           // isn't the selected application
           if (this._defaultSystemReader) {
             var shouldHide =
               this._defaultSystemReader.path == this._selectedApp.path;
-            codeStr += "defaultHandlerMenuItem.hidden = " + shouldHide + ";"
+            this._defaultHandlerMenuItem.hidden = shouldHide;
           }
-          Cu.evalInSandbox(codeStr, this._contentSandbox);
           break;
         }
       }
       case "bookmarks":
       default: {
         var liveBookmarksMenuItem = this._getUIElement("liveBookmarksMenuItem");
         if (liveBookmarksMenuItem)
-          this._safeDoCommand(liveBookmarksMenuItem);
+          liveBookmarksMenuItem.doCommand();
       } 
     }
   },
 
   _initSubscriptionUI: function FW__initSubscriptionUI() {
     var handlersMenuPopup = this._getUIElement("handlersMenuPopup");
     if (!handlersMenuPopup)
       return;
- 
+
     var feedType = this._getFeedType();
-    var codeStr;
 
     // change the background
     var header = this._document.getElementById("feedHeader");
-    this._contentSandbox.header = header;
     switch (feedType) {
       case Ci.nsIFeed.TYPE_VIDEO:
-        codeStr = "header.className = 'videoPodcastBackground'; ";
+        header.className = 'videoPodcastBackground';
         break;
 
       case Ci.nsIFeed.TYPE_AUDIO:
-        codeStr = "header.className = 'audioPodcastBackground'; ";
+        header.className = 'audioPodcastBackground';
         break;
 
       default:
-        codeStr = "header.className = 'feedBackground'; ";
+        header.className = 'feedBackground';
     }
 
     var liveBookmarksMenuItem = this._getUIElement("liveBookmarksMenuItem");
 
     // Last-selected application
     var menuItem = liveBookmarksMenuItem.cloneNode(false);
     menuItem.removeAttribute("selected");
     menuItem.setAttribute("anonid", "selectedAppMenuItem");
@@ -985,20 +910,19 @@ FeedWriter.prototype = {
         // Hide the menuitem if the last selected application doesn't exist
         menuItem.setAttribute("hidden", true);
       }
     }
     catch(ex) {
       // Hide the menuitem until an application is selected
       menuItem.setAttribute("hidden", true);
     }
-    this._contentSandbox.handlersMenuPopup = handlersMenuPopup;
-    this._contentSandbox.selectedAppMenuItem = menuItem;
-    
-    codeStr += "handlersMenuPopup.appendChild(selectedAppMenuItem); ";
+    this._selectedAppMenuItem = menuItem;
+
+    handlersMenuPopup.appendChild(this._selectedAppMenuItem);
 
     // List the default feed reader
     try {
       this._defaultSystemReader = Cc["@mozilla.org/browser/shell-service;1"].
                                   getService(Ci.nsIShellService).
                                   defaultFeedReader;
       menuItem = liveBookmarksMenuItem.cloneNode(false);
       menuItem.removeAttribute("selected");
@@ -1012,36 +936,32 @@ FeedWriter.prototype = {
       // as the last-selected application
       if (this._selectedApp &&
           this._selectedApp.path == this._defaultSystemReader.path)
         menuItem.hidden = true;
     }
     catch(ex) { menuItem = null; /* no default reader */ }
 
     if (menuItem) {
-      this._contentSandbox.defaultHandlerMenuItem = menuItem;
-      codeStr += "handlersMenuPopup.appendChild(defaultHandlerMenuItem); ";
+      this._defaultHandlerMenuItem = menuItem;
+      handlersMenuPopup.appendChild(this._defaultHandlerMenuItem);
     }
 
     // "Choose Application..." menuitem
     menuItem = liveBookmarksMenuItem.cloneNode(false);
     menuItem.removeAttribute("selected");
     menuItem.setAttribute("anonid", "chooseApplicationMenuItem");
     menuItem.className = "menuitem-iconic chooseApplicationMenuItem";
     menuItem.setAttribute("label", this._getString("chooseApplicationMenuItem"));
 
-    this._contentSandbox.chooseAppMenuItem = menuItem;
-    codeStr += "handlersMenuPopup.appendChild(chooseAppMenuItem); ";
+    handlersMenuPopup.appendChild(menuItem);
 
     // separator
-    this._contentSandbox.chooseAppSep =
-      menuItem = liveBookmarksMenuItem.nextSibling.cloneNode(false);
-    codeStr += "handlersMenuPopup.appendChild(chooseAppSep); ";
-
-    Cu.evalInSandbox(codeStr, this._contentSandbox);
+    var chooseAppSep = liveBookmarksMenuItem.nextSibling.cloneNode(false);
+    handlersMenuPopup.appendChild(chooseAppSep);
 
     // List of web handlers
     var wccr = Cc["@mozilla.org/embeddor.implemented/web-content-handler-registrar;1"].
                getService(Ci.nsIWebContentConverterService);
     var handlers = wccr.getContentHandlers(this._getMimeTypeForFeedType(feedType));
     if (handlers.length != 0) {
       for (var i = 0; i < handlers.length; ++i) {
         if (!handlers[i].uri) {
@@ -1049,23 +969,20 @@ FeedWriter.prototype = {
           continue;
         }
         menuItem = liveBookmarksMenuItem.cloneNode(false);
         menuItem.removeAttribute("selected");
         menuItem.className = "menuitem-iconic";
         menuItem.setAttribute("label", handlers[i].name);
         menuItem.setAttribute("handlerType", "web");
         menuItem.setAttribute("webhandlerurl", handlers[i].uri);
-        this._contentSandbox.menuItem = menuItem;
-        codeStr = "handlersMenuPopup.appendChild(menuItem);";
-        Cu.evalInSandbox(codeStr, this._contentSandbox);
+        handlersMenuPopup.appendChild(menuItem);
 
         this._setFaviconForWebReader(handlers[i].uri, menuItem);
       }
-      this._contentSandbox.menuItem = null;
     }
 
     this._setSelectedHandler(feedType);
 
     // "Subscribe using..."
     this._setSubscribeUsingLabel();
 
     // "Always use..." checkbox initial state
@@ -1097,27 +1014,26 @@ FeedWriter.prototype = {
           textfeedinfo1 = "feedSubscriptionAudioPodcast1";
           textfeedinfo2 = "feedSubscriptionAudioPodcast2";
           break;
         default:
           textfeedinfo1 = "feedSubscriptionFeed1";
           textfeedinfo2 = "feedSubscriptionFeed2";
       }
 
-      this._contentSandbox.feedinfo1 =
-        this._document.getElementById("feedSubscriptionInfo1");
-      this._contentSandbox.feedinfo1Str = this._getString(textfeedinfo1);
-      this._contentSandbox.feedinfo2 =
-        this._document.getElementById("feedSubscriptionInfo2");
-      this._contentSandbox.feedinfo2Str = this._getString(textfeedinfo2);
-      this._contentSandbox.header = header;
-      codeStr = "feedinfo1.textContent = feedinfo1Str; " +
-                "feedinfo2.textContent = feedinfo2Str; " +
-                "header.setAttribute('firstrun', 'true');"
-      Cu.evalInSandbox(codeStr, this._contentSandbox);
+      var feedinfo1 = this._document.getElementById("feedSubscriptionInfo1");
+      var feedinfo1Str = this._getString(textfeedinfo1);
+      var feedinfo2 = this._document.getElementById("feedSubscriptionInfo2");
+      var feedinfo2Str = this._getString(textfeedinfo2);
+
+      feedinfo1.textContent = feedinfo1Str;
+      feedinfo2.textContent = feedinfo2Str;
+
+      header.setAttribute('firstrun', 'true');
+
       prefs.setBoolPref(PREF_SHOW_FIRST_RUN_UI, false);
     }
   },
 
   /**
    * Returns the original URI object of the feed and ensures that this
    * component is only ever invoked from the preview document.  
    * @param aWindow 
@@ -1232,17 +1148,20 @@ FeedWriter.prototype = {
     prefs.removeObserver(PREF_AUDIO_SELECTED_READER, this);
     prefs.removeObserver(PREF_AUDIO_SELECTED_WEB, this);
     prefs.removeObserver(PREF_AUDIO_SELECTED_APP, this);
 
     this._removeFeedFromCache();
     this.__faviconService = null;
     this.__bundle = null;
     this._feedURI = null;
-    this.__contentSandbox = null;
+
+    this._selectedApp = undefined;
+    this._selectedAppMenuItem = null;
+    this._defaultHandlerMenuItem = null;
   },
 
   _removeFeedFromCache: function FW__removeFeedFromCache() {
     if (this._feedURI) {
       var feedService = Cc["@mozilla.org/browser/feeds/result-service;1"].
                         getService(Ci.nsIFeedResultService);
       feedService.removeFeedResult(this._feedURI);
       this._feedURI = null;
@@ -1391,22 +1310,17 @@ FeedWriter.prototype = {
                                          .usePrivateBrowsing;
     this._faviconService.setAndFetchFaviconForPage(readerURI, faviconURI, false,
       usePrivateBrowsing ? this._faviconService.FAVICON_LOAD_PRIVATE
                          : this._faviconService.FAVICON_LOAD_NON_PRIVATE,
       function (aURI, aDataLen, aData, aMimeType) {
         if (aDataLen > 0) {
           var dataURL = "data:" + aMimeType + ";base64," +
                         btoa(String.fromCharCode.apply(null, aData));
-          self._contentSandbox.menuItem = aMenuItem;
-          self._contentSandbox.dataURL = dataURL;
-          var codeStr = "menuItem.setAttribute('image', dataURL);";
-          Cu.evalInSandbox(codeStr, self._contentSandbox);
-          self._contentSandbox.menuItem = null;
-          self._contentSandbox.dataURL = null;
+          aMenuItem.setAttribute('image', dataURL);
         }
       });
   },
 
   classID: FEEDWRITER_CID,
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIDOMEventListener, Ci.nsIObserver,
                                          Ci.nsINavHistoryObserver,
                                          Ci.nsIDOMGlobalPropertyInitializer])