Merge m-c to s-c.
authorRichard Newman <rnewman@mozilla.com>
Mon, 10 Sep 2012 08:31:10 -0700
changeset 111070 d2373bfa7b2aca07aa54f48a23493283314cbfcf
parent 111069 39f2364d486010a5ecb605b6cae84b30b9cbb4c8 (current diff)
parent 106785 12dad118c02f3d20b142999462db065b539f6875 (diff)
child 111071 d4e5dcfca00dde4af38015224282d265970cc101
push id93
push usernmatsakis@mozilla.com
push dateWed, 31 Oct 2012 21:26:57 +0000
milestone18.0a1
Merge m-c to s-c.
--- a/browser/base/content/browser-sets.inc
+++ b/browser/base/content/browser-sets.inc
@@ -84,17 +84,17 @@
     <command id="cmd_fullZoomEnlarge" oncommand="FullZoom.enlarge()"/>
     <command id="cmd_fullZoomReset"   oncommand="FullZoom.reset()"/>
     <command id="cmd_fullZoomToggle"  oncommand="ZoomManager.toggleZoom();"/>
     <command id="Browser:OpenLocation" oncommand="openLocation();"/>
 
     <command id="Tools:Search" oncommand="BrowserSearch.webSearch();"/>
     <command id="Tools:Downloads" oncommand="BrowserDownloadsUI();"/>
     <command id="Tools:DevToolbar" oncommand="DeveloperToolbar.toggle();" disabled="true" hidden="true"/>
-    <command id="Tools:DevToolbarFocus" oncommand="DeveloperToolbar.focus();" disabled="true"/>
+    <command id="Tools:DevToolbarFocus" oncommand="DeveloperToolbar.focusToggle();" disabled="true"/>
     <command id="Tools:WebConsole" oncommand="HUDConsoleUI.toggleHUD();"/>
     <command id="Tools:Inspect" oncommand="InspectorUI.toggleInspectorUI();"/>
     <command id="Tools:Debugger" oncommand="DebuggerUI.toggleDebugger();" disabled="true" hidden="true"/>
     <command id="Tools:RemoteDebugger" oncommand="DebuggerUI.toggleRemoteDebugger();" disabled="true" hidden="true"/>
     <command id="Tools:ChromeDebugger" oncommand="DebuggerUI.toggleChromeDebugger();" disabled="true" hidden="true"/>
     <command id="Tools:Scratchpad" oncommand="Scratchpad.openScratchpad();" disabled="true" hidden="true"/>
     <command id="Tools:StyleEditor" oncommand="StyleEditor.toggle();" disabled="true" hidden="true"/>
     <command id="Tools:ResponsiveUI" oncommand="ResponsiveUI.toggle();" disabled="true" hidden="true"/>
--- a/browser/devtools/commandline/CmdScreenshot.jsm
+++ b/browser/devtools/commandline/CmdScreenshot.jsm
@@ -36,17 +36,17 @@ gcli.addCommand({
     },
     {
       name: "fullpage",
       type: "boolean",
       description: gcli.lookup("screenshotFullPageDesc"),
       manual: gcli.lookup("screenshotFullPageManual")
     },
     {
-      name: "node",
+      name: "selector",
       type: "node",
       defaultValue: null,
       description: gcli.lookup("inspectNodeDesc"),
       manual: gcli.lookup("inspectNodeManual")
     }
   ],
   exec: function Command_screenshot(args, context) {
     var document = context.environment.contentDocument;
@@ -54,17 +54,17 @@ gcli.addCommand({
       var promise = context.createPromise();
       document.defaultView.setTimeout(function Command_screenshotDelay() {
         let reply = this.grabScreen(document, args.filename);
         promise.resolve(reply);
       }.bind(this), args.delay * 1000);
       return promise;
     }
     else {
-      return this.grabScreen(document, args.filename, args.fullpage, args.node);
+      return this.grabScreen(document, args.filename, args.fullpage, args.selector);
     }
   },
   grabScreen:
   function Command_screenshotGrabScreen(document, filename, fullpage, node) {
     let window = document.defaultView;
     let canvas = document.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
     let left = 0;
     let top = 0;
--- a/browser/devtools/commandline/gcli.jsm
+++ b/browser/devtools/commandline/gcli.jsm
@@ -1702,17 +1702,17 @@ exports.ArrayArgument = ArrayArgument;
 define('gcli/types/selection', ['require', 'exports', 'module' , 'gcli/l10n', 'gcli/types', 'gcli/types/spell'], function(require, exports, module) {
 
 
 var l10n = require('gcli/l10n');
 var types = require('gcli/types');
 var Type = require('gcli/types').Type;
 var Status = require('gcli/types').Status;
 var Conversion = require('gcli/types').Conversion;
-var Speller = require('gcli/types/spell').Speller;
+var spell = require('gcli/types/spell');
 
 
 /**
  * Registration and de-registration.
  */
 exports.startup = function() {
   types.registerType(SelectionType);
 };
@@ -1883,23 +1883,24 @@ SelectionType.prototype._findPredictions
         if (predictions.indexOf(option) === -1) {
           this._addToPredictions(predictions, option, arg);
         }
       }
     }
   }
 
   // Try fuzzy matching if we don't get a prefix match
-  if (false && predictions.length === 0) {
-    var speller = new Speller();
-    var names = lookup.map(function(opt) {
-      return opt.name;
+  if (predictions.length === 0) {
+    var names = [];
+    lookup.forEach(function(opt) {
+      if (!opt.value.hidden) {
+        names.push(opt.name);
+      }
     });
-    speller.train(names);
-    var corrected = speller.correct(match);
+    var corrected = spell.correct(match, names);
     if (corrected) {
       lookup.forEach(function(opt) {
         if (opt.name === corrected) {
           predictions.push(opt);
         }
       }, this);
     }
   }
@@ -2000,163 +2001,126 @@ SelectionType.prototype._findValue = fun
 
 SelectionType.prototype.name = 'selection';
 
 exports.SelectionType = SelectionType;
 
 
 });
 /*
- * Copyright (c) 2009 Panagiotis Astithas
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
- * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
- * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
- * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ * Copyright 2012, Mozilla Foundation and contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
  */
 
 define('gcli/types/spell', ['require', 'exports', 'module' ], function(require, exports, module) {
 
-
-/**
- * A spell-checker based on the statistical algorithm described by Peter Norvig
- * in http://norvig.com/spell-correct.html, and converted to JavaScript by Past
- * http://past.github.com/speller/
- *
- * Usage requires a two-step process:
- * 1) call speller.train() one or more times with a large text to train the
- *    language model
- * 2) call speller.correct(word) to retrieve the correction for the specified
- *    word
- */
-function Speller() {
-  // A map of words to the count of times they were encountered during training.
-  this._nWords = {};
-}
-
-Speller.letters = "abcdefghijklmnopqrstuvwxyz".split("");
-
-/**
- * A function that trains the language model with the words in the supplied
- * text. Multiple invocation of this function can extend the training of the
- * model.
- */
-Speller.prototype.train = function(words) {
-  words.forEach(function(word) {
-    word = word.toLowerCase();
-    this._nWords[word] = this._nWords.hasOwnProperty(word) ?
-            this._nWords[word] + 1 :
-            1;
-  }, this);
+/*
+ * A spell-checker based on Damerau-Levenshtein distance.
+ */
+
+var INSERTION_COST = 1;
+var DELETION_COST = 1;
+var SWAP_COST = 1;
+var SUBSTITUTION_COST = 2;
+var MAX_EDIT_DISTANCE = 4;
+
+/**
+ * Compute Damerau-Levenshtein Distance
+ * @see http://en.wikipedia.org/wiki/Damerau%E2%80%93Levenshtein_distance
+ */
+function damerauLevenshteinDistance(wordi, wordj) {
+  var N = wordi.length;
+  var M = wordj.length;
+
+  // We only need to store three rows of our dynamic programming matrix.
+  // (Without swap, it would have been two.)
+  var row0 = new Array(N+1);
+  var row1 = new Array(N+1);
+  var row2 = new Array(N+1);
+  var tmp;
+
+  var i, j;
+
+  // The distance between the empty string and a string of size i is the cost
+  // of i insertions.
+  for (i = 0; i <= N; i++) {
+    row1[i] = i * INSERTION_COST;
+  }
+
+  // Row-by-row, we're computing the edit distance between substrings wordi[0..i]
+  // and wordj[0..j].
+  for (j = 1; j <= M; j++)
+  {
+    // Edit distance between wordi[0..0] and wordj[0..j] is the cost of j
+    // insertions.
+    row0[0] = j * INSERTION_COST;
+
+    for (i = 1; i <= N; i++)
+    {
+      // Handle deletion, insertion and substitution: we can reach each cell
+      // from three other cells corresponding to those three operations. We
+      // want the minimum cost.
+      row0[i] = Math.min(
+          row0[i-1] + DELETION_COST,
+          row1[i] + INSERTION_COST,
+          row1[i-1] + (wordi[i-1] === wordj[j-1] ? 0 : SUBSTITUTION_COST));
+      // We handle swap too, eg. distance between help and hlep should be 1. If
+      // we find such a swap, there's a chance to update row0[1] to be lower.
+      if (i > 1 && j > 1 && wordi[i-1] === wordj[j-2] && wordj[j-1] === wordi[i-2]) {
+        row0[i] = Math.min(row0[i], row2[i-2] + SWAP_COST);
+      }
+    }
+
+    tmp = row2;
+    row2 = row1;
+    row1 = row0;
+    row0 = tmp;
+  }
+
+  return row1[N];
 };
 
 /**
  * A function that returns the correction for the specified word.
  */
-Speller.prototype.correct = function(word) {
-  if (this._nWords.hasOwnProperty(word)) {
-    return word;
-  }
-
-  var candidates = {};
-  var list = this._edits(word);
-  list.forEach(function(edit) {
-    if (this._nWords.hasOwnProperty(edit)) {
-      candidates[this._nWords[edit]] = edit;
-    }
-  }, this);
-
-  if (this._countKeys(candidates) > 0) {
-    return candidates[this._max(candidates)];
-  }
-
-  list.forEach(function(edit) {
-    this._edits(edit).forEach(function(w) {
-      if (this._nWords.hasOwnProperty(w)) {
-        candidates[this._nWords[w]] = w;
-      }
-    }, this);
-  }, this);
-
-  return this._countKeys(candidates) > 0 ?
-      candidates[this._max(candidates)] :
-      null;
-};
-
-/**
- * A helper function that counts the keys in the supplied object.
- */
-Speller.prototype._countKeys = function(object) {
-  // return Object.keys(object).length; ?
-  var count = 0;
-  for (var attr in object) {
-    if (object.hasOwnProperty(attr)) {
-      count++;
-    }
-  }
-  return count;
-};
-
-/**
- * A helper function that returns the word with the most occurrences in the
- * language model, among the supplied candidates.
- * @param candidates
- */
-Speller.prototype._max = function(candidates) {
-  var arr = [];
-  for (var candidate in candidates) {
-    if (candidates.hasOwnProperty(candidate)) {
-      arr.push(candidate);
-    }
-  }
-  return Math.max.apply(null, arr);
-};
-
-/**
- * A function that returns the set of possible corrections of the specified
- * word. The edits can be deletions, insertions, alterations or transpositions.
- */
-Speller.prototype._edits = function(word) {
-  var results = [];
-
-  // Deletion
-  for (var i = 0; i < word.length; i++) {
-    results.push(word.slice(0, i) + word.slice(i + 1));
-  }
-
-  // Transposition
-  for (i = 0; i < word.length - 1; i++) {
-    results.push(word.slice(0, i) + word.slice(i + 1, i + 2)
-            + word.slice(i, i + 1) + word.slice(i + 2));
-  }
-
-  // Alteration
-  for (i = 0; i < word.length; i++) {
-    Speller.letters.forEach(function(l) {
-      results.push(word.slice(0, i) + l + word.slice(i + 1));
-    }, this);
-  }
-
-  // Insertion
-  for (i = 0; i <= word.length; i++) {
-    Speller.letters.forEach(function(l) {
-      results.push(word.slice(0, i) + l + word.slice(i));
-    }, this);
-  }
-
-  return results;
-};
-
-exports.Speller = Speller;
+exports.correct = function(word, names) {
+  var distance = {};
+  var sorted_candidates;
+
+  names.forEach(function(candidate) {
+    distance[candidate] = damerauLevenshteinDistance(word, candidate);
+  });
+
+  sorted_candidates = names.sort(function(worda, wordb) {
+    if (distance[worda] !== distance[wordb]) {
+      return distance[worda] - distance[wordb];
+    } else {
+      // if the score is the same, always return the first string
+      // in the lexicographical order
+      return worda < wordb;
+    }
+  });
+
+  if (distance[sorted_candidates[0]] <= MAX_EDIT_DISTANCE) {
+    return sorted_candidates[0];
+  } else {
+    return undefined;
+  }
+};
 
 
 });
 /*
  * Copyright 2012, Mozilla Foundation and contributors
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -4063,17 +4027,19 @@ if (typeof document !== 'undefined') {
  */
 exports._empty = [];
 
 /**
  * Setter for the document that contains the nodes we're matching
  */
 exports.setDocument = function(document) {
   doc = document;
-  exports._empty = doc.querySelectorAll('x>:root');
+  if (doc != null) {
+    exports._empty = doc.querySelectorAll('x>:root');
+  }
 };
 
 /**
  * Undo the effects of setDocument()
  */
 exports.unsetDocument = function() {
   doc = undefined;
 };
@@ -5122,17 +5088,17 @@ define('gcli/ui/domtemplate', ['require'
 define('gcli/cli', ['require', 'exports', 'module' , 'gcli/util', 'gcli/ui/view', 'gcli/l10n', 'gcli/canon', 'gcli/promise', 'gcli/types', 'gcli/types/basic', 'gcli/argument'], function(require, exports, module) {
 
 
 var util = require('gcli/util');
 var view = require('gcli/ui/view');
 var l10n = require('gcli/l10n');
 
 var canon = require('gcli/canon');
-var Promise = require('gcli/promise').Promise;
+var Q = require('gcli/promise');
 
 var Status = require('gcli/types').Status;
 var Conversion = require('gcli/types').Conversion;
 var ArrayType = require('gcli/types/basic').ArrayType;
 var StringType = require('gcli/types/basic').StringType;
 var BooleanType = require('gcli/types/basic').BooleanType;
 var NumberType = require('gcli/types/basic').NumberType;
 
@@ -5185,18 +5151,16 @@ function Assignment(param, paramIndex) {
   this.conversion = undefined;
 
   // The index of this parameter in the parent Requisition. paramIndex === -1
   // is the command assignment although this should not be relied upon, it is
   // better to test param instanceof CommandAssignment
   this.paramIndex = paramIndex;
 
   this.onAssignmentChange = util.createEvent('Assignment.onAssignmentChange');
-
-  this.setBlank();
 }
 
 /**
  * Easy accessor for conversion.arg.
  * This is a read-only property because writes to arg should be done through
  * the 'conversion' property.
  */
 Object.defineProperty(Assignment.prototype, 'arg', {
@@ -5271,49 +5235,16 @@ Assignment.prototype.getPredictionAt = f
  * to take this into account.
  */
 Assignment.prototype.isInName = function() {
   return this.conversion.arg.type === 'NamedArgument' &&
          this.conversion.arg.prefix.slice(-1) !== ' ';
 };
 
 /**
- * Report on the status of the last parse() conversion.
- * We force mutations to happen through this method rather than have
- * setValue and setArgument functions to help maintain integrity when we
- * have ArrayArguments and don't want to get confused. This way assignments
- * are just containers for a conversion rather than things that store
- * a connection between an arg/value.
- * @see types.Conversion
- */
-Assignment.prototype.setConversion = function(conversion) {
-  var oldConversion = this.conversion;
-
-  this.conversion = conversion;
-  this.conversion.assign(this);
-
-  if (this.conversion.equals(oldConversion)) {
-    return;
-  }
-
-  this.onAssignmentChange({
-    assignment: this,
-    conversion: this.conversion,
-    oldConversion: oldConversion
-  });
-};
-
-/**
- * Setup an empty value for the conversion by parsing an empty argument.
- */
-Assignment.prototype.setBlank = function() {
-  this.setConversion(this.param.type.getBlank());
-};
-
-/**
  * Make sure that there is some content for this argument by using an
  * Argument of '' if needed.
  */
 Assignment.prototype.ensureVisibleArgument = function() {
   // It isn't clear if we should be sending events from this method.
   // It should only be called when structural changes are happening in which
   // case we're going to ignore the event anyway. But on the other hand
   // perhaps this function shouldn't need to know how it is used, and should
@@ -5449,18 +5380,16 @@ function CommandAssignment() {
           value.description :
           'The command to execute';
     },
     enumerable: true
   });
   this.param = new canon.Parameter(commandParamMetadata);
   this.paramIndex = -1;
   this.onAssignmentChange = util.createEvent('CommandAssignment.onAssignmentChange');
-
-  this.setBlank();
 }
 
 CommandAssignment.prototype = Object.create(Assignment.prototype);
 
 CommandAssignment.prototype.getStatus = function(arg) {
   return Status.combine(
     Assignment.prototype.getStatus.call(this, arg),
     this.conversion.value && this.conversion.value.exec ?
@@ -5535,16 +5464,17 @@ function Requisition(environment, doc) {
     catch (ex) {
       // Ignore
     }
   }
 
   // The command that we are about to execute.
   // @see setCommandConversion()
   this.commandAssignment = new CommandAssignment();
+  this._setAssignment(this.commandAssignment, null, true);
 
   // The object that stores of Assignment objects that we are filling out.
   // The Assignment objects are stored under their param.name for named
   // lookup. Note: We make use of the property of Javascript objects that
   // they are not just hashmaps, but linked-list hashmaps which iterate in
   // insertion order.
   // _assignments excludes the commandAssignment.
   this._assignments = {};
@@ -5621,16 +5551,17 @@ Requisition.prototype._commandAssignment
 
   this._assignments = {};
 
   var command = this.commandAssignment.value;
   if (command) {
     for (var i = 0; i < command.params.length; i++) {
       var param = command.params[i];
       var assignment = new Assignment(param, i);
+      this._setAssignment(assignment, null, true);
       assignment.onAssignmentChange.add(this._assignmentChanged, this);
       this._assignments[param.name] = assignment;
     }
   }
   this.assignmentCount = Object.keys(this._assignments).length;
 };
 
 /**
@@ -5743,58 +5674,99 @@ Requisition.prototype.getAssignments = f
   }
   Object.keys(this._assignments).forEach(function(name) {
     assignments.push(this.getAssignment(name));
   }, this);
   return assignments;
 };
 
 /**
- * Alter the given assignment using the given arg. This function is better than
- * calling assignment.setConversion(assignment.param.type.parse(arg)) because
- * it adjusts the args in this requisition to keep things up to date
+ * Alter the given assignment using the given arg.
+ * @param assignment The assignment to alter
+ * @param arg The new value for the assignment. An instance of Argument, or an
+ * instance of Conversion, or null to set the blank value.
  */
 Requisition.prototype.setAssignment = function(assignment, arg) {
-  var originalArgs = assignment.arg.getArgs();
-  var conversion = assignment.param.type.parse(arg);
-  assignment.setConversion(conversion);
-
-  var replacementArgs = arg.getArgs();
-  var maxLen = Math.max(originalArgs.length, replacementArgs.length);
-  for (var i = 0; i < maxLen; i++) {
-    // If there are no more original args, or if the original arg was blank
-    // (i.e. not typed by the user), we'll just need to add at the end
-    if (i >= originalArgs.length || originalArgs[i].type === 'BlankArgument') {
-      this._args.push(replacementArgs[i]);
-      continue;
-    }
-
-    var index = this._args.indexOf(originalArgs[i]);
-    if (index === -1) {
-      console.error('Couldn\'t find ', originalArgs[i], ' in ', this._args);
-      throw new Error('Couldn\'t find ' + originalArgs[i]);
-    }
-
-    // If there are no more replacement args, we just remove the original args
-    // Otherwise swap original args and replacements
-    if (i >= replacementArgs.length) {
-      this._args.splice(index, 1);
-    }
-    else {
-      this._args[index] = replacementArgs[i];
-    }
-  }
+  this._setAssignment(assignment, arg, false);
+};
+
+/**
+ * Internal function to alter the given assignment using the given arg.
+ * @param assignment The assignment to alter
+ * @param arg The new value for the assignment. An instance of Argument, or an
+ * instance of Conversion, or null to set the blank value.
+ * @param skipArgUpdate (default=false) Adjusts the args in this requisition to
+ * keep things up to date. Args should only be skipped when setAssignment is
+ * being called as part of the update process.
+ */
+Requisition.prototype._setAssignment = function(assignment, arg, skipArgUpdate) {
+  if (!skipArgUpdate) {
+    var originalArgs = assignment.arg.getArgs();
+
+    // Update the args array
+    var replacementArgs = arg.getArgs();
+    var maxLen = Math.max(originalArgs.length, replacementArgs.length);
+    for (var i = 0; i < maxLen; i++) {
+      // If there are no more original args, or if the original arg was blank
+      // (i.e. not typed by the user), we'll just need to add at the end
+      if (i >= originalArgs.length || originalArgs[i].type === 'BlankArgument') {
+        this._args.push(replacementArgs[i]);
+        continue;
+      }
+
+      var index = this._args.indexOf(originalArgs[i]);
+      if (index === -1) {
+        console.error('Couldn\'t find ', originalArgs[i], ' in ', this._args);
+        throw new Error('Couldn\'t find ' + originalArgs[i]);
+      }
+
+      // If there are no more replacement args, we just remove the original args
+      // Otherwise swap original args and replacements
+      if (i >= replacementArgs.length) {
+        this._args.splice(index, 1);
+      }
+      else {
+        this._args[index] = replacementArgs[i];
+      }
+    }
+  }
+
+  var conversion;
+  if (arg == null) {
+    conversion = assignment.param.type.getBlank();
+  }
+  else if (typeof arg.getStatus === 'function') {
+    conversion = arg;
+  }
+  else {
+    conversion = assignment.param.type.parse(arg);
+  }
+
+  var oldConversion = assignment.conversion;
+
+  assignment.conversion = conversion;
+  assignment.conversion.assign(assignment);
+
+  if (assignment.conversion.equals(oldConversion)) {
+    return;
+  }
+
+  assignment.onAssignmentChange({
+    assignment: assignment,
+    conversion: assignment.conversion,
+    oldConversion: oldConversion
+  });
 };
 
 /**
  * Reset all the assignments to their default values
  */
 Requisition.prototype.setBlankArguments = function() {
   this.getAssignments().forEach(function(assignment) {
-    assignment.setBlank();
+    this._setAssignment(assignment, null, true);
   }, this);
 };
 
 /**
  * Complete the argument at <tt>cursor</tt>.
  * Basically the same as:
  *   assignment = getAssignmentAt(cursor);
  *   assignment.value = assignment.conversion.predictions[0];
@@ -6143,18 +6115,18 @@ Requisition.prototype.getAssignmentAt = 
  *    argument values as passed to command.exec. This method is significantly
  *    faster, and designed for use from keyboard shortcuts.
  * In addition to these properties, the input parameter can contain a 'hidden'
  * property which can be set to true to hide the output from the
  * CommandOutputManager.
  * @param input (optional) The command to execute. See above.
  */
 Requisition.prototype.exec = function(input) {
-  var command;
-  var args;
+  var command = null;
+  var args = null;
   var hidden = false;
   if (input && input.hidden) {
     hidden = true;
   }
 
   if (input) {
     if (typeof input === 'string') {
       this.update(input);
@@ -6195,44 +6167,69 @@ Requisition.prototype.exec = function(in
     args: args,
     typed: typed,
     canonical: this.toCanonicalString(),
     hidden: hidden
   });
 
   this.commandOutputManager.onOutput({ output: output });
 
+  var onDone = function(data) {
+    output.complete(data);
+  };
+
+  var onError = function(error) {
+    console.error(error);
+    output.error = true;
+    output.complete(error);
+  };
+
   try {
     var context = exports.createExecutionContext(this);
     var reply = command.exec(args, context);
 
-    if (reply != null && typeof reply.then === 'function') {
-      reply.then(
-          function(data) { output.complete(data); },
-          function(error) { output.error = true; output.complete(error); });
-
-      output.promise = reply;
-      // Add progress to our promise and add a handler for it here
-      // See bug 659300
-    }
-    else {
-      output.complete(reply);
-    }
+    this._then(reply, onDone, onError);
   }
   catch (ex) {
-    console.error(ex);
-    output.error = true;
-    output.complete(ex);
+    onError(ex);
   }
 
   this.update('');
   return output;
 };
 
 /**
+ * Different types of promise have different ways of doing 'then'. This is a
+ * catch-all so we can ignore the differences. It also handles concrete values
+ * and calls onDone directly if thing is not a promise.
+ * @param thing The value to test for 'promiseness'
+ * @param onDone The action to take if thing is resolved
+ * @param onError The action to take if thing is rejected
+ */
+Requisition.prototype._then = function(thing, onDone, onError) {
+  var then = null;
+  if (thing != null && typeof thing.then === 'function') {
+    // Old GCLI style / simple promises with a then function
+    then = thing.then;
+  }
+  else if (thing != null && thing.promise != null &&
+                typeof thing.promise.then === 'function') {
+    // Q / Mozilla add-ons style
+    then = thing.promise.then;
+  }
+
+  if (then != null) {
+    then(onDone, onError);
+  }
+  else {
+    onDone(thing);
+  }
+};
+
+/**
  * Called by the UI when ever the user interacts with a command line input
  * @param typed The contents of the input field
  */
 Requisition.prototype.update = function(typed) {
   this._structuralChangeInProgress = true;
 
   this._args = this._tokenize(typed);
   var args = this._args.slice(0); // i.e. clone
@@ -6512,26 +6509,26 @@ Requisition.prototype._split = function(
   // Handle the special case of the user typing { javascript(); }
   // We use the hidden 'eval' command directly rather than shift()ing one of
   // the parameters, and parse()ing it.
   var conversion;
   if (args[0].type === 'ScriptArgument') {
     // Special case: if the user enters { console.log('foo'); } then we need to
     // use the hidden 'eval' command
     conversion = new Conversion(evalCommand, new ScriptArgument());
-    this.commandAssignment.setConversion(conversion);
+    this._setAssignment(this.commandAssignment, conversion, true);
     return;
   }
 
   var argsUsed = 1;
 
   while (argsUsed <= args.length) {
     var arg = (argsUsed === 1) ?
-      args[0] :
-      new MergedArgument(args, 0, argsUsed);
+              args[0] :
+              new MergedArgument(args, 0, argsUsed);
     conversion = this.commandAssignment.param.type.parse(arg);
 
     // We only want to carry on if this command is a parent command,
     // which means that there is a commandAssignment, but not one with
     // an exec function.
     if (!conversion.value || conversion.value.exec) {
       break;
     }
@@ -6539,17 +6536,17 @@ Requisition.prototype._split = function(
     // Previously we needed a way to hide commands depending context.
     // We have not resurrected that feature yet, but if we do we should
     // insert code here to ignore certain commands depending on the
     // context/environment
 
     argsUsed++;
   }
 
-  this.commandAssignment.setConversion(conversion);
+  this._setAssignment(this.commandAssignment, conversion, true);
 
   for (var i = 0; i < argsUsed; i++) {
     args.shift();
   }
 
   // This could probably be re-written to consume args as we go
 };
 
@@ -6585,21 +6582,18 @@ Requisition.prototype._assign = function
     return;
   }
 
   // Special case: if there is only 1 parameter, and that's of type
   // text, then we put all the params into the first param
   if (this.assignmentCount === 1) {
     var assignment = this.getAssignment(0);
     if (assignment.param.type instanceof StringType) {
-      var arg = (args.length === 1) ?
-        args[0] :
-        new MergedArgument(args);
-      var conversion = assignment.param.type.parse(arg);
-      assignment.setConversion(conversion);
+      var arg = (args.length === 1) ? args[0] : new MergedArgument(args);
+      this._setAssignment(assignment, arg, true);
       return;
     }
   }
 
   // Positional arguments can still be specified by name, but if they are
   // then we need to ignore them when working them out positionally
   var unassignedParams = this.getParameterNames();
 
@@ -6634,77 +6628,74 @@ Requisition.prototype._assign = function
           var arrayArg = arrayArgs[assignment.param.name];
           if (!arrayArg) {
             arrayArg = new ArrayArgument();
             arrayArgs[assignment.param.name] = arrayArg;
           }
           arrayArg.addArgument(arg);
         }
         else {
-          var conversion = assignment.param.type.parse(arg);
-          assignment.setConversion(conversion);
+          this._setAssignment(assignment, arg, true);
         }
       }
       else {
         // Skip this parameter and handle as a positional parameter
         i++;
       }
     }
   }, this);
 
   // What's left are positional parameters assign in order
   unassignedParams.forEach(function(name) {
     var assignment = this.getAssignment(name);
 
     // If not set positionally, and we can't set it non-positionally,
     // we have to default it to prevent previous values surviving
     if (!assignment.param.isPositionalAllowed) {
-      assignment.setBlank();
+      this._setAssignment(assignment, null, true);
       return;
     }
 
     // If this is a positional array argument, then it swallows the
     // rest of the arguments.
     if (assignment.param.type instanceof ArrayType) {
       var arrayArg = arrayArgs[assignment.param.name];
       if (!arrayArg) {
         arrayArg = new ArrayArgument();
         arrayArgs[assignment.param.name] = arrayArg;
       }
       arrayArg.addArguments(args);
       args = [];
     }
     else {
       if (args.length === 0) {
-        assignment.setBlank();
+        this._setAssignment(assignment, null, true);
       }
       else {
         var arg = args.splice(0, 1)[0];
         // --foo and -f are named parameters, -4 is a number. So '-' is either
         // the start of a named parameter or a number depending on the context
         var isIncompleteName = assignment.param.type instanceof NumberType ?
             /-[-a-zA-Z_]/.test(arg.text) :
             arg.text.charAt(0) === '-';
 
         if (isIncompleteName) {
           this._unassigned.push(new UnassignedAssignment(this, arg));
         }
         else {
-          var conversion = assignment.param.type.parse(arg);
-          assignment.setConversion(conversion);
+          this._setAssignment(assignment, arg, true);
         }
       }
     }
   }, this);
 
   // Now we need to assign the array argument (if any)
   Object.keys(arrayArgs).forEach(function(name) {
     var assignment = this.getAssignment(name);
-    var conversion = assignment.param.type.parse(arrayArgs[name]);
-    assignment.setConversion(conversion);
+    this._setAssignment(assignment, arrayArgs[name], true);
   }, this);
 
   // What's left is can't be assigned, but we need to extract
   this._addUnassignedArgs(args);
 };
 
 exports.Requisition = Requisition;
 
@@ -6724,27 +6715,41 @@ function Output(options) {
   this.error = false;
   this.start = new Date();
 
   this.onClose = util.createEvent('Output.onClose');
   this.onChange = util.createEvent('Output.onChange');
 }
 
 /**
- * Called when there is data to display
- * @param data
- */
-Output.prototype.complete = function(data) {
+ * Called when there is data to display, but the command is still executing
+ * @param data The new data. If the data structure has been altered but the
+ * root object is still the same, The same root object should be passed in the
+ * data parameter.
+ * @param ev Optional additional event data, for example to explain how the
+ * data structure has changed
+ */
+Output.prototype.changed = function(data, ev) {
   this.data = data;
 
+  ev = ev || {};
+  ev.output = this;
+  this.onChange(ev);
+};
+
+/**
+ * Called when there is data to display, and the command has finished executing
+ * See changed() for details on parameters.
+ */
+Output.prototype.complete = function(data, ev) {
   this.end = new Date();
   this.duration = this.end.getTime() - this.start.getTime();
   this.completed = true;
 
-  this.onChange({ output: this });
+  this.changed(data, ev);
 };
 
 /**
  * Convert to a DOM element for display.
  * @param element The DOM node to which the data should be written. Existing
  * content of 'element' will be removed before 'outputData' is added.
  */
 Output.prototype.toDom = function(element) {
@@ -6825,18 +6830,25 @@ exports.Output = Output;
  */
 exports.createExecutionContext = function(requisition) {
   return {
     exec: requisition.exec.bind(requisition),
     update: requisition.update.bind(requisition),
     document: requisition.document,
     environment: requisition.environment,
     createView: view.createView,
+    defer: function() {
+      return Q.defer();
+    },
+    /**
+     * @deprecated Use defer() instead, which does the same thing, but is not
+     * confusingly named
+     */
     createPromise: function() {
-      return new Promise();
+      return Q.defer();
     }
   };
 };
 
 
 });
 /*
  * Copyright 2012, Mozilla Foundation and contributors
@@ -6851,18 +6863,23 @@ exports.createExecutionContext = functio
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 define('gcli/promise', ['require', 'exports', 'module' ], function(require, exports, module) {
 
-  Components.utils.import("resource:///modules/devtools/Promise.jsm");
-  exports.Promise = Promise;
+  var imported = {};
+  Components.utils.import("resource://gre/modules/commonjs/promise/core.js",
+                          imported);
+
+  exports.defer = imported.Promise.defer;
+  exports.resolve = imported.Promise.resolve;
+  exports.reject = imported.Promise.reject;
 
 });
 define("text!gcli/ui/intro.html", [], "\n" +
   "<div>\n" +
   "  <p>${l10n.introTextOpening}</p>\n" +
   "\n" +
   "  <p>\n" +
   "    ${l10n.introTextCommands}\n" +
@@ -7232,17 +7249,17 @@ FocusManager.prototype._checkShow = func
   var showOutput = this._shouldShowOutput();
   if (this.isOutputVisible !== showOutput.visible) {
     ev.outputVisible = this.isOutputVisible = showOutput.visible;
     fire = true;
   }
 
   if (fire) {
     if (this._debug) {
-      console.debug('FocusManager.onVisibilityChange', ev);
+      console.log('FocusManager.onVisibilityChange', ev);
     }
     this.onVisibilityChange(ev);
   }
 };
 
 /**
  * Calculate if we should be showing or hidden taking into account all the
  * available inputs
@@ -7730,16 +7747,19 @@ Field.prototype.element = undefined;
 
 /**
  * Indicates that this field should drop any resources that it has created
  */
 Field.prototype.destroy = function() {
   delete this.messageElement;
 };
 
+// Note: We could/should probably change Fields from working with Conversions
+// to working with Arguments (Tokens), which makes for less calls to parse()
+
 /**
  * Update this field display with the value from this conversion.
  * Subclasses should provide an implementation of this function.
  */
 Field.prototype.setConversion = function(conversion) {
   throw new Error('Field should not be used directly');
 };
 
@@ -9388,17 +9408,19 @@ Inputter.prototype.textChanged = functio
   }
 
   var newStr = this.requisition.toString();
   var input = this.getInputState();
 
   input.typed = newStr;
   this._processCaretChange(input);
 
-  this.element.value = newStr;
+  if (this.element.value !== newStr) {
+    this.element.value = newStr;
+  }
   this.onInputChange({ inputState: input });
 };
 
 /**
  * Various ways in which we need to manipulate the caret/selection position.
  * A value of null means we're not expecting a change
  */
 var Caret = {
@@ -9464,18 +9486,22 @@ Inputter.prototype._processCaretChange =
   start = (start > input.typed.length) ? input.typed.length : start;
   end = (end > input.typed.length) ? input.typed.length : end;
 
   var newInput = {
     typed: input.typed,
     cursor: { start: start, end: end }
   };
 
-  this.element.selectionStart = start;
-  this.element.selectionEnd = end;
+  if (this.element.selectionStart !== start) {
+    this.element.selectionStart = start;
+  }
+  if (this.element.selectionEnd !== end) {
+    this.element.selectionEnd = end;
+  }
 
   this._checkAssignment(start);
 
   this._caretChange = null;
   return newInput;
 };
 
 /**
@@ -9600,17 +9626,17 @@ Inputter.prototype.onKeyUp = function(ev
     else if (this.element.value === '' || this._scrollingThroughHistory) {
       this._scrollingThroughHistory = true;
       this.requisition.update(this.history.backward());
     }
     else {
       // If the user is on a valid value, then we increment the value, but if
       // they've typed something that's not right we page through predictions
       if (this.assignment.getStatus() === Status.VALID) {
-        this.requisition.increment(assignment);
+        this.requisition.increment(this.assignment);
         // See notes on focusManager.onInputChange in onKeyDown
         if (this.focusManager) {
           this.focusManager.onInputChange();
         }
       }
       else {
         this.changeChoice(-1);
       }
@@ -9624,17 +9650,17 @@ Inputter.prototype.onKeyUp = function(ev
     }
     else if (this.element.value === '' || this._scrollingThroughHistory) {
       this._scrollingThroughHistory = true;
       this.requisition.update(this.history.forward());
     }
     else {
       // See notes above for the UP key
       if (this.assignment.getStatus() === Status.VALID) {
-        this.requisition.decrement(assignment);
+        this.requisition.decrement(this.assignment);
         // See notes on focusManager.onInputChange in onKeyDown
         if (this.focusManager) {
           this.focusManager.onInputChange();
         }
       }
       else {
         this.changeChoice(+1);
       }
@@ -10309,17 +10335,17 @@ Tooltip.prototype.selectChoice = functio
   }
   return false;
 };
 
 /**
  * Called by the onFieldChange event on the current Field
  */
 Tooltip.prototype.fieldChanged = function(ev) {
-  this.assignment.setConversion(ev.conversion);
+  this.requisition.setAssignment(this.assignment, ev.conversion.arg);
 
   var isError = ev.conversion.message != null && ev.conversion.message !== '';
   this.focusManager.setError(isError);
 
   // Nasty hack, the inputter won't know about the text change yet, so it will
   // get it's calculations wrong. We need to wait until the current set of
   // changes has had a chance to propagate
   this.document.defaultView.setTimeout(function() {
--- a/browser/devtools/commandline/test/browser_cmd_pref.js
+++ b/browser/devtools/commandline/test/browser_cmd_pref.js
@@ -158,18 +158,18 @@ function testPrefStatus() {
       setting: { arg: ' devtools.editor.tabsize' },
       value: { value: 4 },
     }
   });
 
   helpers.setInput('pref list');
   helpers.check({
     input:  'pref list',
-    hints:           '',
-    markup: 'EEEEVEEEE',
+    hints:           ' -> pref set',
+    markup: 'IIIIVIIII',
     status: 'ERROR'
   });
 }
 
 function testPrefSetEnable() {
   DeveloperToolbarTest.exec({
     typed: "pref set devtools.editor.tabsize 9",
     args: {
--- a/browser/devtools/commandline/test/browser_gcli_web.js
+++ b/browser/devtools/commandline/test/browser_gcli_web.js
@@ -103,33 +103,33 @@ define('gclitest/index', ['require', 'ex
   };
 
   /**
    * A simple proxy to examiner.run, for convenience - this is run from the
    * top level.
    * @param options Lookup of options that customize test running. Includes:
    * - window (default=undefined) A reference to the DOM window. If left
    *   undefined then a reduced set of tests will run.
-   * - isNode (default=false) Are we running under NodeJS, specifically, are we
-   *   using JSDom, which isn't a 100% complete DOM implementation.
+   * - isJsdom (default=false) Are we running under JSDom, specifically, which
+   *   isn't a 100% complete DOM implementation.
    *   Some tests are skipped when using NodeJS.
    * - display (default=undefined) A reference to a Display implementation.
    *   A reduced set of tests will run if left undefined
    * - detailedResultLog (default=false) do we output a test summary to
    *   |console.log| on test completion.
    * - hideExec (default=false) Set the |hidden| property in calls to
    *   |requisition.exec()| which prevents the display from becoming messed up,
    *   however use of hideExec restricts the set of tests that are run
    */
-  exports.run = function(options) {
+  exports.runAsync = function(options, callback) {
     options = options || {};
     examiner.mergeDefaultOptions(options);
 
     examiner.reset();
-    examiner.run(options);
+    examiner.runAsync(options, callback);
 
     // A better set of default than those specified above, come from the set
     // that are passed to run().
     examiner.defaultOptions = {
       window: options.window,
       display: options.display,
       hideExec: options.hideExec
     };
@@ -150,23 +150,16 @@ define('gclitest/index', ['require', 'ex
       settings.setDefaults(options.settings);
     }
 
     window.display = new Display(options);
     var requisition = window.display.requisition;
 
     // setTimeout keeps stack traces clear of RequireJS frames
     window.setTimeout(function() {
-      var options = {
-        window: window,
-        display: window.display,
-        hideExec: true
-      };
-      exports.run(options);
-
       window.createDebugCheck = function() {
         require([ 'gclitest/helpers' ], function(helpers) {
           helpers.setup(options);
           console.log(helpers._createDebugCheck());
           helpers.shutdown(options);
         });
       };
 
@@ -193,17 +186,25 @@ define('gclitest/index', ['require', 'ex
         }
       }, true);
 
       window.testCommands = function() {
         require([ 'gclitest/mockCommands' ], function(mockCommands) {
           mockCommands.setup();
         });
       };
-      window.testCommands();
+
+      var options = {
+        window: window,
+        display: window.display,
+        hideExec: true
+      };
+      exports.runAsync(options, function() {
+        window.testCommands();
+      });
     }, 10);
 
     return {
       /**
        * The exact shape of the object returned by exec is likely to change in
        * the near future. If you do use it, please expect your code to break.
        */
       exec: requisition.exec.bind(requisition),
@@ -322,35 +323,16 @@ examiner.mergeDefaultOptions = function(
   Object.keys(examiner.defaultOptions).forEach(function(name) {
     if (options[name] == null) {
       options[name] = examiner.defaultOptions[name];
     }
   });
 };
 
 /**
- * Run the tests defined in the test suite synchronously
- */
-examiner.run = function(options) {
-  Object.keys(examiner.suites).forEach(function(suiteName) {
-    var suite = examiner.suites[suiteName];
-    suite.run(options);
-  }.bind(this));
-
-  if (options.detailedResultLog) {
-    examiner.detailedResultLog();
-  }
-  else {
-    console.log('Completed test suite');
-  }
-
-  return examiner.suites;
-};
-
-/**
  * Run all the tests asynchronously
  */
 examiner.runAsync = function(options, callback) {
   this._runAsyncInternal(0, options, callback);
 };
 
 /**
  * Run all the test suits asynchronously
@@ -473,31 +455,16 @@ function Suite(suiteName, suite) {
  */
 Suite.prototype.reset = function() {
   Object.keys(this.tests).forEach(function(testName) {
     this.tests[testName].reset();
   }, this);
 };
 
 /**
- * Run all the tests in this suite synchronously
- */
-Suite.prototype.run = function(options) {
-  if (!this._setup(options)) {
-    return;
-  }
-
-  Object.keys(this.tests).forEach(function(testName) {
-    this.tests[testName].run(options);
-  }, this);
-
-  this._shutdown(options);
-};
-
-/**
  * Run all the tests in this suite asynchronously
  */
 Suite.prototype.runAsync = function(options, callback) {
   if (!this._setup(options)) {
     if (typeof callback === 'function') {
       callback();
     }
     return;
@@ -631,35 +598,42 @@ Suite.prototype._logToAllTests = functio
  * A test represents data about a single test function
  */
 function Test(suite, name, func) {
   this.suite = suite;
   this.name = name;
   this.func = func;
   this.title = name.replace(/^test/, '').replace(/([A-Z])/g, ' $1');
 
+  this.outstanding = [];
+  this.callback = undefined;
+
   this.failures = [];
   this.status = stati.notrun;
   this.checks = 0;
 }
 
 /**
  * Reset the test to its original state
  */
 Test.prototype.reset = function() {
+  this.outstanding = [];
+  this.callback = undefined;
+
   this.failures = [];
   this.status = stati.notrun;
   this.checks = 0;
 };
 
 /**
- * Run just a single test
+ * Run all the tests in this suite asynchronously
  */
-Test.prototype.run = function(options) {
+Test.prototype.runAsync = function(options, callback) {
   assert.currentTest = this;
+  this.callback = callback;
   this.status = stati.executing;
   this.failures = [];
   this.checks = 0;
 
   try {
     this.func.apply(this.suite, [ options ]);
   }
   catch (ex) {
@@ -670,28 +644,30 @@ Test.prototype.run = function(options) {
     }
   }
 
   if (this.status === stati.executing) {
     this.status = stati.pass;
   }
 
   assert.currentTest = null;
+
+  this.checkFinish();
 };
 
 /**
- * Run all the tests in this suite asynchronously
+ * Check to see if the currently executing test is completed (i.e. the list of
+ * outstanding tasks has all been completed)
  */
-Test.prototype.runAsync = function(options, callback) {
-  setTimeout(function() {
-    this.run(options);
-    if (typeof callback === 'function') {
-      callback();
+Test.prototype.checkFinish = function() {
+  if (this.outstanding.length == 0) {
+    if (typeof this.callback === 'function') {
+      this.callback();
     }
-  }.bind(this), delay);
+  }
 };
 
 /**
  * Create a JSON object suitable for serialization
  */
 Test.prototype.toRemote = function() {
   return {
     name: this.name,
@@ -887,31 +863,35 @@ define('gclitest/testCanon', ['require',
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
-define('gclitest/helpers', ['require', 'exports', 'module' , 'test/assert', 'gcli/util'], function(require, exports, module) {
+define('gclitest/helpers', ['require', 'exports', 'module' , 'test/assert'], function(require, exports, module) {
 
 
 var test = require('test/assert');
-var util = require('gcli/util');
-
+// A copy of this code exists in firefox mochitests; when updated here it
+// should be updated there too. Hence the use of an exports synonym for non
+// AMD contexts.
 var helpers = exports;
 
 helpers._display = undefined;
+helpers._options = undefined;
 
 helpers.setup = function(options) {
+  helpers._options = options;
   helpers._display = options.display;
 };
 
 helpers.shutdown = function(options) {
+  helpers._options = undefined;
   helpers._display = undefined;
 };
 
 /**
  * Various functions to return the actual state of the command line
  */
 helpers._actual = {
   input: function() {
@@ -1020,16 +1000,26 @@ helpers._createDebugCheck = function() {
  * and check() which ensures that things are in the right place afterwards.
  */
 helpers.setInput = function(typed, cursor) {
   helpers._display.inputter.setInput(typed);
 
   if (cursor) {
     helpers._display.inputter.setCursor({ start: cursor, end: cursor });
   }
+  else {
+    // This is a hack because jsdom appears to not handle cursor updates
+    // in the same way as most browsers.
+    if (helpers._options.isJsdom) {
+      helpers._display.inputter.setCursor({
+        start: typed.length,
+        end: typed.length
+      });
+    }
+  }
 
   helpers._display.focusManager.onInputChange();
 };
 
 /**
  * Simulate focusing the input field
  */
 helpers.focusInput = function() {
@@ -1584,17 +1574,17 @@ exports.testSingleNumber = function() {
 exports.testElement = function(options) {
   update({ typed: 'tse', cursor: { start: 3, end: 3 } });
   test.is(        'VVV', statuses);
   test.is(Status.ERROR, status);
   test.is('tse', requ.commandAssignment.value.name);
   test.ok(assign1.arg.type === 'BlankArgument');
   test.is(undefined, assign1.value);
 
-  if (!options.isNode) {
+  if (!options.isJsdom) {
     update({ typed: 'tse :root', cursor: { start: 9, end: 9 } });
     test.is(        'VVVVVVVVV', statuses);
     test.is(Status.VALID, status);
     test.is('tse', requ.commandAssignment.value.name);
     test.is(':root', assign1.arg.text);
     if (!options.window.isFake) {
       test.is(options.window.document.documentElement, assign1.value);
     }
@@ -1620,17 +1610,17 @@ exports.testElement = function(options) 
     // digging into the CSS engine we can't tell that so we default to incomplete
     test.is(        'VVVVIIIIIIIIIIIII', statuses);
     test.is(Status.ERROR, status);
     test.is('tse', requ.commandAssignment.value.name);
     test.is('#gcli-nomatch', assign1.arg.text);
     test.is(undefined, assign1.value);
   }
   else {
-    test.log('Skipping :root test due to jsdom (from isNode)');
+    test.log('Skipping :root test due to jsdom');
   }
 
   update({ typed: 'tse #', cursor: { start: 5, end: 5 } });
   test.is(        'VVVVE', statuses);
   test.is(Status.ERROR, status);
   test.is('tse', requ.commandAssignment.value.name);
   test.is('#', assign1.arg.text);
   test.is(undefined, assign1.value);
@@ -2355,17 +2345,17 @@ exports.testActivate = function(options)
 
   helpers.setInput('tsg b');
   helpers.check({
     hints: 'bb [options]'
   });
 
   helpers.setInput('tsg d');
   helpers.check({
-    hints: ' [options]'
+    hints: ' [options] -> ccc'
   });
 
   helpers.setInput('tsg aa');
   helpers.check({
     hints: 'a [options]'
   });
 
   helpers.setInput('tsg aaa');
@@ -2769,33 +2759,39 @@ var mockDoc = {
 
 });
 /*
  * Copyright 2009-2011 Mozilla Foundation and contributors
  * Licensed under the New BSD license. See LICENSE.txt or:
  * http://opensource.org/licenses/BSD-3-Clause
  */
 
-define('gclitest/testFocus', ['require', 'exports', 'module' , 'gclitest/helpers', 'gclitest/mockCommands'], function(require, exports, module) {
-
-
+define('gclitest/testFocus', ['require', 'exports', 'module' , 'test/assert', 'gclitest/helpers', 'gclitest/mockCommands'], function(require, exports, module) {
+
+
+var test = require('test/assert');
 var helpers = require('gclitest/helpers');
 var mockCommands = require('gclitest/mockCommands');
 
 exports.setup = function(options) {
   mockCommands.setup();
   helpers.setup(options);
 };
 
 exports.shutdown = function(options) {
   mockCommands.shutdown();
   helpers.shutdown(options);
 };
 
 exports.testBasic = function(options) {
+  if (options.isJsdom) {
+    test.log('jsdom does not pass on focus events properly, skipping testBasic');
+    return;
+  }
+
   helpers.focusInput();
   helpers.exec(options, 'help');
 
   helpers.setInput('tsn deep');
   helpers.check({
     input:  'tsn deep',
     hints:          '',
     markup: 'IIIVIIII',
@@ -3338,19 +3334,18 @@ exports.testIncomplete = function(option
   test.is(requisition._unassigned[0].param.type.isIncompleteName, true,
           'unassigned.isIncompleteName: tsg -');
 };
 
 exports.testHidden = function(options) {
   helpers.setInput('tshidde');
   helpers.check({
     input:  'tshidde',
-    markup: 'EEEEEEE',
-    status: 'ERROR',
-    hints:  '',
+    hints:         ' -> tse',
+    status: 'ERROR'
   });
 
   helpers.setInput('tshidden');
   helpers.check({
     input:  'tshidden',
     hints:          ' [options]',
     markup: 'VVVVVVVV',
     status: 'VALID',
@@ -3465,32 +3460,33 @@ exports.testHidden = function(options) {
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
-define('gclitest/testIntro', ['require', 'exports', 'module' , 'gclitest/helpers', 'test/assert'], function(require, exports, module) {
+define('gclitest/testIntro', ['require', 'exports', 'module' , 'gclitest/helpers', 'test/assert', 'gcli/canon'], function(require, exports, module) {
 
   var helpers = require('gclitest/helpers');
   var test = require('test/assert');
+  var canon = require('gcli/canon');
 
   exports.setup = function(options) {
     helpers.setup(options);
   };
 
   exports.shutdown = function(options) {
     helpers.shutdown(options);
   };
 
   exports.testIntroStatus = function(options) {
-    if (options.isFirefox) {
-      test.log('Skipping testIntroStatus in Firefox.');
+    if (canon.getCommand('intro') == null) {
+      test.log('Skipping testIntroStatus; missing intro command.');
       return;
     }
 
     helpers.setInput('intro');
     helpers.check({
       typed:  'intro',
       markup: 'VVVVV',
       status: 'VALID',
@@ -3502,18 +3498,18 @@ define('gclitest/testIntro', ['require',
       typed:  'intro foo',
       markup: 'VVVVVVEEE',
       status: 'ERROR',
       hints: ''
     });
   };
 
   exports.testIntroExec = function(options) {
-    if (options.isFirefox) {
-      test.log('Skipping testIntroExec in Firefox.');
+    if (canon.getCommand('intro') == null) {
+      test.log('Skipping testIntroStatus; missing intro command.');
       return;
     }
 
     helpers.exec(options, {
       typed: 'intro',
       args: { },
       outputMatch: [
         /command\s*line/,
@@ -3678,17 +3674,20 @@ exports.testBasic = function(options) {
   check('VVVVVVVVVVVVVVVVVVVVVVV', Status.VALID, 'window.document.title', 0);
 
   input('{ d');
   check('VVI', Status.ERROR, 'd', 'document');
 
   input('{ document.title');
   check('VVVVVVVVVVVVVVVV', Status.VALID, 'document.title', 0);
 
-  test.ok('donteval' in options.window, 'donteval exists');
+  if (!options.isJsdom) {
+    // jsdom causes an eval here, maybe that's node/v8?
+    test.ok('donteval' in options.window, 'donteval exists');
+  }
 
   input('{ don');
   check('VVIII', Status.ERROR, 'don', 'donteval');
 
   input('{ donteval');
   check('VVVVVVVVVV', Status.VALID, 'donteval', 0);
 
   /*
@@ -3827,22 +3826,22 @@ exports.testComplete = function(options)
 
   if (!canon.getCommand('{')) {
     test.log('Skipping exec tests because { is not registered');
   }
   else {
     check('{ wind', COMPLETES_TO, '{ window', 0);
     check('{ window.docum', COMPLETES_TO, '{ window.document', 0);
 
-    // Bug 717228: This fails under node
-    if (!options.isNode) {
+    // Bug 717228: This fails under jsdom
+    if (!options.isJsdom) {
       check('{ window.document.titl', COMPLETES_TO, '{ window.document.title ', 0);
     }
     else {
-      test.log('Running under Node. Skipping tests due to bug 717228.');
+      test.log('Skipping tests due to jsdom and bug 717228.');
     }
   }
 };
 
 exports.testInternalComplete = function(options) {
   // Bug 664377
   // check('tsela 1', COMPLETES_TO, 'tselarr 1', 0, 3, 8);
 };
@@ -4003,71 +4002,76 @@ exports.testNode = function(options) {
         status: 'ERROR',
         message: 'Syntax error in CSS query'
       },
       nodes: { status: 'VALID' },
       nodes2: { status: 'VALID' }
     }
   });
 
-  helpers.setInput('tse :root');
-  helpers.check({
-    input:  'tse :root',
-    hints:           ' [options]',
-    markup: 'VVVVVVVVV',
-    cursor: 9,
-    current: 'node',
-    status: 'VALID',
-    args: {
-      command: { name: 'tse' },
-      node: { arg: ' :root', status: 'VALID' },
-      nodes: { status: 'VALID' },
-      nodes2: { status: 'VALID' }
-    }
-  });
-
-  helpers.setInput('tse :root ');
-  helpers.check({
-    input:  'tse :root ',
-    hints:            '[options]',
-    markup: 'VVVVVVVVVV',
-    cursor: 10,
-    current: 'node',
-    status: 'VALID',
-    args: {
-      command: { name: 'tse' },
-      node: { arg: ' :root ', status: 'VALID' },
-      nodes: { status: 'VALID' },
-      nodes2: { status: 'VALID' }
-    }
-  });
-  test.is(requisition.getAssignment('node').value.tagName,
-          'HTML',
-          'root id');
-
-  helpers.setInput('tse #gcli-nomatch');
-  helpers.check({
-    input:  'tse #gcli-nomatch',
-    hints:                   ' [options]',
-    markup: 'VVVVIIIIIIIIIIIII',
-    cursor: 17,
-    current: 'node',
-    status: 'ERROR',
-    args: {
-      command: { name: 'tse' },
-      node: {
-        value: undefined,
-        arg: ' #gcli-nomatch',
-        status: 'INCOMPLETE',
-        message: 'No matches'
-      },
-      nodes: { status: 'VALID' },
-      nodes2: { status: 'VALID' }
-    }
-  });
+  if (options.isJsdom) {
+    test.log('skipping node tests because jsdom');
+  }
+  else {
+    helpers.setInput('tse :root');
+    helpers.check({
+      input:  'tse :root',
+      hints:           ' [options]',
+      markup: 'VVVVVVVVV',
+      cursor: 9,
+      current: 'node',
+      status: 'VALID',
+      args: {
+        command: { name: 'tse' },
+        node: { arg: ' :root', status: 'VALID' },
+        nodes: { status: 'VALID' },
+        nodes2: { status: 'VALID' }
+      }
+    });
+
+    helpers.setInput('tse :root ');
+    helpers.check({
+      input:  'tse :root ',
+      hints:            '[options]',
+      markup: 'VVVVVVVVVV',
+      cursor: 10,
+      current: 'node',
+      status: 'VALID',
+      args: {
+        command: { name: 'tse' },
+        node: { arg: ' :root ', status: 'VALID' },
+        nodes: { status: 'VALID' },
+        nodes2: { status: 'VALID' }
+      }
+    });
+    test.is(requisition.getAssignment('node').value.tagName,
+            'HTML',
+            'root id');
+
+    helpers.setInput('tse #gcli-nomatch');
+    helpers.check({
+      input:  'tse #gcli-nomatch',
+      hints:                   ' [options]',
+      markup: 'VVVVIIIIIIIIIIIII',
+      cursor: 17,
+      current: 'node',
+      status: 'ERROR',
+      args: {
+        command: { name: 'tse' },
+        node: {
+          value: undefined,
+          arg: ' #gcli-nomatch',
+          status: 'INCOMPLETE',
+          message: 'No matches'
+        },
+        nodes: { status: 'VALID' },
+        nodes2: { status: 'VALID' }
+      }
+    });
+  }
 
   helpers.setInput('tse #');
   helpers.check({
     input:  'tse #',
     hints:       ' [options]',
     markup: 'VVVVE',
     cursor: 5,
     current: 'node',
@@ -4126,16 +4130,21 @@ exports.testNode = function(options) {
       nodes2: { status: 'VALID' }
     }
   });
 };
 
 exports.testNodes = function(options) {
   var requisition = options.display.requisition;
 
+  if (options.isJsdom) {
+    test.log('skipping node tests because jsdom');
+    return;
+  }
+
   helpers.setInput('tse :root --nodes *');
   helpers.check({
     input:  'tse :root --nodes *',
     hints:                       ' [options]',
     markup: 'VVVVVVVVVVVVVVVVVVV',
     current: 'nodes',
     status: 'VALID',
     args: {
@@ -4231,23 +4240,24 @@ exports.testNodes = function(options) {
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
-define('gclitest/testPref', ['require', 'exports', 'module' , 'gcli/commands/pref', 'gclitest/helpers', 'gclitest/mockSettings', 'test/assert'], function(require, exports, module) {
+define('gclitest/testPref', ['require', 'exports', 'module' , 'gcli/commands/pref', 'gclitest/helpers', 'gclitest/mockSettings', 'test/assert', 'gcli/canon'], function(require, exports, module) {
 
 
 var pref = require('gcli/commands/pref');
 var helpers = require('gclitest/helpers');
 var mockSettings = require('gclitest/mockSettings');
 var test = require('test/assert');
+var canon = require('gcli/canon');
 
 
 exports.setup = function(options) {
   helpers.setup(options);
 
   if (!options.isFirefox) {
     mockSettings.setup();
   }
@@ -4265,16 +4275,21 @@ exports.shutdown = function(options) {
 };
 
 exports.testPrefShowStatus = function(options) {
   if (options.isFirefox) {
     test.log('Skipping testPrefShowStatus in Firefox.');
     return;
   }
 
+  if (canon.getCommand('intro') == null) {
+    test.log('Skipping testIntroStatus; missing intro command.');
+    return;
+  }
+
   helpers.setInput('pref s');
   helpers.check({
     typed:  'pref s',
     hints:        'et',
     markup: 'IIIIVI',
     status: 'ERROR'
   });
 
@@ -4328,16 +4343,21 @@ exports.testPrefShowStatus = function(op
 };
 
 exports.testPrefSetStatus = function(options) {
   if (options.isFirefox) {
     test.log('Skipping testPrefSetStatus in Firefox.');
     return;
   }
 
+  if (canon.getCommand('intro') == null) {
+    test.log('Skipping testIntroStatus; missing intro command.');
+    return;
+  }
+
   helpers.setInput('pref s');
   helpers.check({
     typed:  'pref s',
     hints:        'et',
     markup: 'IIIIVI',
     status: 'ERROR',
   });
 
@@ -4347,17 +4367,17 @@ exports.testPrefSetStatus = function(opt
     hints:          ' <setting> <value>',
     markup: 'VVVVVVVV',
     status: 'ERROR'
   });
 
   helpers.setInput('pref xxx');
   helpers.check({
     typed:  'pref xxx',
-    markup: 'EEEEVEEE',
+    markup: 'IIIIVIII',
     status: 'ERROR'
   });
 
   helpers.setInput('pref set ');
   helpers.check({
     typed:  'pref set ',
     hints:           'allowSet <value>',
     markup: 'VVVVVVVVV',
@@ -4390,16 +4410,21 @@ exports.testPrefSetStatus = function(opt
 };
 
 exports.testPrefExec = function(options) {
   if (options.isFirefox) {
     test.log('Skipping testPrefExec in Firefox.');
     return;
   }
 
+  if (canon.getCommand('intro') == null) {
+    test.log('Skipping testIntroStatus; missing intro command.');
+    return;
+  }
+
   var initialAllowSet = pref.allowSet.value;
   pref.allowSet.value = false;
 
   test.is(mockSettings.tempNumber.value, 42, 'set to 42');
 
   helpers.exec(options, {
     typed: 'pref set tempNumber 4',
     args: {
@@ -4739,22 +4764,21 @@ exports.testPredictions = function(optio
   test.ok(options2.length > 1, 'have resources');
   options2.forEach(function(prediction) {
     checkPrediction(resource2, prediction);
   });
 
   var resource3 = types.getType({ name: 'resource', include: 'text/css' });
   var options3 = resource3.getLookup();
   // jsdom fails to support digging into stylesheets
-  if (!options.isNode) {
+  if (!options.isJsdom) {
     test.ok(options3.length >= 1, 'have resources');
   }
   else {
-    test.log('Running under Node. ' +
-             'Skipping checks due to jsdom document.stylsheets support.');
+    test.log('Skipping checks due to jsdom document.stylsheets support.');
   }
   options3.forEach(function(prediction) {
     checkPrediction(resource3, prediction);
   });
 
   var resource4 = types.getType({ name: 'resource' });
   var options4 = resource4.getLookup();
 
@@ -5013,39 +5037,38 @@ exports.testChange = function(options) {
  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
 define('gclitest/testSpell', ['require', 'exports', 'module' , 'test/assert', 'gcli/types/spell'], function(require, exports, module) {
 
 var test = require('test/assert');
-var Speller = require('gcli/types/spell').Speller;
+var spell = require('gcli/types/spell');
 
 exports.setup = function() {
 };
 
 exports.shutdown = function() {
 };
 
 exports.testSpellerSimple = function(options) {
   if (options.isFirefox) {
     test.log('Skipping testPref in Firefox.');
     return;
   }
 
-  var speller = new Speller();
-  speller.train(Object.keys(options.window));
-
-  test.is(speller.correct('document'), 'document');
-  test.is(speller.correct('documen'), 'document');
-  test.is(speller.correct('ocument'), 'document');
-  test.is(speller.correct('odcument'), 'document');
-
-  test.is(speller.correct('========='), null);
+  var alternatives = Object.keys(options.window);
+
+  test.is(spell.correct('document', alternatives), 'document');
+  test.is(spell.correct('documen', alternatives), 'document');
+  test.is(spell.correct('ocument', alternatives), 'document');
+  test.is(spell.correct('odcument', alternatives), 'document');
+
+  test.is(spell.correct('=========', alternatives), undefined);
 };
 
 
 });
 /*
  * Copyright 2012, Mozilla Foundation and contributors
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
@@ -5453,17 +5476,17 @@ function type(typed, tests, options) {
   var inputter = options.display.inputter;
   var tooltip = options.display.tooltip;
 
   inputter.setInput(typed);
   if (tests.cursor) {
     inputter.setCursor({ start: tests.cursor, end: tests.cursor });
   }
 
-  if (!options.isNode) {
+  if (!options.isJsdom) {
     if (tests.important) {
       test.ok(tooltip.field.isImportant, 'Important for ' + typed);
     }
     else {
       test.ok(!tooltip.field.isImportant, 'Not important for ' + typed);
     }
 
     if (tests.options) {
@@ -5483,39 +5506,37 @@ function type(typed, tests, options) {
 }
 
 exports.testActivate = function(options) {
   if (!options.display) {
     test.log('No display. Skipping activate tests');
     return;
   }
 
-  if (options.isNode) {
-    test.log('Running under Node. Reduced checks due to JSDom.textContent');
+  if (options.isJsdom) {
+    test.log('Reduced checks due to JSDom.textContent');
   }
 
   type(' ', { }, options);
 
   type('tsb ', {
     important: true,
     options: [ 'false', 'true' ]
   }, options);
 
   type('tsb t', {
     important: true,
     options: [ 'true' ]
   }, options);
 
   type('tsb tt', {
     important: true,
-    options: [ ],
-    error: 'Can\'t use \'tt\'.'
+    options: [ 'true' ]
   }, options);
 
-
   type('asdf', {
     important: false,
     options: [ ],
     error: 'Can\'t use \'asdf\'.'
   }, options);
 
   type('', { }, options);
 };
@@ -5567,19 +5588,18 @@ function forEachType(options, callback) 
     }
 
     var type = types.getType(options);
     callback(type);
   });
 }
 
 exports.testDefault = function(options) {
-  if (options.isNode) {
-    test.log('Running under Node. ' +
-             'Skipping tests due to issues with resource type.');
+  if (options.isJsdom) {
+    test.log('Skipping tests due to issues with resource type.');
     return;
   }
 
   forEachType({}, function(type) {
     var blank = type.getBlank().value;
 
     // boolean and array types are exempt from needing undefined blank values
     if (type.name === 'boolean') {
--- a/browser/devtools/scratchpad/scratchpad.js
+++ b/browser/devtools/scratchpad/scratchpad.js
@@ -30,16 +30,17 @@ Cu.import("resource://gre/modules/jsdebu
 const SCRATCHPAD_CONTEXT_CONTENT = 1;
 const SCRATCHPAD_CONTEXT_BROWSER = 2;
 const SCRATCHPAD_L10N = "chrome://browser/locale/devtools/scratchpad.properties";
 const DEVTOOLS_CHROME_ENABLED = "devtools.chrome.enabled";
 const PREF_RECENT_FILES_MAX = "devtools.scratchpad.recentFilesMax";
 const BUTTON_POSITION_SAVE = 0;
 const BUTTON_POSITION_CANCEL = 1;
 const BUTTON_POSITION_DONT_SAVE = 2;
+const BUTTON_POSITION_REVERT=0;
 
 let keysbundle = Services.strings.createBundle("chrome://global-platform/locale/platformKeys.properties");
 
 
 function SP_Pretty_Key(aElemKey) {
 
   let elemString = "";
   let elemMod = aElemKey.getAttribute("modifiers");
@@ -918,16 +919,82 @@ var Scratchpad = {
         if (aCallback) {
           aCallback(aStatus);
         }
       });
     }
   },
 
   /**
+   * Restore content from saved version of current file.
+   *
+   * @param function aCallback
+   *        Optional function you want to call when file is saved
+   */
+  revertFile: function SP_revertFile(aCallback)
+  {
+    
+    let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile);
+    file.initWithPath(this.filename);
+
+    if (!file.exists()) {
+      return;
+    }
+
+    this.importFromFile(file, false, function(aStatus, aContent) {
+      if (aCallback) {
+        aCallback(aStatus);
+      }
+    });
+  },
+
+  /**
+   * Prompt to revert scratchpad if it has unsaved changes.
+   *
+   * @param function aCallback
+   *        Optional function you want to call when file is saved. The callback
+   *        receives three arguments:
+   *          - aRevert (boolean) - tells if the file has been reverted.
+   *          - status (number) - the file revert status result (if the file was
+   *          saved).
+   */
+  promptRevert: function SP_promptRervert(aCallback)
+  {
+    if (this.filename) {
+      let ps = Services.prompt;
+      let flags = ps.BUTTON_POS_0 * ps.BUTTON_TITLE_REVERT +
+                  ps.BUTTON_POS_1 * ps.BUTTON_TITLE_CANCEL;
+
+      let button = ps.confirmEx(window,
+                          this.strings.GetStringFromName("confirmRevert.title"),
+                          this.strings.GetStringFromName("confirmRevert"),
+                          flags, null, null, null, null, {});
+      if (button == BUTTON_POSITION_CANCEL) {
+        if (aCallback) {
+          aCallback(false);
+        }
+
+        return;
+      }
+      if (button == BUTTON_POSITION_REVERT) {
+        this.revertFile(function(aStatus) {
+          if(aCallback){
+            aCallback(true, aStatus);
+          }
+        });
+
+        return;
+      }
+    }
+    if (aCallback) {
+      aCallback(false);
+    }
+  },
+
+  /**
    * Open the Error Console.
    */
   openErrorConsole: function SP_openErrorConsole()
   {
     this.browserWindow.toJavaScriptConsole();
   },
 
   /**
@@ -1102,16 +1169,24 @@ var Scratchpad = {
    * @private
    * @see SourceEditor.EVENTS.DIRTY_CHANGED
    * @param object aEvent
    *        The DirtyChanged event object.
    */
   _onDirtyChanged: function SP__onDirtyChanged(aEvent)
   {
     Scratchpad._updateTitle();
+    if (Scratchpad.filename) {
+      if (Scratchpad.editor.dirty) {
+        document.getElementById("sp-cmd-revert").removeAttribute("disabled");
+      }
+      else {
+        document.getElementById("sp-cmd-revert").setAttribute("disabled", true);
+      }
+    }
   },
 
   /**
    * Undo the last action of the user.
    */
   undo: function SP_undo()
   {
     this.editor.undo();
--- a/browser/devtools/scratchpad/scratchpad.xul
+++ b/browser/devtools/scratchpad/scratchpad.xul
@@ -28,16 +28,17 @@
 <commandset id="sourceEditorCommands"/>
 
 <commandset id="sp-commandset">
   <command id="sp-cmd-newWindow" oncommand="Scratchpad.openScratchpad();"/>
   <command id="sp-cmd-openFile" oncommand="Scratchpad.openFile();"/>
   <command id="sp-cmd-clearRecentFiles" oncommand="Scratchpad.clearRecentFiles();"/>
   <command id="sp-cmd-save" oncommand="Scratchpad.saveFile();"/>
   <command id="sp-cmd-saveas" oncommand="Scratchpad.saveFileAs();"/>
+  <command id="sp-cmd-revert" oncommand="Scratchpad.promptRevert();" disabled="true"/>
 
   <!-- TODO: bug 650340 - implement printFile()
   <command id="sp-cmd-printFile" oncommand="Scratchpad.printFile();" disabled="true"/>
  -->
 
   <command id="sp-cmd-close" oncommand="Scratchpad.close();"/>
   <command id="sp-cmd-run" oncommand="Scratchpad.run();"/>
   <command id="sp-cmd-inspect" oncommand="Scratchpad.inspect();"/>
@@ -127,16 +128,20 @@
                 label="&saveFileCmd.label;"
                 accesskey="&saveFileCmd.accesskey;"
                 key="sp-key-save"
                 command="sp-cmd-save"/>
       <menuitem id="sp-menu-saveas"
                 label="&saveFileAsCmd.label;"
                 accesskey="&saveFileAsCmd.accesskey;"
                 command="sp-cmd-saveas"/>
+      <menuitem id="sp-menu-revert"
+                label="&revertCmd.label;"
+                accesskey="&revertCmd.accesskey;"
+                command="sp-cmd-revert"/>
       <menuseparator/>
 
       <!-- TODO: bug 650340 - implement printFile
       <menuitem id="sp-menu-print"
                 label="&printCmd.label;"
                 accesskey="&printCmd.accesskey;"
                 command="sp-cmd-printFile"/>
       <menuseparator/>
--- a/browser/devtools/scratchpad/test/Makefile.in
+++ b/browser/devtools/scratchpad/test/Makefile.in
@@ -28,11 +28,12 @@ MOCHITEST_BROWSER_FILES = \
 		browser_scratchpad_bug_653427_confirm_close.js \
 		browser_scratchpad_bug684546_reset_undo.js \
 		browser_scratchpad_bug690552_display_outputs_errors.js \
 		browser_scratchpad_bug650345_find_ui.js \
 		browser_scratchpad_bug714942_goto_line_ui.js \
 		browser_scratchpad_bug_650760_help_key.js \
 		browser_scratchpad_bug_651942_recent_files.js \
 		browser_scratchpad_bug756681_display_non_error_exceptions.js \
+		browser_scratchpad_bug_751744_revert_to_saved.js \
 		head.js \
 
 include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/browser/devtools/scratchpad/test/browser_scratchpad_bug_751744_revert_to_saved.js
@@ -0,0 +1,137 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+let tempScope = {};
+Cu.import("resource://gre/modules/NetUtil.jsm", tempScope);
+Cu.import("resource://gre/modules/FileUtils.jsm", tempScope);
+let NetUtil = tempScope.NetUtil;
+let FileUtils = tempScope.FileUtils;
+
+// Reference to the Scratchpad object.
+let gScratchpad;
+
+// Reference to the temporary nsIFiles.
+let gFile;
+
+// Temporary file name.
+let gFileName = "testFileForBug751744.tmp"
+
+
+// Content for the temporary file.
+let gFileContent = "/* this file is already saved */\n" +
+                   "function foo() { alert('bar') }";
+let gLength = gFileContent.length;
+
+// Reference to the menu entry.
+let menu;
+
+function startTest()
+{
+  gScratchpad = gScratchpadWindow.Scratchpad;
+  menu = gScratchpadWindow.document.getElementById("sp-menu-revert");
+  createAndLoadTemporaryFile();
+}
+
+function testAfterSaved() {
+  // Check if the revert menu is disabled as the file is at saved state.
+  ok(menu.hasAttribute("disabled"), "The revert menu entry is disabled.");
+
+  // chancging the text in the file
+  gScratchpad.setText("\nfoo();", gLength, gLength);
+  // Checking the text got changed
+  is(gScratchpad.getText(), gFileContent + "\nfoo();",
+     "The text changed the first time.");
+
+  // Revert menu now should be enabled.
+  ok(!menu.hasAttribute("disabled"),
+     "The revert menu entry is enabled after changing text first time");
+
+  // reverting back to last saved state.
+  gScratchpad.revertFile(testAfterRevert);
+}
+
+function testAfterRevert() {
+  // Check if the file's text got reverted
+  is(gScratchpad.getText(), gFileContent,
+     "The text reverted back to original text.");
+  // The revert menu should be disabled again.
+  ok(menu.hasAttribute("disabled"),
+     "The revert menu entry is disabled after reverting.");
+
+  // chancging the text in the file again
+  gScratchpad.setText("\nalert(foo.toSource());", gLength, gLength);
+  // Saving the file.
+  gScratchpad.saveFile(testAfterSecondSave);
+}
+
+function testAfterSecondSave() {
+  // revert menu entry should be disabled.
+  ok(menu.hasAttribute("disabled"),
+     "The revert menu entry is disabled after saving.");
+
+  // changing the text.
+  gScratchpad.setText("\nfoo();", gLength + 23, gLength + 23);
+
+  // revert menu entry should get enabled yet again.
+  ok(!menu.hasAttribute("disabled"),
+     "The revert menu entry is enabled after changing text third time");
+
+  // reverting back to last saved state.
+  gScratchpad.revertFile(testAfterSecondRevert);
+}
+
+function testAfterSecondRevert() {
+  // Check if the file's text got reverted
+  is(gScratchpad.getText(), gFileContent + "\nalert(foo.toSource());",
+     "The text reverted back to the changed saved text.");
+  // The revert menu should be disabled again.
+  ok(menu.hasAttribute("disabled"),
+     "Revert menu entry is disabled after reverting to changed saved state.");
+  gFile.remove(false);
+  gFile = null;
+  gScratchpad = null;
+}
+
+function createAndLoadTemporaryFile()
+{
+  // Create a temporary file.
+  gFile = FileUtils.getFile("TmpD", [gFileName]);
+  gFile.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0666);
+
+  // Write the temporary file.
+  let fout = Cc["@mozilla.org/network/file-output-stream;1"].
+             createInstance(Ci.nsIFileOutputStream);
+  fout.init(gFile.QueryInterface(Ci.nsILocalFile), 0x02 | 0x08 | 0x20,
+            0644, fout.DEFER_OPEN);
+
+  let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"].
+                  createInstance(Ci.nsIScriptableUnicodeConverter);
+  converter.charset = "UTF-8";
+  let fileContentStream = converter.convertToInputStream(gFileContent);
+
+  NetUtil.asyncCopy(fileContentStream, fout, tempFileSaved);
+}
+
+function tempFileSaved(aStatus)
+{
+  ok(Components.isSuccessCode(aStatus),
+     "the temporary file was saved successfully");
+
+  // Import the file into Scratchpad.
+  gScratchpad.setFilename(gFile.path);
+  gScratchpad.importFromFile(gFile.QueryInterface(Ci.nsILocalFile),  true,
+                             testAfterSaved);
+}
+
+function test()
+{
+  gBrowser.selectedTab = gBrowser.addTab();
+  gBrowser.selectedBrowser.addEventListener("load", function onLoad() {
+    gBrowser.selectedBrowser.removeEventListener("load", onLoad, true);
+    openScratchpad(startTest);
+  }, true);
+
+  content.location = "data:text/html,<p>test reverting to last saved state of" +
+                     " a file </p>";
+}
--- a/browser/devtools/shared/DeveloperToolbar.jsm
+++ b/browser/devtools/shared/DeveloperToolbar.jsm
@@ -10,16 +10,18 @@ const NS_XHTML = "http://www.w3.org/1999
 
 const WEBCONSOLE_CONTENT_SCRIPT_URL =
   "chrome://browser/content/devtools/HUDService-content.js";
 
 Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
 Components.utils.import("resource://gre/modules/Services.jsm");
 Components.utils.import("resource:///modules/devtools/Commands.jsm");
 
+const Node = Components.interfaces.nsIDOMNode;
+
 XPCOMUtils.defineLazyModuleGetter(this, "console",
                                   "resource://gre/modules/devtools/Console.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "gcli",
                                   "resource:///modules/devtools/gcli.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "CmdCommands",
                                   "resource:///modules/devtools/CmdCmd.jsm");
@@ -133,16 +135,38 @@ DeveloperToolbar.prototype.focus = funct
   if (this.visible) {
     this._input.focus();
   } else {
     this.show(true);
   }
 };
 
 /**
+ * Called from browser.xul in response to menu-click or keyboard shortcut to
+ * toggle the toolbar
+ */
+DeveloperToolbar.prototype.focusToggle = function DT_focusToggle()
+{
+  if (this.visible) {
+    // If we have focus then the active element is the HTML input contained
+    // inside the xul input element
+    var active = this._chromeWindow.document.activeElement;
+    var position = this._input.compareDocumentPosition(active);
+    if (position & Node.DOCUMENT_POSITION_CONTAINED_BY) {
+      this.hide();
+    }
+    else {
+      this._input.focus();
+    }
+  } else {
+    this.show(true);
+  }
+};
+
+/**
  * Even if the user has not clicked on 'Got it' in the intro, we only show it
  * once per session.
  * Warning this is slightly messed up because this.DeveloperToolbar is not the
  * same as this.DeveloperToolbar when in browser.js context.
  */
 DeveloperToolbar.introShownThisSession = false;
 
 /**
--- a/browser/devtools/styleeditor/StyleEditor.jsm
+++ b/browser/devtools/styleeditor/StyleEditor.jsm
@@ -754,16 +754,97 @@ StyleEditor.prototype = {
         break;
       default:
         this._loadSourceFromCache(this.styleSheet.href);
         break;
     }
   },
 
   /**
+   * Decode a CSS source string to unicode according to the character set rules
+   * defined in <http://www.w3.org/TR/CSS2/syndata.html#charset>.
+   *
+   * @param string aString
+   *        Source of a CSS stylesheet, loaded from file or cache.
+   * @param string aChannelCharset
+   *        Charset of the source string if set by the HTTP channel.
+   * @return string
+   *         The CSS string, in unicode.
+   */
+  _decodeCSSCharset: function SE__decodeCSSCharset(aString, aChannelCharset)
+  {
+    // StyleSheet's charset can be specified from multiple sources
+
+    if (aChannelCharset.length > 0) {
+      // step 1 of syndata.html: charset given in HTTP header.
+      return this._convertToUnicode(aString, aChannelCharset);
+    }
+
+    let sheet = this.styleSheet;
+    if (sheet) {
+      // Do we have a @charset rule in the stylesheet?
+      // step 2 of syndata.html (without the BOM check).
+      if (sheet.cssRules) {
+        let rules = sheet.cssRules;
+        if (rules.length
+            && rules.item(0).type == Ci.nsIDOMCSSRule.CHARSET_RULE) {
+          return this._convertToUnicode(aString, rules.item(0).encoding);
+        }
+      }
+
+      if (sheet.ownerNode) {
+        // step 3: see <link charset="…">
+        let linkCharset = sheet.ownerNode.getAttribute("charset");
+        if (linkCharset != null) {
+          return this._convertToUnicode(aString, linkCharset);
+        }
+      }
+
+      // step 4 (1 of 2): charset of referring stylesheet.
+      let parentSheet = sheet.parentStyleSheet;
+      if (parentSheet && parentSheet.cssRules &&
+          parentSheet.cssRules[0].type == Ci.nsIDOMCSSRule.CHARSET_RULE) {
+        return this._convertToUnicode(aString,
+            parentSheet.cssRules[0].encoding);
+      }
+
+      // step 4 (2 of 2): charset of referring document.
+      if (sheet.ownerNode && sheet.ownerNode.ownerDocument.characterSet) {
+        return this._convertToUnicode(aString,
+            sheet.ownerNode.ownerDocument.characterSet);
+      }
+    }
+
+    // step 5: default to utf-8.
+    return this._convertToUnicode(aString, "UTF-8");
+  },
+
+  /**
+   * Convert a given string, encoded in a given character set, to unicode.
+   * @param string aString
+   *        A string.
+   * @param string aCharset
+   *        A character set.
+   * @return string
+   *         A unicode string.
+   */
+  _convertToUnicode: function SE__convertToUnicode(aString, aCharset) {
+    // Decoding primitives.
+    let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
+        .createInstance(Ci.nsIScriptableUnicodeConverter);
+
+    try {
+      converter.charset = aCharset;
+      return converter.ConvertToUnicode(aString);
+    } catch(e) {
+      return aString;
+    }
+  },
+
+  /**
    * Load source from a file or file-like resource.
    *
    * @param string aHref
    *        URL for the stylesheet.
    */
   _loadSourceFromFile: function SE__loadSourceFromFile(aHref)
   {
     try {
@@ -785,45 +866,55 @@ StyleEditor.prototype = {
    *
    * @param string aHref
    *        URL for the stylesheet.
    */
   _loadSourceFromCache: function SE__loadSourceFromCache(aHref)
   {
     let channel = Services.io.newChannel(aHref, null, null);
     let chunks = [];
+    let channelCharset = "";
     let streamListener = { // nsIStreamListener inherits nsIRequestObserver
       onStartRequest: function (aRequest, aContext, aStatusCode) {
         if (!Components.isSuccessCode(aStatusCode)) {
           return this._signalError(LOAD_ERROR);
         }
       }.bind(this),
       onDataAvailable: function (aRequest, aContext, aStream, aOffset, aCount) {
+        let channel = aRequest.QueryInterface(Ci.nsIChannel);
+        if (!channelCharset) {
+          channelCharset = channel.contentCharset;
+        }
         chunks.push(NetUtil.readInputStreamToString(aStream, aCount));
       },
       onStopRequest: function (aRequest, aContext, aStatusCode) {
         if (!Components.isSuccessCode(aStatusCode)) {
           return this._signalError(LOAD_ERROR);
         }
 
-        this._onSourceLoad(chunks.join(""));
+        this._onSourceLoad(chunks.join(""), channelCharset);
       }.bind(this)
     };
 
     channel.loadFlags = channel.LOAD_FROM_CACHE;
     channel.asyncOpen(streamListener, null);
   },
 
   /**
    * Called when source has been loaded.
    *
    * @param string aSourceText
+   * @param string aCharset
+   *        Optional. The character set to use. The default is to detect the
+   *        character set following the standard (see
+   *        <http://www.w3.org/TR/CSS2/syndata.html#charset>).
    */
-  _onSourceLoad: function SE__onSourceLoad(aSourceText)
+  _onSourceLoad: function SE__onSourceLoad(aSourceText, aCharset)
   {
+    aSourceText = this._decodeCSSCharset(aSourceText, aCharset || "");
     this._restoreExpando();
     this._state.text = prettifyCSS(aSourceText);
     this._loaded = true;
     this._triggerAction("Load");
   },
 
   /**
    * Create a new style sheet and append it to the content document.
--- a/browser/devtools/styleeditor/test/browser_styleeditor_init.js
+++ b/browser/devtools/styleeditor/test/browser_styleeditor_init.js
@@ -66,16 +66,20 @@ function testEditorAdded(aChrome, aEdito
 
   if (gEditorAddedCount == 2) {
     finish();
   }
 }
 
 function testFirstStyleSheetEditor(aChrome, aEditor)
 {
+  // Note: the html <link> contains charset="UTF-8".
+  ok(aEditor._state.text.indexOf("\u263a") >= 0,
+     "stylesheet is unicode-aware.");
+
   //testing TESTCASE's simple.css stylesheet
   is(aEditor.styleSheetIndex, 0,
      "first stylesheet is at index 0");
 
   is(aEditor, aChrome.editors[0],
      "first stylesheet corresponds to StyleEditorChrome.editors[0]");
 
   ok(!aEditor.hasFlag("inline"),
--- a/browser/devtools/styleeditor/test/simple.css
+++ b/browser/devtools/styleeditor/test/simple.css
@@ -1,8 +1,9 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
+/* ☺ */
 
 body {
   margin: 0;
 }
 
--- a/browser/devtools/styleeditor/test/simple.html
+++ b/browser/devtools/styleeditor/test/simple.html
@@ -1,13 +1,13 @@
 <!doctype html>
 <html>
 <head>
   <title>simple testcase</title>
-  <link rel="stylesheet" type="text/css" media="screen" href="simple.css"/>
+  <link rel="stylesheet" charset="UTF-8" type="text/css" media="screen" href="simple.css"/>
   <style type="text/css">
   body {
     background: white;
   }
 
   div {
     font-size: 4em;
   }
--- a/browser/locales/en-US/chrome/browser/devtools/scratchpad.dtd
+++ b/browser/locales/en-US/chrome/browser/devtools/scratchpad.dtd
@@ -31,16 +31,19 @@
 
 <!ENTITY openFileCmd.label            "Open File…">
 <!ENTITY openFileCmd.accesskey        "O">
 <!ENTITY openFileCmd.commandkey       "o">
 
 <!ENTITY openRecentMenu.label         "Open Recent">
 <!ENTITY openRecentMenu.accesskey     "R">
 
+<!ENTITY revertCmd.label              "Revert…">
+<!ENTITY revertCmd.accesskey          "t">
+
 <!ENTITY saveFileCmd.label            "Save">
 <!ENTITY saveFileCmd.accesskey        "S">
 <!ENTITY saveFileCmd.commandkey       "s">
 
 <!ENTITY saveFileAsCmd.label          "Save As…">
 <!ENTITY saveFileAsCmd.accesskey      "A">
 
 <!ENTITY closeCmd.label               "Close">
--- a/browser/locales/en-US/chrome/browser/devtools/scratchpad.properties
+++ b/browser/locales/en-US/chrome/browser/devtools/scratchpad.properties
@@ -49,16 +49,24 @@ saveFile.failed=The file save operation 
 # LOCALIZATION NOTE  (confirmClose): This is message in the prompt dialog when
 # you try to close a scratchpad with unsaved changes.
 confirmClose=Do you want to save the changes you made to this scratchpad?
 
 # LOCALIZATION NOTE  (confirmClose.title): This is title of the prompt dialog when
 # you try to close a scratchpad with unsaved changes.
 confirmClose.title=Unsaved Changes
 
+# LOCALIZATION NOTE  (confirmRevert): This is message in the prompt dialog when
+# you try to revert unsaved content of scratchpad.
+confirmRevert=Do you want to revert the changes you made to this scratchpad?
+
+# LOCALIZATION NOTE  (confirmRevert.title): This is title of the prompt dialog when
+# you try to revert unsaved content of scratchpad.
+confirmRevert.title=Revert Changes
+
 # LOCALIZATION NOTE  (scratchpadIntro): This is a multi-line comment explaining
 # how to use the Scratchpad. Note that this should be a valid JavaScript
 # comment inside /* and */.
 scratchpadIntro1=/*\n * This is a JavaScript Scratchpad.\n *\n * Enter some JavaScript, then Right Click or choose from the Execute Menu:\n * 1. Run to evaluate the selected text (%1$S),\n * 2. Inspect to bring up an Object Inspector on the result (%2$S), or,\n * 3. Display to insert the result in a comment after the selection. (%3$S)\n */\n\n
 
 # LOCALIZATION NOTE  (notification.browserContext): This is the message displayed
 # over the top of the editor when the user has switched to browser context.
 browserContext.notification=This scratchpad executes in the Browser context.
--- a/browser/themes/pinstripe/browser.css
+++ b/browser/themes/pinstripe/browser.css
@@ -3151,16 +3151,24 @@ html|*#gcli-output-frame {
   border-color: hsl(210,11%,10%);
   color: hsl(210,30%,85%);
   text-shadow: 0 -1px 0 hsla(210,8%,5%,.45);
   box-shadow: 0 1px 1px hsla(210,8%,5%,.3) inset,
               0 0 0 1px hsla(210,16%,76%,.1) inset,
               0 1px 0 hsla(210,16%,76%,.15);
 }
 
+.gclitoolbar-input-node::-moz-selection {
+  background-color: green !important;
+}
+
+.gclitoolbar-input-node {
+  background-color: red;
+}
+
 .gclitoolbar-complete-node {
   padding-left: 21px;
   background-color: transparent;
   color: transparent;
 }
 
 .gclitoolbar-prompt {
   padding-left: 4px;
--- a/browser/themes/winstripe/browser.css
+++ b/browser/themes/winstripe/browser.css
@@ -3113,16 +3113,20 @@ html|*#gcli-output-frame {
   -moz-appearance: none;
   border-color: hsl(210,24%,10%);
   color: hsl(210,30%,85%);
   text-shadow: 0 -1px 0 hsla(210,8%,5%,.45);
   box-shadow: inset 0 1px 0 hsla(211,68%,6%,.05),
               0 0 0 1px hsla(210,40%,83%,.1);
 }
 
+.gclitoolbar-input-node {
+  background-color: green !important;
+}
+
 .gclitoolbar-complete-node {
   padding-left: 21px;
   background-color: transparent;
   color: transparent;
 }
 
 .gclitoolbar-prompt {
   padding-left: 4px;
--- a/chrome/src/nsChromeRegistry.cpp
+++ b/chrome/src/nsChromeRegistry.cpp
@@ -81,19 +81,19 @@ nsChromeRegistry::LogMessageWithContext(
   va_end(args);
   if (!formatted)
     return;
 
   nsCString spec;
   if (aURL)
     aURL->GetSpec(spec);
 
-  rv = error->Init(NS_ConvertUTF8toUTF16(formatted).get(),
-                   NS_ConvertUTF8toUTF16(spec).get(),
-                   nullptr,
+  rv = error->Init(NS_ConvertUTF8toUTF16(formatted),
+                   NS_ConvertUTF8toUTF16(spec),
+                   EmptyString(),
                    aLineNumber, 0, flags, "chrome registration");
   PR_smprintf_free(formatted);
 
   if (NS_FAILED(rv))
     return;
 
   console->LogMessage(error);
 }
--- a/content/base/src/nsContentUtils.cpp
+++ b/content/base/src/nsContentUtils.cpp
@@ -3255,19 +3255,19 @@ nsContentUtils::ReportToConsole(uint32_t
   }
   if (spec.IsEmpty() && aURI)
     aURI->GetSpec(spec);
 
   nsCOMPtr<nsIScriptError> errorObject =
       do_CreateInstance(NS_SCRIPTERROR_CONTRACTID, &rv);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  rv = errorObject->InitWithWindowID(errorText.get(),
-                                     NS_ConvertUTF8toUTF16(spec).get(), // file name
-                                     aSourceLine.get(),
+  rv = errorObject->InitWithWindowID(errorText,
+                                     NS_ConvertUTF8toUTF16(spec), // file name
+                                     aSourceLine,
                                      aLineNumber, aColumnNumber,
                                      aErrorFlags, aCategory,
                                      innerWindowID);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return sConsoleService->LogMessage(errorObject);
 }
 
--- a/content/base/src/nsEventSource.cpp
+++ b/content/base/src/nsEventSource.cpp
@@ -1088,19 +1088,19 @@ nsEventSource::PrintErrorOnConsole(const
     rv = strBundle->FormatStringFromName(aError, aFormatStrings,
                                          aFormatStringsLen,
                                          getter_Copies(message));
   } else {
     rv = strBundle->GetStringFromName(aError, getter_Copies(message));
   }
   NS_ENSURE_SUCCESS(rv, rv);
 
-  rv = errObj->InitWithWindowID(message.get(),
-                                mScriptFile.get(),
-                                nullptr,
+  rv = errObj->InitWithWindowID(message,
+                                mScriptFile,
+                                EmptyString(),
                                 mScriptLine, 0,
                                 nsIScriptError::errorFlag,
                                 "Event Source", mInnerWindowID);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // print the error message directly to the JS console
   rv = console->LogMessage(errObj);
   NS_ENSURE_SUCCESS(rv, rv);
--- a/content/base/src/nsFrameMessageManager.cpp
+++ b/content/base/src/nsFrameMessageManager.cpp
@@ -678,34 +678,34 @@ ContentScriptErrorReporter(JSContext* aC
   if (NS_FAILED(rv)) {
     return;
   }
   nsAutoString message, filename, line;
   uint32_t lineNumber, columnNumber, flags, errorNumber;
 
   if (aReport) {
     if (aReport->ucmessage) {
-      message.Assign(reinterpret_cast<const PRUnichar*>(aReport->ucmessage));
+      message.Assign(static_cast<const PRUnichar*>(aReport->ucmessage));
     }
     filename.AssignWithConversion(aReport->filename);
-    line.Assign(reinterpret_cast<const PRUnichar*>(aReport->uclinebuf));
+    line.Assign(static_cast<const PRUnichar*>(aReport->uclinebuf));
     lineNumber = aReport->lineno;
     columnNumber = aReport->uctokenptr - aReport->uclinebuf;
     flags = aReport->flags;
     errorNumber = aReport->errorNumber;
   } else {
     lineNumber = columnNumber = errorNumber = 0;
     flags = nsIScriptError::errorFlag | nsIScriptError::exceptionFlag;
   }
 
   if (message.IsEmpty()) {
     message.AssignWithConversion(aMessage);
   }
 
-  rv = scriptError->Init(message.get(), filename.get(), line.get(),
+  rv = scriptError->Init(message, filename, line,
                          lineNumber, columnNumber, flags,
                          "Message manager content script");
   if (NS_FAILED(rv)) {
     return;
   }
 
   nsCOMPtr<nsIConsoleService> consoleService =
       do_GetService(NS_CONSOLESERVICE_CONTRACTID);
@@ -726,17 +726,17 @@ ContentScriptErrorReporter(JSContext* aC
   } else {
     error.Append("error: ");
   }
   error.Append(aReport->filename);
   error.Append(", line ");
   error.AppendInt(lineNumber, 10);
   error.Append(": ");
   if (aReport->ucmessage) {
-    AppendUTF16toUTF8(reinterpret_cast<const PRUnichar*>(aReport->ucmessage),
+    AppendUTF16toUTF8(static_cast<const PRUnichar*>(aReport->ucmessage),
                       error);
   } else {
     error.Append(aMessage);
   }
 
   fprintf(stderr, "%s\n", error.get());
   fflush(stderr);
 #endif
--- a/content/base/src/nsGkAtomList.h
+++ b/content/base/src/nsGkAtomList.h
@@ -1817,16 +1817,17 @@ GK_ATOM(svgPatternFrame, "SVGPatternFram
 GK_ATOM(svgRadialGradientFrame, "SVGRadialGradientFrame")
 GK_ATOM(svgStopFrame, "SVGStopFrame")
 GK_ATOM(svgSwitchFrame, "SVGSwitchFrame")
 GK_ATOM(svgTextFrame, "SVGTextFrame")
 GK_ATOM(svgTextFrame2, "SVGTextFrame2")
 GK_ATOM(svgTextPathFrame, "SVGTextPathFrame")
 GK_ATOM(svgTSpanFrame, "SVGTSpanFrame")
 GK_ATOM(svgUseFrame, "SVGUseFrame")
+GK_ATOM(svgViewFrame, "SVGViewFrame")
 GK_ATOM(HTMLVideoFrame, "VideoFrame")
 GK_ATOM(onloadend, "onloadend")
 GK_ATOM(onloadstart, "onloadstart")
 GK_ATOM(onprogress, "onprogress")
 GK_ATOM(onsuspend, "onsuspend")
 GK_ATOM(onemptied, "onemptied")
 GK_ATOM(onstalled, "onstalled")
 GK_ATOM(onplay, "onplay")
--- a/content/base/src/nsWebSocket.cpp
+++ b/content/base/src/nsWebSocket.cpp
@@ -125,19 +125,19 @@ nsWebSocket::PrintErrorOnConsole(const c
     rv = strBundle->FormatStringFromName(aError, aFormatStrings,
                                          aFormatStringsLen,
                                          getter_Copies(message));
   } else {
     rv = strBundle->GetStringFromName(aError, getter_Copies(message));
   }
   NS_ENSURE_SUCCESS(rv, rv);
 
-  rv = errorObject->InitWithWindowID(message.get(),
-                                     NS_ConvertUTF8toUTF16(mScriptFile).get(),
-                                     nullptr, mScriptLine, 0,
+  rv = errorObject->InitWithWindowID(message,
+                                     NS_ConvertUTF8toUTF16(mScriptFile),
+                                     EmptyString(), mScriptLine, 0,
                                      nsIScriptError::errorFlag, "Web Socket",
                                      mInnerWindowID);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // print the error message directly to the JS console
   rv = console->LogMessage(errorObject);
   NS_ENSURE_SUCCESS(rv, rv);
 
--- a/content/svg/content/src/SVGFragmentIdentifier.cpp
+++ b/content/svg/content/src/SVGFragmentIdentifier.cpp
@@ -206,42 +206,35 @@ SVGFragmentIdentifier::ProcessFragmentId
     SaveOldViewBox(rootElement);
     SaveOldPreserveAspectRatio(rootElement);
     SaveOldZoomAndPan(rootElement);
   }
 
   const nsSVGViewElement *viewElement = GetViewElement(aDocument, aAnchorName);
 
   if (viewElement) {
-    if (viewElement->mViewBox.IsExplicitlySet()) {
-      rootElement->mViewBox.SetBaseValue(
-        viewElement->mViewBox.GetBaseValue(), rootElement);
-    } else {
-      RestoreOldViewBox(rootElement);
+    if (!rootElement->mCurrentViewID) {
+      rootElement->mCurrentViewID = new nsString();
     }
-    if (viewElement->mPreserveAspectRatio.IsExplicitlySet()) {
-      rootElement->mPreserveAspectRatio.SetBaseValue(
-        viewElement->mPreserveAspectRatio.GetBaseValue(), rootElement);
-    } else {
-      RestoreOldPreserveAspectRatio(rootElement);
-    }
-    if (viewElement->mEnumAttributes[nsSVGViewElement::ZOOMANDPAN].IsExplicitlySet()) {
-      rootElement->mEnumAttributes[nsSVGSVGElement::ZOOMANDPAN].SetBaseValue(
-        viewElement->mEnumAttributes[nsSVGViewElement::ZOOMANDPAN].GetBaseValue(), rootElement);
-    } else {
-      RestoreOldZoomAndPan(rootElement);
-    }
+    *rootElement->mCurrentViewID = aAnchorName;
     rootElement->mUseCurrentView = true;
+    rootElement->InvalidateTransformNotifyFrame();
     return true;
   }
 
+  bool wasOverridden = !!rootElement->mCurrentViewID;
+  rootElement->mCurrentViewID = nullptr;
+
   rootElement->mUseCurrentView = ProcessSVGViewSpec(aAnchorName, rootElement);
   if (rootElement->mUseCurrentView) {
     return true;
   }
   RestoreOldViewBox(rootElement);
   rootElement->ClearViewBoxProperty();
   RestoreOldPreserveAspectRatio(rootElement);
   rootElement->ClearPreserveAspectRatioProperty();
   RestoreOldZoomAndPan(rootElement);
   rootElement->ClearZoomAndPanProperty();
+  if (wasOverridden) {
+    rootElement->InvalidateTransformNotifyFrame();
+  }
   return false;
 }
--- a/content/svg/content/src/nsSVGSVGElement.cpp
+++ b/content/svg/content/src/nsSVGSVGElement.cpp
@@ -22,16 +22,17 @@
 #include "nsIFrame.h"
 #include "nsISVGSVGFrame.h" //XXX
 #include "nsSVGRect.h"
 #include "nsError.h"
 #include "nsISVGChildFrame.h"
 #include "nsGUIEvent.h"
 #include "nsSVGUtils.h"
 #include "nsSVGSVGElement.h"
+#include "nsSVGViewElement.h"
 #include "nsStyleUtil.h"
 
 #include "nsEventDispatcher.h"
 #include "nsSMILTimeContainer.h"
 #include "nsSMILAnimationController.h"
 #include "nsSMILTypes.h"
 #include "nsIContentIterator.h"
 
@@ -154,17 +155,16 @@ NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION
 NS_INTERFACE_MAP_END_INHERITING(nsSVGSVGElementBase)
 
 //----------------------------------------------------------------------
 // Implementation
 
 nsSVGSVGElement::nsSVGSVGElement(already_AddRefed<nsINodeInfo> aNodeInfo,
                                  FromParser aFromParser)
   : nsSVGSVGElementBase(aNodeInfo),
-    mCoordCtx(nullptr),
     mViewportWidth(0),
     mViewportHeight(0),
     mCurrentTranslate(0.0f, 0.0f),
     mCurrentScale(1.0f),
     mPreviousTranslate(0.0f, 0.0f),
     mPreviousScale(1.0f),
     mStartAnimationOnBindToTree(!aFromParser),
     mImageNeedsTransformInvalidation(false),
@@ -714,17 +714,24 @@ nsSVGSVGElement::GetTransformToElement(n
 
 //----------------------------------------------------------------------
 // nsIDOMSVGZoomAndPan methods
 
 /* attribute unsigned short zoomAndPan; */
 NS_IMETHODIMP
 nsSVGSVGElement::GetZoomAndPan(uint16_t *aZoomAndPan)
 {
-  *aZoomAndPan = mEnumAttributes[ZOOMANDPAN].GetAnimValue();
+  nsSVGViewElement* viewElement = GetCurrentViewElement();
+  if (viewElement && viewElement->mEnumAttributes[
+                       nsSVGViewElement::ZOOMANDPAN].IsExplicitlySet()) {
+    *aZoomAndPan = viewElement->mEnumAttributes[
+                     nsSVGViewElement::ZOOMANDPAN].GetAnimValue();
+  } else {
+    *aZoomAndPan = mEnumAttributes[ZOOMANDPAN].GetAnimValue();
+  }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsSVGSVGElement::SetZoomAndPan(uint16_t aZoomAndPan)
 {
   if (aZoomAndPan == nsIDOMSVGZoomAndPan::SVG_ZOOMANDPAN_DISABLE ||
       aZoomAndPan == nsIDOMSVGZoomAndPan::SVG_ZOOMANDPAN_MAGNIFY) {
@@ -1064,21 +1071,41 @@ nsSVGSVGElement::InvalidateTransformNoti
 
 bool
 nsSVGSVGElement::HasPreserveAspectRatio()
 {
   return HasAttr(kNameSpaceID_None, nsGkAtoms::preserveAspectRatio) ||
     mPreserveAspectRatio.IsAnimated();
 }
 
+nsSVGViewElement*
+nsSVGSVGElement::GetCurrentViewElement() const
+{
+  if (mCurrentViewID) {
+    nsIDocument* doc = GetCurrentDoc();
+    if (doc) {
+      Element *element = doc->GetElementById(*mCurrentViewID);
+      if (element && element->Tag() == nsGkAtoms::view) {
+        return static_cast<nsSVGViewElement*>(element);
+      }
+    }
+  }
+  return nullptr;
+}
+
 nsSVGViewBoxRect
 nsSVGSVGElement::GetViewBoxWithSynthesis(
   float aViewportWidth, float aViewportHeight) const
 {
-  if (HasViewBox()) {
+  // The logic here should match HasViewBox().
+  nsSVGViewElement* viewElement = GetCurrentViewElement();
+  if (viewElement && viewElement->mViewBox.IsExplicitlySet()) {
+    return viewElement->mViewBox.GetAnimValue();
+  }
+  if (mViewBox.IsExplicitlySet()) {
     return mViewBox.GetAnimValue();
   }
 
   if (ShouldSynthesizeViewBox()) {
     // Special case -- fake a viewBox, using height & width attrs.
     // (Use |this| as context, since if we get here, we're outermost <svg>.)
     return nsSVGViewBoxRect(0, 0,
               ComputeSynthesizedViewBoxDimension(mLengthAttributes[WIDTH],
@@ -1099,38 +1126,57 @@ nsSVGSVGElement::GetPreserveAspectRatioW
   nsIDocument* doc = GetCurrentDoc();
   if (doc && doc->IsBeingUsedAsImage()) {
     const SVGPreserveAspectRatio *pAROverridePtr = GetPreserveAspectRatioProperty();
     if (pAROverridePtr) {
       return *pAROverridePtr;
     }
   }
 
-  if (!HasViewBox() && ShouldSynthesizeViewBox()) {
+  nsSVGViewElement* viewElement = GetCurrentViewElement();
+
+  // This check is equivalent to "!HasViewBox() && ShouldSynthesizeViewBox()".
+  // We're just holding onto the viewElement that HasViewBox() would look up,
+  // so that we don't have to look it up again later.
+  if (!((viewElement && viewElement->mViewBox.IsExplicitlySet()) ||
+        mViewBox.IsExplicitlySet()) &&
+      ShouldSynthesizeViewBox()) {
     // If we're synthesizing a viewBox, use preserveAspectRatio="none";
     return SVGPreserveAspectRatio(
          nsIDOMSVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_NONE,
          nsIDOMSVGPreserveAspectRatio::SVG_MEETORSLICE_SLICE);
   }
 
+  if (viewElement && viewElement->mPreserveAspectRatio.IsExplicitlySet()) {
+    return viewElement->mPreserveAspectRatio.GetAnimValue();
+  }
   return mPreserveAspectRatio.GetAnimValue();
 }
 
 //----------------------------------------------------------------------
 // nsSVGSVGElement
 
 float
 nsSVGSVGElement::GetLength(uint8_t aCtxType)
 {
   float h, w;
 
-  if (HasViewBox()) {
-    const nsSVGViewBoxRect& viewbox = mViewBox.GetAnimValue();
-    w = viewbox.width;
-    h = viewbox.height;
+  nsSVGViewElement* viewElement = GetCurrentViewElement();
+  const nsSVGViewBoxRect* viewbox = nullptr;
+
+  // The logic here should match HasViewBox().
+  if (viewElement && viewElement->mViewBox.IsExplicitlySet()) {
+    viewbox = &viewElement->mViewBox.GetAnimValue();
+  } else if (mViewBox.IsExplicitlySet()) {
+    viewbox = &mViewBox.GetAnimValue();
+  }
+
+  if (viewbox) {
+    w = viewbox->width;
+    h = viewbox->height;
   } else if (IsInner()) {
     nsSVGSVGElement *ctx = GetCtx();
     w = mLengthAttributes[WIDTH].GetAnimValue(ctx);
     h = mLengthAttributes[HEIGHT].GetAnimValue(ctx);
   } else if (ShouldSynthesizeViewBox()) {
     w = ComputeSynthesizedViewBoxDimension(mLengthAttributes[WIDTH],
                                            mViewportWidth, this);
     h = ComputeSynthesizedViewBoxDimension(mLengthAttributes[HEIGHT],
@@ -1226,16 +1272,26 @@ nsSVGSVGElement::GetViewBox()
 
 SVGAnimatedPreserveAspectRatio *
 nsSVGSVGElement::GetPreserveAspectRatio()
 {
   return &mPreserveAspectRatio;
 }
 
 bool
+nsSVGSVGElement::HasViewBox() const
+{
+  nsSVGViewElement* viewElement = GetCurrentViewElement();
+  if (viewElement && viewElement->mViewBox.IsExplicitlySet()) {
+    return true;
+  }
+  return mViewBox.IsExplicitlySet();
+}
+
+bool
 nsSVGSVGElement::ShouldSynthesizeViewBox() const
 {
   NS_ABORT_IF_FALSE(!HasViewBox(),
                     "Should only be called if we lack a viewBox");
 
   nsIDocument* doc = GetCurrentDoc();
   return doc &&
     doc->IsBeingUsedAsImage() &&
@@ -1298,25 +1354,26 @@ void
 nsSVGSVGElement::
   SetImageOverridePreserveAspectRatio(const SVGPreserveAspectRatio& aPAR)
 {
 #ifdef DEBUG
   NS_ABORT_IF_FALSE(GetCurrentDoc()->IsBeingUsedAsImage(),
                     "should only override preserveAspectRatio in images");
 #endif
 
-  if (!HasViewBox() && ShouldSynthesizeViewBox()) {
+  bool hasViewBox = HasViewBox();
+  if (!hasViewBox && ShouldSynthesizeViewBox()) {
     // My non-<svg:image> clients will have been painting me with a synthesized
     // viewBox, but my <svg:image> client that's about to paint me now does NOT
     // want that.  Need to tell ourselves to flush our transform.
     mImageNeedsTransformInvalidation = true;
   }
   mIsPaintingSVGImageElement = true;
 
-  if (!HasViewBox()) {
+  if (!hasViewBox) {
     return; // preserveAspectRatio irrelevant (only matters if we have viewBox)
   }
 
   if (aPAR.GetDefer() && HasPreserveAspectRatio()) {
     return; // Referring element defers to my own preserveAspectRatio value.
   }
 
   if (SetPreserveAspectRatioProperty(aPAR)) {
--- a/content/svg/content/src/nsSVGSVGElement.h
+++ b/content/svg/content/src/nsSVGSVGElement.h
@@ -174,27 +174,27 @@ public:
  
   // nsSVGSVGElement methods:
   float GetLength(uint8_t mCtxType);
 
   // public helpers:
 
   /**
    * Returns true if this element has a base/anim value for its "viewBox"
-   * attribute that defines a viewBox rectangle with finite values.
+   * attribute that defines a viewBox rectangle with finite values, or
+   * if there is a view element overriding this element's viewBox and it
+   * has a valid viewBox.
    *
    * Note that this does not check whether we need to synthesize a viewBox,
    * so you must call ShouldSynthesizeViewBox() if you need to check that too.
    *
    * Note also that this method does not pay attention to whether the width or
    * height values of the viewBox rect are positive!
    */
-  bool HasViewBox() const {
-    return mViewBox.IsExplicitlySet();
-  }
+  bool HasViewBox() const;
 
   /**
    * Returns true if we should synthesize a viewBox for ourselves (that is, if
    * we're the root element in an image document, and we're not currently being
    * painted for an <svg:image> element).
    *
    * Only call this method if HasViewBox() returns false.
    */
@@ -228,16 +228,22 @@ public:
 
   // This services any pending notifications for the transform on on this root
   // <svg> node needing to be recalculated.  (Only applicable in
   // SVG-as-an-image documents.)
   virtual void FlushImageTransformInvalidation();
 
   virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const;
 
+  // Returns true IFF our attributes are currently overridden by a <view>
+  // element and that element's ID matches the passed-in string.
+  bool IsOverriddenBy(const nsAString &aViewID) const {
+    return mCurrentViewID && mCurrentViewID->Equals(aViewID);
+  }
+
   svgFloatSize GetViewportSize() const {
     return svgFloatSize(mViewportWidth, mViewportHeight);
   }
 
   void SetViewportSize(const svgFloatSize& aSize) {
     mViewportWidth  = aSize.width;
     mViewportHeight = aSize.height;
   }
@@ -252,16 +258,18 @@ private:
 
   virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent,
                               nsIContent* aBindingParent,
                               bool aCompileEventHandlers);
   virtual void UnbindFromTree(bool aDeep, bool aNullParent);
 
   // implementation helpers:
 
+  nsSVGViewElement* GetCurrentViewElement() const;
+
   // Methods for <image> elements to override my "PreserveAspectRatio" value.
   // These are private so that only our friends (nsSVGImageFrame in
   // particular) have access.
   void SetImageOverridePreserveAspectRatio(const SVGPreserveAspectRatio& aPAR);
   void ClearImageOverridePreserveAspectRatio();
 
   // Set/Clear properties to hold old or override versions of attributes
   bool SetPreserveAspectRatioProperty(const SVGPreserveAspectRatio& aPAR);
@@ -338,17 +346,17 @@ private:
   static EnumInfo sEnumInfo[1];
 
   virtual nsSVGViewBox *GetViewBox();
   virtual SVGAnimatedPreserveAspectRatio *GetPreserveAspectRatio();
 
   nsSVGViewBox                   mViewBox;
   SVGAnimatedPreserveAspectRatio mPreserveAspectRatio;
 
-  nsSVGSVGElement               *mCoordCtx;
+  nsAutoPtr<nsString>            mCurrentViewID;
 
   // The size of the rectangular SVG viewport into which we render. This is
   // not (necessarily) the same as the content area. See:
   //
   //   http://www.w3.org/TR/SVG11/coords.html#ViewportSpace
   //
   // XXXjwatt Currently only used for outer <svg>, but maybe we could use -1 to
   // flag this as an inner <svg> to save the overhead of GetCtx calls?
--- a/content/svg/content/src/nsSVGViewElement.h
+++ b/content/svg/content/src/nsSVGViewElement.h
@@ -22,16 +22,18 @@ namespace mozilla {
 typedef nsSVGElement nsSVGViewElementBase;
 
 class nsSVGViewElement : public nsSVGViewElementBase,
                          public nsIDOMSVGViewElement,
                          public nsIDOMSVGFitToViewBox,
                          public nsIDOMSVGZoomAndPan
 {
   friend class mozilla::SVGFragmentIdentifier;
+  friend class nsSVGSVGElement;
+  friend class nsSVGOuterSVGFrame;
   friend nsresult NS_NewSVGViewElement(nsIContent **aResult,
                                        already_AddRefed<nsINodeInfo> aNodeInfo);
   nsSVGViewElement(already_AddRefed<nsINodeInfo> aNodeInfo);
   
 public:
   // interfaces:
   
   NS_DECL_ISUPPORTS_INHERITED
--- a/content/svg/content/test/test_fragments.html
+++ b/content/svg/content/test/test_fragments.html
@@ -30,17 +30,16 @@ function Test(svgFragmentIdentifier, val
 }
 
 function runTests()
 {
   var svg = $("svg");
   var doc = svg.contentWindow.document;
   
   var tests = [
-      new Test("view", true, "0 200 100 100", "none", null),
       new Test("unknown", false, null, null, null),
       new Test("svgView(viewBox(0,0,200,200))", true, "0 0 200 200", null, null),
       new Test("svgView(preserveAspectRatio(xMaxYMin slice))", true, null, "xMaxYMin slice", null),
       new Test("svgView(viewBox(1,2,3,4);preserveAspectRatio(xMinYMax))", true, "1 2 3 4", "xMinYMax meet", null),
       new Test("svgView(zoomAndPan(disable))", true, null, null, "disable"),
       new Test("svgView(viewBox(bad)", false, null, null, null),
       new Test("svgView(preserveAspectRatio(bad))", false, null, null, null),
       new Test("svgView(zoomAndPan(bad))", false, null, null, null),
--- a/content/xbl/src/nsXBLDocumentInfo.cpp
+++ b/content/xbl/src/nsXBLDocumentInfo.cpp
@@ -213,20 +213,25 @@ XBL_ProtoErrorReporter(JSContext *cx,
   nsCOMPtr<nsIScriptError>
     errorObject(do_CreateInstance("@mozilla.org/scripterror;1"));
   nsCOMPtr<nsIConsoleService>
     consoleService(do_GetService("@mozilla.org/consoleservice;1"));
 
   if (errorObject && consoleService) {
     uint32_t column = report->uctokenptr - report->uclinebuf;
 
+    const PRUnichar* ucmessage =
+      static_cast<const PRUnichar*>(report->ucmessage);
+    const PRUnichar* uclinebuf =
+      static_cast<const PRUnichar*>(report->uclinebuf);
+
     errorObject->Init
-         (reinterpret_cast<const PRUnichar*>(report->ucmessage),
-          NS_ConvertUTF8toUTF16(report->filename).get(),
-          reinterpret_cast<const PRUnichar*>(report->uclinebuf),
+         (ucmessage ? nsDependentString(ucmessage) : EmptyString(),
+          NS_ConvertUTF8toUTF16(report->filename),
+          uclinebuf ? nsDependentString(uclinebuf) : EmptyString(),
           report->lineno, column, report->flags,
           "xbl javascript"
           );
     consoleService->LogMessage(errorObject);
   }
 }
 
 //----------------------------------------------------------------------
--- a/content/xml/document/src/nsXMLDocument.cpp
+++ b/content/xml/document/src/nsXMLDocument.cpp
@@ -363,18 +363,20 @@ nsXMLDocument::Load(const nsAString& aUr
 
       nsAutoString error;
       error.AssignLiteral("Cross site loading using document.load is no "
                           "longer supported. Use XMLHttpRequest instead.");
       nsCOMPtr<nsIScriptError> errorObject =
           do_CreateInstance(NS_SCRIPTERROR_CONTRACTID, &rv);
       NS_ENSURE_SUCCESS(rv, rv);
 
-      rv = errorObject->InitWithWindowID(error.get(), NS_ConvertUTF8toUTF16(spec).get(),
-                                         nullptr, 0, 0, nsIScriptError::warningFlag,
+      rv = errorObject->InitWithWindowID(error,
+                                         NS_ConvertUTF8toUTF16(spec),
+                                         EmptyString(),
+                                         0, 0, nsIScriptError::warningFlag,
                                          "DOM",
                                          callingDoc ?
                                            callingDoc->InnerWindowID() :
                                            this->InnerWindowID());
 
       NS_ENSURE_SUCCESS(rv, rv);
 
       nsCOMPtr<nsIConsoleService> consoleService =
--- a/dom/base/nsDOMClassInfo.cpp
+++ b/dom/base/nsDOMClassInfo.cpp
@@ -1948,19 +1948,19 @@ PrintWarningOnConsole(JSContext *cx, con
   nsAutoString sourcefile;
 
   if (JS_DescribeScriptedCaller(cx, &script, &lineno)) {
     if (const char *filename = ::JS_GetScriptFilename(cx, script)) {
       CopyUTF8toUTF16(nsDependentCString(filename), sourcefile);
     }
   }
 
-  nsresult rv = scriptError->InitWithWindowID(msg.get(),
-                                              sourcefile.get(),
-                                              EmptyString().get(),
+  nsresult rv = scriptError->InitWithWindowID(msg,
+                                              sourcefile,
+                                              EmptyString(),
                                               lineno,
                                               0, // column for error is not available
                                               nsIScriptError::warningFlag,
                                               "DOM:HTML",
                                               nsJSUtils::GetCurrentlyRunningCodeInnerWindowID(cx));
 
   if (NS_SUCCEEDED(rv)) {
     consoleService->LogMessage(scriptError);
@@ -5981,19 +5981,19 @@ IDBConstantGetter(JSContext *cx, JSHandl
       windowID = window->WindowID();
     }
   }
 
   nsCOMPtr<nsIScriptError> errorObject =
     do_CreateInstance(NS_SCRIPTERROR_CONTRACTID);
   NS_WARN_IF_FALSE(errorObject, "Failed to create error object");
   if (errorObject) {
-    nsresult rv = errorObject->InitWithWindowID(warnText.get(),
-                                                nullptr, // file name
-                                                nullptr, // source line
+    nsresult rv = errorObject->InitWithWindowID(warnText,
+                                                EmptyString(), // file name
+                                                EmptyString(), // source line
                                                 0, 0, // Line/col number
                                                 nsIScriptError::warningFlag,
                                                 "DOM Core", windowID);
     NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Failed to init error object");
 
     if (NS_SUCCEEDED(rv)) {
       nsCOMPtr<nsIConsoleService> consoleServ =
         do_GetService(NS_CONSOLESERVICE_CONTRACTID);
--- a/dom/base/nsJSEnvironment.cpp
+++ b/dom/base/nsJSEnvironment.cpp
@@ -396,18 +396,18 @@ public:
                      "nsIScriptObjectPrincipal");
         nsCOMPtr<nsIPrincipal> systemPrincipal;
         sSecurityManager->GetSystemPrincipal(getter_AddRefs(systemPrincipal));
         const char * category =
           scriptPrincipal->GetPrincipal() == systemPrincipal
           ? "chrome javascript"
           : "content javascript";
 
-        rv = errorObject->InitWithWindowID(mErrorMsg.get(), mFileName.get(),
-                                           mSourceLine.get(),
+        rv = errorObject->InitWithWindowID(mErrorMsg, mFileName,
+                                           mSourceLine,
                                            mLineNr, mColumn, mFlags,
                                            category, mInnerWindowID);
 
         if (NS_SUCCEEDED(rv)) {
           nsCOMPtr<nsIConsoleService> consoleService =
             do_GetService(NS_CONSOLESERVICE_CONTRACTID, &rv);
           if (NS_SUCCEEDED(rv)) {
             consoleService->LogMessage(errorObject);
--- a/dom/browser-element/BrowserElementPromptService.jsm
+++ b/dom/browser-element/BrowserElementPromptService.jsm
@@ -11,16 +11,19 @@ let Cc = Components.classes;
 let Cr = Components.results;
 let Cm = Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
 
 let EXPORTED_SYMBOLS = ["BrowserElementPromptService"];
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 
+const NS_PREFBRANCH_PREFCHANGE_TOPIC_ID = "nsPref:changed";
+const BROWSER_FRAMES_ENABLED_PREF = "dom.mozBrowserFramesEnabled";
+
 function debug(msg) {
   //dump("BrowserElementPromptService - " + msg + "\n");
 }
 
 function BrowserElementPrompt(win, browserElementChild) {
   this._win = win;
   this._browserElementChild = browserElementChild;
 }
@@ -413,17 +416,31 @@ BrowserElementPromptFactory.prototype = 
                                    .QueryInterface(iid);
   }
 };
 
 let BrowserElementPromptService = {
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
                                          Ci.nsISupportsWeakReference]),
 
+  _initialized: false,
+
   _init: function() {
+    if (this._initialized) {
+      return;
+    }
+
+    // If the pref is disabled, do nothing except wait for the pref to change.
+    if (!this._browserFramesPrefEnabled()) {
+      var prefs = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch);
+      prefs.addObserver(BROWSER_FRAMES_ENABLED_PREF, this, /* ownsWeak = */ true);
+      return;
+    }
+
+    this._initialized = true;
     this._browserElementParentMap = new WeakMap();
 
     var os = Cc["@mozilla.org/observer-service;1"].getService(Ci.nsIObserverService);
     os.addObserver(this, "outer-window-destroyed", /* ownsWeak = */ true);
 
     // Wrap the existing @mozilla.org/prompter;1 implementation.
     var contractID = "@mozilla.org/prompter;1";
     var oldCID = Cm.contractIDToCID(contractID);
@@ -482,18 +499,33 @@ let BrowserElementPromptService = {
   },
 
   _observeOuterWindowDestroyed: function(outerWindowID) {
     let id = outerWindowID.QueryInterface(Ci.nsISupportsPRUint64).data;
     debug("observeOuterWindowDestroyed " + id);
     delete this._browserElementChildMap[outerWindowID.data];
   },
 
+  _browserFramesPrefEnabled: function() {
+    var prefs = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch);
+    try {
+      return prefs.getBoolPref(BROWSER_FRAMES_ENABLED_PREF);
+    }
+    catch(e) {
+      return false;
+    }
+  },
+
   observe: function(subject, topic, data) {
     switch(topic) {
+    case NS_PREFBRANCH_PREFCHANGE_TOPIC_ID:
+      if (data == BROWSER_FRAMES_ENABLED_PREF) {
+        this._init();
+      }
+      break;
     case "outer-window-destroyed":
       this._observeOuterWindowDestroyed(subject);
       break;
     default:
       debug("Observed unexpected topic " + topic);
     }
   }
 };
--- a/dom/indexedDB/IndexedDatabaseManager.cpp
+++ b/dom/indexedDB/IndexedDatabaseManager.cpp
@@ -359,16 +359,20 @@ IndexedDatabaseManager::FireWindowOnErro
   nsString errorName;
   if (error) {
     rv = error->GetName(errorName);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   nsScriptErrorEvent event(true, NS_LOAD_ERROR);
   request->FillScriptErrorEvent(&event);
+  NS_ABORT_IF_FALSE(event.fileName,
+                    "FillScriptErrorEvent should give us a non-null string "
+                    "for our error's fileName");
+
   event.errorMsg = errorName.get();
 
   nsCOMPtr<nsIScriptGlobalObject> sgo(do_QueryInterface(aOwner));
   NS_ASSERTION(sgo, "How can this happen?!");
 
   nsEventStatus status = nsEventStatus_eIgnore;
   if (NS_FAILED(sgo->HandleScriptError(&event, &status))) {
     NS_WARNING("Failed to dispatch script error event");
@@ -380,19 +384,19 @@ IndexedDatabaseManager::FireWindowOnErro
     return NS_OK;
   }
 
   // Log an error to the error console.
   nsCOMPtr<nsIScriptError> scriptError =
     do_CreateInstance(NS_SCRIPTERROR_CONTRACTID, &rv);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  if (NS_FAILED(scriptError->InitWithWindowID(event.errorMsg,
-                                              event.fileName,
-                                              nullptr, event.lineNr,
+  if (NS_FAILED(scriptError->InitWithWindowID(errorName,
+                                              nsDependentString(event.fileName),
+                                              EmptyString(), event.lineNr,
                                               0, 0,
                                               "IndexedDB",
                                               aOwner->WindowID()))) {
     NS_WARNING("Failed to init script error!");
     return NS_ERROR_FAILURE;
   }
 
   nsCOMPtr<nsIConsoleService> consoleService =
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -1792,17 +1792,17 @@ ContentParent::RecvScriptError(const nsS
                                       const uint32_t& aFlags,
                                       const nsCString& aCategory)
 {
   nsCOMPtr<nsIConsoleService> svc(do_GetService(NS_CONSOLESERVICE_CONTRACTID));
   if (!svc)
       return true;
 
   nsCOMPtr<nsIScriptError> msg(do_CreateInstance(NS_SCRIPTERROR_CONTRACTID));
-  nsresult rv = msg->Init(aMessage.get(), aSourceName.get(), aSourceLine.get(),
+  nsresult rv = msg->Init(aMessage, aSourceName, aSourceLine,
                           aLineNumber, aColNumber, aFlags, aCategory.get());
   if (NS_FAILED(rv))
     return true;
 
   svc->LogMessage(msg);
   return true;
 }
 
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -198,23 +198,23 @@ TabChild::Init()
   NS_ASSERTION(mWebNav, "nsWebBrowser doesn't implement nsIWebNavigation?");
 
   nsCOMPtr<nsIDocShellTreeItem> docShellItem(do_QueryInterface(mWebNav));
   docShellItem->SetItemType(nsIDocShellTreeItem::typeContentWrapper);
   
   nsCOMPtr<nsIBaseWindow> baseWindow = do_QueryInterface(mWebNav);
   if (!baseWindow) {
     NS_ERROR("mWebNav doesn't QI to nsIBaseWindow");
-    return false;
+    return NS_ERROR_FAILURE;
   }
 
   mWidget = nsIWidget::CreatePuppetWidget(this);
   if (!mWidget) {
     NS_ERROR("couldn't create fake widget");
-    return false;
+    return NS_ERROR_FAILURE;
   }
   mWidget->Create(
     nullptr, 0,              // no parents
     nsIntRect(nsIntPoint(0, 0), nsIntSize(0, 0)),
     nullptr,                 // HandleWidgetEvent
     nullptr                  // nsDeviceContext
   );
 
--- a/dom/sms/tests/marionette/manifest.ini
+++ b/dom/sms/tests/marionette/manifest.ini
@@ -1,7 +1,8 @@
 [DEFAULT]
 b2g = true
 browser = false
 qemu = true
 
 [test_between_emulators.py]
 [test_incoming.js]
+[test_outgoing.js]
new file mode 100644
--- /dev/null
+++ b/dom/sms/tests/marionette/test_outgoing.js
@@ -0,0 +1,143 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+MARIONETTE_TIMEOUT = 20000;
+
+SpecialPowers.setBoolPref("dom.sms.enabled", true);
+SpecialPowers.addPermission("sms", true, document);
+SpecialPowers.addPermission("mobileconnection", true, document);
+
+let sms = window.navigator.mozSms;
+let receiver = "5555552368";
+let body = "Hello SMS world!";
+
+function checkSentMessage(message, sentDate) {
+  ok(message, "message is valid");
+  ok(message instanceof MozSmsMessage,
+     "message is instanceof " + message.constructor);
+
+  ok(message.id, "message.id is valid");
+  is(message.delivery, "sent", "message.delivery");
+  is(message.sender, null, "message.sender");
+  is(message.receiver, receiver, "message.receiver");
+  is(message.body, body, "message.body");
+  ok(message.timestamp instanceof Date,
+     "message.timestamp is instanceof " + message.timestamp.constructor);
+  // SMSC timestamp is in seconds.
+  ok(Math.floor(message.timestamp.getTime() / 1000) >= Math.floor(sentDate / 1000),
+     "sent timestamp is valid");
+  is(message.read, true, "message.read");
+}
+
+let sentMessages = null;
+function checkSameSentMessage(message, now) {
+  checkSentMessage(message, now);
+
+  let sentMessage = sentMessages[message.id];
+  if (!sentMessage) {
+    sentMessages[message.id] = message;
+    return;
+  }
+
+  // The two message instance should be exactly the same, but since we had
+  // already checked most attributes of them, we only compare their
+  // timestamps here.
+  ok(sentMessage.timestamp.getTime() == message.timestamp.getTime(),
+     "the messages got from onsent event and request result must be the same");
+}
+
+function doSendMessageAndCheckSuccess(receivers, callback) {
+  sentMessages = [];
+
+  let now = Date.now();
+  let count;
+
+  function done() {
+    if (--count > 0) {
+      return;
+    }
+
+    sms.removeEventListener("sent", onSmsSent);
+    setTimeout(callback, 0);
+  }
+
+  function onSmsSent(event) {
+    log("SmsManager.onsent event received.");
+
+    ok(event instanceof MozSmsEvent,
+       "event is instanceof " + event.constructor);
+    // Event listener is removed in done().
+
+    checkSameSentMessage(event.message, now);
+
+    done();
+  }
+
+  function onRequestSuccess(event) {
+    log("SmsRequest.onsuccess event received.");
+
+    ok(event.target instanceof MozSmsRequest,
+       "event.target is instanceof " + event.target.constructor);
+    event.target.removeEventListener("success", onRequestSuccess);
+
+    checkSameSentMessage(event.target.result, now);
+
+    done();
+  }
+
+  sms.addEventListener("sent", onSmsSent);
+
+  let result = sms.send(receivers, body);
+  if (Array.isArray(receivers)) {
+    ok(Array.isArray(result),
+      "send() returns an array of requests if receivers is an array");
+    is(result.length, receivers.length, "returned array length");
+
+    // `receivers` is an array, so we have N request.onsuccess and N sms.onsent.
+    count = result.length * 2;
+
+    for (let i = 0; i < result.length; i++) {
+      let request = result[i];
+      ok(request instanceof MozSmsRequest,
+         "request is instanceof " + request.constructor);
+      request.addEventListener("success", onRequestSuccess);
+    }
+
+    return;
+  }
+
+  // `receivers` is not an array, so we have one request.onsuccess and one
+  // sms.onsent.
+  count = 2;
+
+  let request = result;
+  ok(request instanceof MozSmsRequest,
+     "request is instanceof " + request.constructor);
+  request.addEventListener("success", onRequestSuccess);
+}
+
+function testSendMessage() {
+  log("Testing sending message to one receiver:");
+  doSendMessageAndCheckSuccess(receiver, testSendMessageToMultipleRecipients);
+}
+
+function testSendMessageToMultipleRecipients() {
+  log("Testing sending message to multiple receivers:");
+  // TODO: bug 788928 - add test cases for nsIDOMSmsManager.ondelivered event
+  doSendMessageAndCheckSuccess([receiver, receiver], cleanUp);
+}
+
+function cleanUp() {
+  SpecialPowers.removePermission("sms", document);
+  SpecialPowers.removePermission("mobileconnection", document);
+  finish();
+}
+
+waitFor(testSendMessage, function () {
+  let connected = navigator.mozMobileConnection.voice.connected;
+  if (!connected) {
+    log("MobileConnection is not ready yet.");
+  }
+  return connected;
+});
+
--- a/dom/system/gonk/RadioInterfaceLayer.js
+++ b/dom/system/gonk/RadioInterfaceLayer.js
@@ -29,16 +29,17 @@ const DEBUG = RIL.DEBUG_RIL;
 const RADIOINTERFACELAYER_CID =
   Components.ID("{2d831c8d-6017-435b-a80c-e5d422810cea}");
 
 const nsIAudioManager = Ci.nsIAudioManager;
 const nsIRadioInterfaceLayer = Ci.nsIRadioInterfaceLayer;
 
 const kNetworkInterfaceStateChangedTopic = "network-interface-state-changed";
 const kSmsReceivedObserverTopic          = "sms-received";
+const kSmsSentObserverTopic              = "sms-sent";
 const kSmsDeliveredObserverTopic         = "sms-delivered";
 const kMozSettingsChangedObserverTopic   = "mozsettings-changed";
 const kSysMsgListenerReadyObserverTopic  = "system-message-listener-ready";
 const DOM_SMS_DELIVERY_RECEIVED          = "received";
 const DOM_SMS_DELIVERY_SENT              = "sent";
 
 const RIL_IPC_MSG_NAMES = [
   "RIL:GetRilContext",
@@ -1013,16 +1014,18 @@ RadioInterfaceLayer.prototype = {
     if (!options.requestStatusReport) {
       // No more used if STATUS-REPORT not requested.
       delete this._sentSmsEnvelopes[message.envelopeId];
     } else {
       options.sms = sms;
     }
 
     gSmsRequestManager.notifySmsSent(options.requestId, sms);
+
+    Services.obs.notifyObservers(options.sms, kSmsSentObserverTopic, null);
   },
 
   handleSmsDelivered: function handleSmsDelivered(message) {
     debug("handleSmsDelivered: " + JSON.stringify(message));
 
     let options = this._sentSmsEnvelopes[message.envelopeId];
     if (!options) {
       return;
--- a/dom/system/gonk/ril_worker.js
+++ b/dom/system/gonk/ril_worker.js
@@ -2893,17 +2893,17 @@ let RIL = {
       return null;
     }
 
     // An SMS is a string, but we won't read it as such, so let's read the
     // string length and then defer to PDU parsing helper.
     let messageStringLength = Buf.readUint32();
     if (DEBUG) debug("Got new SMS, length " + messageStringLength);
     let message = GsmPDUHelper.readMessage();
-    if (DEBUG) debug(message);
+    if (DEBUG) debug("Got new SMS: " + JSON.stringify(message));
 
     // Read string delimiters. See Buf.readString().
     Buf.readStringDelimiter(length);
 
     return message;
   },
 
   /**
@@ -2953,21 +2953,23 @@ let RIL = {
    * @param length
    *        Length of SMS string in the incoming parcel.
    *
    * @return A failure cause defined in 3GPP 23.040 clause 9.2.3.22.
    */
   _processSmsStatusReport: function _processSmsStatusReport(length) {
     let message = this._processReceivedSms(length);
     if (!message) {
+      if (DEBUG) debug("invalid SMS-STATUS-REPORT");
       return PDU_FCS_UNSPECIFIED;
     }
 
     let options = this._pendingSentSmsMap[message.messageRef];
     if (!options) {
+      if (DEBUG) debug("no pending SMS-SUBMIT message");
       return PDU_FCS_OK;
     }
 
     let status = message.status;
 
     // 3GPP TS 23.040 9.2.3.15 `The MS shall interpret any reserved values as
     // "Service Rejected"(01100011) but shall store them exactly as received.`
     if ((status >= 0x80)
@@ -2980,22 +2982,24 @@ let RIL = {
         || ((status >= PDU_ST_3_RESERVED_BEGIN)
             && (status < PDU_ST_3_SC_SPECIFIC_BEGIN))
         ) {
       status = PDU_ST_3_SERVICE_REJECTED;
     }
 
     // Pending. Waiting for next status report.
     if ((status >>> 5) == 0x01) {
+      if (DEBUG) debug("SMS-STATUS-REPORT: delivery still pending");
       return PDU_FCS_OK;
     }
 
     delete this._pendingSentSmsMap[message.messageRef];
 
     if ((status >>> 5) != 0x00) {
+      if (DEBUG) debug("SMS-STATUS-REPORT: delivery failed");
       // It seems unlikely to get a result code for a failure to deliver.
       // Even if, we don't want to do anything with this.
       return PDU_FCS_OK;
     }
 
     if ((options.segmentMaxSeq > 1)
         && (options.segmentSeq < options.segmentMaxSeq)) {
       // Not last segment. Send next segment here.
@@ -3520,16 +3524,17 @@ RIL[REQUEST_SEND_SMS] = function REQUEST
     return;
   }
 
   options.messageRef = Buf.readUint32();
   options.ackPDU = Buf.readString();
   options.errorCode = Buf.readUint32();
 
   if (options.requestStatusReport) {
+    if (DEBUG) debug("waiting SMS-STATUS-REPORT for messageRef " + options.messageRef);
     this._pendingSentSmsMap[options.messageRef] = options;
   }
 
   if ((options.segmentMaxSeq > 1)
       && (options.segmentSeq < options.segmentMaxSeq)) {
     // Not last segment
     if (!options.requestStatusReport) {
       // Status-Report not requested, send next segment here.
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -1068,19 +1068,19 @@ public:
     }
 
     // Otherwise log an error to the error console.
     nsCOMPtr<nsIScriptError> scriptError =
       do_CreateInstance(NS_SCRIPTERROR_CONTRACTID);
     NS_WARN_IF_FALSE(scriptError, "Failed to create script error!");
 
     if (scriptError) {
-      if (NS_FAILED(scriptError->InitWithWindowID(aMessage.get(),
-                                                  aFilename.get(),
-                                                  aLine.get(), aLineNumber,
+      if (NS_FAILED(scriptError->InitWithWindowID(aMessage,
+                                                  aFilename,
+                                                  aLine, aLineNumber,
                                                   aColumnNumber, aFlags,
                                                   "Web Worker",
                                                   aInnerWindowId))) {
         NS_WARNING("Failed to init script error!");
         scriptError = nullptr;
       }
     }
 
--- a/gfx/tests/reftest/reftest.list
+++ b/gfx/tests/reftest/reftest.list
@@ -1,4 +1,4 @@
 # 468496-1 will also detect bugs in video drivers.
-fails-if(/Mac\x20OS\x20X\x2010\.5/.test(http.oscpu)) == 468496-1.html 468496-1-ref.html # bug 486761, 660740
+== 468496-1.html 468496-1-ref.html
 random-if(bug685516) == 611498-1.html 611498-ref.html
 == 709477-1.html 709477-1-ref.html
--- a/image/src/Decoder.cpp
+++ b/image/src/Decoder.cpp
@@ -101,19 +101,19 @@ Decoder::Finish()
     nsCOMPtr<nsIScriptError> errorObject =
       do_CreateInstance(NS_SCRIPTERROR_CONTRACTID);
 
     if (consoleService && errorObject && !HasDecoderError()) {
       nsAutoString msg(NS_LITERAL_STRING("Image corrupt or truncated: ") +
                        NS_ConvertASCIItoUTF16(mImage.GetURIString()));
 
       if (NS_SUCCEEDED(errorObject->InitWithWindowID(
-                         msg.get(),
-                         NS_ConvertUTF8toUTF16(mImage.GetURIString()).get(),
-                         nullptr, 0, 0, nsIScriptError::errorFlag,
+                         msg,
+                         NS_ConvertUTF8toUTF16(mImage.GetURIString()),
+                         EmptyString(), 0, 0, nsIScriptError::errorFlag,
                          "Image", mImage.InnerWindowID()
                        ))) {
         consoleService->LogMessage(errorObject);
       }
     }
 
     // If we only have a data error, see if things are worth salvaging
     bool salvage = !HasDecoderError() && mImage.GetNumFrames();
--- a/js/xpconnect/idl/nsIScriptError.idl
+++ b/js/xpconnect/idl/nsIScriptError.idl
@@ -6,17 +6,17 @@
 /*
  * nsIConsoleMessage subclass for representing JavaScript errors and warnings.
  */
 
 
 #include "nsISupports.idl"
 #include "nsIConsoleMessage.idl"
 
-[scriptable, uuid(c6dd877a-87b6-47cc-968d-90f4514ec65f)]
+[scriptable, uuid(ddc19b34-e22e-46d5-9f7f-c7768a7dc50a)]
 interface nsIScriptError : nsIConsoleMessage
 {
     /** pseudo-flag for default case */
     const unsigned long errorFlag = 0x0;
 
     /** message is warning */
     const unsigned long warningFlag = 0x1;
 
@@ -61,30 +61,30 @@ interface nsIScriptError : nsIConsoleMes
     /* Get the window id this was initialized with.  Zero will be
        returned if init() was used instead of initWithWindowID(). */
     readonly attribute unsigned long long outerWindowID;
 
     /* Get the inner window id this was initialized with.  Zero will be
        returned if init() was used instead of initWithWindowID(). */
     readonly attribute unsigned long long innerWindowID;
 
-    void init(in wstring message,
-              in wstring sourceName,
-              in wstring sourceLine,
+    void init(in AString message,
+              in AString sourceName,
+              in AString sourceLine,
               in uint32_t lineNumber,
               in uint32_t columnNumber,
               in uint32_t flags,
               in string category);
 
     /* This should be called instead of nsIScriptError.init to
        initialize with a window id.  The window id should be for the
        inner window associated with this error. */
-    void initWithWindowID(in wstring message,
-                          in wstring sourceName,
-                          in wstring sourceLine,
+    void initWithWindowID(in AString message,
+                          in AString sourceName,
+                          in AString sourceLine,
                           in uint32_t lineNumber,
                           in uint32_t columnNumber,
                           in uint32_t flags,
                           in string category,
                           in unsigned long long innerWindowID);
 
     AUTF8String toString();
 };
--- a/js/xpconnect/loader/mozJSComponentLoader.cpp
+++ b/js/xpconnect/loader/mozJSComponentLoader.cpp
@@ -120,31 +120,29 @@ mozJSLoaderErrorReporter(JSContext *cx, 
      * Make an nsIScriptError, populate it with information from this
      * error, then log it with the console service.  The UI can then
      * poll the service to update the Error console.
      */
     nsCOMPtr<nsIScriptError> errorObject =
         do_CreateInstance(NS_SCRIPTERROR_CONTRACTID);
 
     if (consoleService && errorObject) {
-        /*
-         * Got an error object; prepare appropriate-width versions of
-         * various arguments to it.
-         */
-        NS_ConvertASCIItoUTF16 fileUni(rep->filename);
-
         uint32_t column = rep->uctokenptr - rep->uclinebuf;
 
-        rv = errorObject->Init(reinterpret_cast<const PRUnichar*>
-                                               (rep->ucmessage),
-                               fileUni.get(),
-                               reinterpret_cast<const PRUnichar*>
-                                               (rep->uclinebuf),
-                               rep->lineno, column, rep->flags,
-                               "component javascript");
+        const PRUnichar* ucmessage =
+            static_cast<const PRUnichar*>(rep->ucmessage);
+        const PRUnichar* uclinebuf =
+            static_cast<const PRUnichar*>(rep->uclinebuf);
+
+        rv = errorObject->Init(
+              ucmessage ? nsDependentString(ucmessage) : EmptyString(),
+              NS_ConvertASCIItoUTF16(rep->filename),
+              uclinebuf ? nsDependentString(uclinebuf) : EmptyString(),
+              rep->lineno, column, rep->flags,
+              "component javascript");
         if (NS_SUCCEEDED(rv)) {
             rv = consoleService->LogMessage(errorObject);
             if (NS_SUCCEEDED(rv)) {
                 // We're done!  Skip return to fall thru to stderr
                 // printout, for the benefit of those invoking the
                 // browser with -console
                 // return;
             }
--- a/js/xpconnect/src/XPCComponents.cpp
+++ b/js/xpconnect/src/XPCComponents.cpp
@@ -2779,19 +2779,26 @@ nsXPCComponents_Utils::ReportError(const
     JSErrorReport *err = JS_ErrorFromException(cx, error);
     if (err) {
         // It's a proper JS Error
         nsAutoString fileUni;
         CopyUTF8toUTF16(err->filename, fileUni);
 
         uint32_t column = err->uctokenptr - err->uclinebuf;
 
+        const PRUnichar* ucmessage =
+            static_cast<const PRUnichar*>(err->ucmessage);
+        const PRUnichar* uclinebuf =
+            static_cast<const PRUnichar*>(err->uclinebuf);
+
         nsresult rv = scripterr->InitWithWindowID(
-                static_cast<const PRUnichar*>(err->ucmessage), fileUni.get(),
-                static_cast<const PRUnichar*>(err->uclinebuf), err->lineno,
+                ucmessage ? nsDependentString(ucmessage) : EmptyString(),
+                fileUni,
+                uclinebuf ? nsDependentString(uclinebuf) : EmptyString(),
+                err->lineno,
                 column, err->flags, "XPConnect JavaScript", innerWindowID);
         NS_ENSURE_SUCCESS(rv, NS_OK);
 
         console->LogMessage(scripterr);
         return NS_OK;
     }
 
     // It's not a JS Error object, so we synthesize as best we're able.
@@ -2812,19 +2819,19 @@ nsXPCComponents_Utils::ReportError(const
         frame->GetLineNumber(&lineNo);
     }
 
     const jschar *msgchars = JS_GetStringCharsZ(cx, msgstr);
     if (!msgchars)
         return NS_OK;
 
     nsresult rv = scripterr->InitWithWindowID(
-            reinterpret_cast<const PRUnichar *>(msgchars),
-            NS_ConvertUTF8toUTF16(fileName).get(),
-            nullptr, lineNo, 0, 0, "XPConnect JavaScript", innerWindowID);
+            nsDependentString(static_cast<const PRUnichar *>(msgchars)),
+            NS_ConvertUTF8toUTF16(fileName),
+            EmptyString(), lineNo, 0, 0, "XPConnect JavaScript", innerWindowID);
     NS_ENSURE_SUCCESS(rv, NS_OK);
 
     console->LogMessage(scripterr);
     return NS_OK;
 }
 
 #include "nsIScriptSecurityManager.h"
 #include "nsIURI.h"
--- a/js/xpconnect/src/XPCConvert.cpp
+++ b/js/xpconnect/src/XPCConvert.cpp
@@ -1345,34 +1345,35 @@ XPCConvert::JSErrorToXPCException(XPCCal
                                   const JSErrorReport* report,
                                   nsIException** exceptn)
 {
     nsresult rv = NS_ERROR_FAILURE;
     nsRefPtr<nsScriptError> data;
     if (report) {
         nsAutoString bestMessage;
         if (report && report->ucmessage) {
-            bestMessage = (const PRUnichar *)report->ucmessage;
+            bestMessage = static_cast<const PRUnichar*>(report->ucmessage);
         } else if (message) {
             CopyASCIItoUTF16(message, bestMessage);
         } else {
             bestMessage.AssignLiteral("JavaScript Error");
         }
 
-        data = new nsScriptError();
-        if (!data)
-            return NS_ERROR_OUT_OF_MEMORY;
-
+        const PRUnichar* uclinebuf =
+            static_cast<const PRUnichar*>(report->uclinebuf);
 
-        data->InitWithWindowID(bestMessage.get(),
-                               NS_ConvertASCIItoUTF16(report->filename).get(),
-                               (const PRUnichar *)report->uclinebuf, report->lineno,
-                               report->uctokenptr - report->uclinebuf, report->flags,
-                               "XPConnect JavaScript",
-                               nsJSUtils::GetCurrentlyRunningCodeInnerWindowID(ccx.GetJSContext()));
+        data = new nsScriptError();
+        data->InitWithWindowID(
+            bestMessage,
+            NS_ConvertASCIItoUTF16(report->filename),
+            uclinebuf ? nsDependentString(uclinebuf) : EmptyString(),
+            report->lineno,
+            report->uctokenptr - report->uclinebuf, report->flags,
+            "XPConnect JavaScript",
+            nsJSUtils::GetCurrentlyRunningCodeInnerWindowID(ccx.GetJSContext()));
     }
 
     if (data) {
         nsAutoCString formattedMsg;
         data->ToString(formattedMsg);
 
         rv = ConstructException(NS_ERROR_XPC_JAVASCRIPT_ERROR_WITH_DETAILS,
                                 formattedMsg.get(), ifaceName, methodName,
--- a/js/xpconnect/src/XPCWrappedJSClass.cpp
+++ b/js/xpconnect/src/XPCWrappedJSClass.cpp
@@ -1076,19 +1076,19 @@ nsXPCWrappedJSClass::CheckForException(X
                                 if (location) {
                                     // Get line number w/o checking; 0 is ok.
                                     location->GetLineNumber(&lineNumber);
 
                                     // get a filename.
                                     rv = location->GetFilename(getter_Copies(sourceName));
                                 }
 
-                                rv = scriptError->InitWithWindowID(newMessage.get(),
-                                                                   NS_ConvertASCIItoUTF16(sourceName).get(),
-                                                                   nullptr,
+                                rv = scriptError->InitWithWindowID(newMessage,
+                                                                   NS_ConvertASCIItoUTF16(sourceName),
+                                                                   EmptyString(),
                                                                    lineNumber, 0, 0,
                                                                    "XPConnect JavaScript",
                                                                    nsJSUtils::GetCurrentlyRunningCodeInnerWindowID(cx));
                                 if (NS_FAILED(rv))
                                     scriptError = nullptr;
                             }
                         }
                     }
--- a/js/xpconnect/src/nsScriptError.cpp
+++ b/js/xpconnect/src/nsScriptError.cpp
@@ -86,32 +86,32 @@ nsScriptError::GetFlags(uint32_t *result
 
 NS_IMETHODIMP
 nsScriptError::GetCategory(char **result) {
     *result = ToNewCString(mCategory);
     return NS_OK;
 }
 
 NS_IMETHODIMP
-nsScriptError::Init(const PRUnichar *message,
-                    const PRUnichar *sourceName,
-                    const PRUnichar *sourceLine,
+nsScriptError::Init(const nsAString& message,
+                    const nsAString& sourceName,
+                    const nsAString& sourceLine,
                     uint32_t lineNumber,
                     uint32_t columnNumber,
                     uint32_t flags,
                     const char *category)
 {
     return InitWithWindowID(message, sourceName, sourceLine, lineNumber,
                             columnNumber, flags, category, 0);
 }
 
 NS_IMETHODIMP
-nsScriptError::InitWithWindowID(const PRUnichar *message,
-                                const PRUnichar *sourceName,
-                                const PRUnichar *sourceLine,
+nsScriptError::InitWithWindowID(const nsAString& message,
+                                const nsAString& sourceName,
+                                const nsAString& sourceLine,
                                 uint32_t lineNumber,
                                 uint32_t columnNumber,
                                 uint32_t flags,
                                 const char *category,
                                 uint64_t aInnerWindowID)
 {
     mMessage.Assign(message);
     mSourceName.Assign(sourceName);
--- a/layout/base/nsCSSFrameConstructor.cpp
+++ b/layout/base/nsCSSFrameConstructor.cpp
@@ -159,16 +159,18 @@ NS_NewSVGSwitchFrame(nsIPresShell* aPres
 nsIFrame*
 NS_NewSVGTextFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
 nsIFrame*
 NS_NewSVGTSpanFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
 nsIFrame*
 NS_NewSVGContainerFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
 nsIFrame*
 NS_NewSVGUseFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
+nsIFrame*
+NS_NewSVGViewFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
 extern nsIFrame*
 NS_NewSVGLinearGradientFrame(nsIPresShell *aPresShell, nsStyleContext* aContext);
 extern nsIFrame*
 NS_NewSVGRadialGradientFrame(nsIPresShell *aPresShell, nsStyleContext* aContext);
 extern nsIFrame*
 NS_NewSVGStopFrame(nsIPresShell *aPresShell, nsStyleContext* aContext);
 nsIFrame*
 NS_NewSVGMarkerFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
@@ -4969,16 +4971,17 @@ nsCSSFrameConstructor::FindSVGData(Eleme
     SIMPLE_SVG_CREATE(a, NS_NewSVGAFrame),
     SIMPLE_SVG_CREATE(altGlyph, NS_NewSVGTSpanFrame),
     SIMPLE_SVG_CREATE(text, NS_NewSVGTextFrame),
     SIMPLE_SVG_CREATE(tspan, NS_NewSVGTSpanFrame),
     SIMPLE_SVG_CREATE(linearGradient, NS_NewSVGLinearGradientFrame),
     SIMPLE_SVG_CREATE(radialGradient, NS_NewSVGRadialGradientFrame),
     SIMPLE_SVG_CREATE(stop, NS_NewSVGStopFrame),
     SIMPLE_SVG_CREATE(use, NS_NewSVGUseFrame),
+    SIMPLE_SVG_CREATE(view, NS_NewSVGViewFrame),
     SIMPLE_SVG_CREATE(marker, NS_NewSVGMarkerFrame),
     SIMPLE_SVG_CREATE(image, NS_NewSVGImageFrame),
     SIMPLE_SVG_CREATE(clipPath, NS_NewSVGClipPathFrame),
     SIMPLE_SVG_CREATE(textPath, NS_NewSVGTextPathFrame),
     SIMPLE_SVG_CREATE(filter, NS_NewSVGFilterFrame),
     SIMPLE_SVG_CREATE(pattern, NS_NewSVGPatternFrame),
     SIMPLE_SVG_CREATE(mask, NS_NewSVGMaskFrame),
     SIMPLE_SVG_CREATE(feDistantLight, NS_NewSVGFEUnstyledLeafFrame),
--- a/layout/generic/nsFrameIdList.h
+++ b/layout/generic/nsFrameIdList.h
@@ -154,16 +154,17 @@ FRAME_ID(nsSVGPatternFrame)
 FRAME_ID(nsSVGRadialGradientFrame)
 FRAME_ID(nsSVGStopFrame)
 FRAME_ID(nsSVGSwitchFrame)
 FRAME_ID(nsSVGTextContainerFrame)
 FRAME_ID(nsSVGTextFrame)
 FRAME_ID(nsSVGTextPathFrame)
 FRAME_ID(nsSVGTSpanFrame)
 FRAME_ID(nsSVGUseFrame)
+FRAME_ID(SVGViewFrame)
 FRAME_ID(nsTableCaptionFrame)
 FRAME_ID(nsTableCellFrame)
 FRAME_ID(nsTableColFrame)
 FRAME_ID(nsTableColGroupFrame)
 FRAME_ID(nsTableFrame)
 FRAME_ID(nsTableOuterFrame)
 FRAME_ID(nsTableRowFrame)
 FRAME_ID(nsTableRowGroupFrame)
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/448193-ref.html
@@ -0,0 +1,18 @@
+<!doctype html>
+<html><head>
+<title>3d effect degradation at 1px with border radius</title>
+<style>
+div {
+  width: 50px;
+  height: 50px;
+  margin: 10px;
+  border-width: 1px;
+  border-style: solid;
+  -moz-border-radius: 20px;
+}
+</style>
+<body>
+<div style="border-color: #b30000 #ff7f7f #ff7f7f #b30000"></div>
+<div style="border-color: #ff7f7f #b30000 #b30000 #ff7f7f"></div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/448193.html
@@ -0,0 +1,18 @@
+<!doctype html>
+<html><head>
+<title>3d effect degradation at 1px with border radius</title>
+<style>
+div {
+  width: 50px;
+  height: 50px;
+  margin: 10px;
+  border-width: 1px;
+  border-color: red;
+  -moz-border-radius: 20px;
+}
+</style>
+<body>
+<div style="border-style: inset"></div>
+<div style="border-style: outset"></div>
+</body>
+</html>
--- a/layout/reftests/bugs/reftest.list
+++ b/layout/reftests/bugs/reftest.list
@@ -547,19 +547,19 @@ fails-if(cocoaWidget) == 356774-1.html 3
 == 363706-1.html 363706-1-ref.html
 != 363706-1.html about:blank
 == 363728-1.html 363728-1-ref.html
 == 363728-2.html 363728-2-ref.html
 == 363858-1.html 363858-1-ref.html
 == 363858-2.html 363858-2-ref.html
 == 363858-3.html 363858-3-ref.html
 == 363858-4.html 363858-4-ref.html
-== 363858-5a.html 363858-5-ref.html
+fuzzy-if(OSX==10.8,45,2) == 363858-5a.html 363858-5-ref.html
 == 363858-5b.html 363858-5-ref.html
-== 363858-6a.html 363858-6-ref.html
+fuzzy-if(OSX==10.8,45,2) == 363858-6a.html 363858-6-ref.html
 == 363858-6b.html 363858-6-ref.html
 == 363874.html 363874-ref.html
 == 363874-max-width.html 363874-max-width-ref.html
 == 364066-1.html 364066-1-ref.html
 == 364318-1.xhtml 364318-1-ref.xhtml
 == 364079-1.html 364079-1-ref.html
 == 364861-1.html 364861-1-ref.html
 == 364862-1.html 364862-1-ref.html
@@ -846,17 +846,17 @@ fails-if(winWidget) fails-if(cocoaWidget
 == 402338-1.html 402338-1-ref.html
 == 402567-1.html 402567-1-ref.html
 == 402567-2.html 402567-2-ref.html
 == 402567-3.html 402567-3-ref.html
 == 402567-4.html 402567-4-ref.html
 == 402629-1.html 402629-1-ref.html
 == 402629-2.html 402629-2-ref.html
 == 402629-3.html 402629-3-ref.html
-#== 402807-1.html 402807-1-ref.html # Fails on Mac (qm-xserve01)
+== 402807-1.html 402807-1-ref.html
 == 402940-1.html 402940-1-ref.html
 == 402940-1b.html 402940-1-ref.html
 != 402940-2.html 402940-2-notref.html
 != 402940-3.html 402940-3-notref.html
 == 402950-1.html 402950-1-ref.html
 == 403129-1.html 403129-1-ref.html
 == 403129-2.html 403129-2-ref.html
 == 403129-3.html 403129-3-ref.html
@@ -1162,17 +1162,17 @@ random == 445004-1.html 445004-1-ref.htm
 == 446100-1a.html about:blank
 == 446100-1b.html about:blank
 == 446100-1c.html about:blank
 == 446100-1d.html about:blank
 == 446100-1e.html about:blank
 == 446100-1f.html about:blank
 == 446100-1g.html about:blank
 == 446100-1h.html about:blank
-# == 448193.html 448193-ref.html  # Fails due to 2 small single-pixel differences
+fuzzy(127,2) == 448193.html 448193-ref.html
 # == 448987.html 448987-ref.html  # Disabled for now - it needs privileges
 != 449149-1a.html about:blank
 != 449149-1b.html about:blank
 == 449149-2.html 449149-2-ref.html
 == 449171-1.html 449171-ref.html
 == 449362-1.html 449362-1-ref.html
 == 449519-1.html 449519-1-ref.html
 # == 449653-1.html 449653-1-ref.html # Disabled for now - it needs privileges
@@ -1448,17 +1448,17 @@ random-if(d2d) == 523468-1.html 523468-1
 == 531098-1.html 531098-1-ref.html
 == 531200-1.html 531200-1-ref.html
 == 531371-1.html 531371-1-ref.html
 == 534526-1a.html 534526-1-ref.html
 == 534526-1b.html 534526-1-ref.html
 == 534804-1.html 534804-1-ref.html
 == 534808-1.html 534808-1-ref.html
 == 534808-2.html 534808-2-ref.html
-fails-if(/Mac\x20OS\x20X\x2010\.7/.test(http.oscpu)) == 534919-1.html 534919-1-ref.html # Bug 705044
+fails-if(OSX==10.7) == 534919-1.html 534919-1-ref.html # Bug 705044
 random == 536061.html 536061-ref.html # fixedpoint division in blur code makes this fail
 == 537507-1.xul 537507-1-ref.xul
 == 537507-2.html 537507-2-ref.html
 == 537471-1.html 537471-1-ref.html
 == 538909-1.html 538909-1-ref.html
 == 538935-1.html 538935-1-ref.html
 == 539226-1.html about:blank
 == 539323-1.html 539323-1-ref.html
@@ -1623,17 +1623,17 @@ HTTP(..) != 615121-2.html 615121-2-notre
 == 617242-1.html 617242-1-ref.html
 != 618071.html 618071-notref.html
 == 619117-1.html 619117-1-ref.html
 skip-if(Android) HTTP(..) == 621253-1-externalFilter.html 621253-1-ref.html
 skip-if(Android) == 621253-1-internalFilter.html 621253-1-ref.html
 HTTP(..) == 621253-2-externalFilter.html 621253-2-ref.html
 == 621253-2-internalFilter.html 621253-2-ref.html
 fails-if(Android) HTTP(..) == 619511-1.html 619511-1-ref.html
-random-if(winWidget) == 621918-1.svg 621918-1-ref.svg # 1-pixel diacritic positioning discrepancy in rotated text (may depend on platform fonts)
+random-if(winWidget) fuzzy-if(OSX==10.8,19,17) == 621918-1.svg 621918-1-ref.svg # 1-pixel diacritic positioning discrepancy in rotated text (may depend on platform fonts)
 random-if(winWidget) HTTP(..) == 621918-2.svg 621918-2-ref.svg # same 1px issue as above, and HTTP(..) for filters.svg, used to mask antialiasing issues where glyphs touch
 == 622585-1.html 622585-1-ref.html
 fails-if(Android) == 625409-1.html 625409-1-ref.html
 == 627393-1.html about:blank
 == 630835-1.html about:blank
 == 631352-1.html 631352-1-ref.html
 fails-if(Android) == 632423-1.html 632423-1-ref.html
 skip-if(Android) == 632781-verybig.html 632781-ref.html
--- a/layout/reftests/canvas/reftest.list
+++ b/layout/reftests/canvas/reftest.list
@@ -50,34 +50,34 @@ fails-if(Android) != text-font-lang.html
 == strokeText-path.html strokeText-path-ref.html
 
 # azure quartz uses CGDrawLinearGradient instead of DrawShading
 # so we have less control over degenerate behaviour as tested by this
 # test
 fails-if(azureQuartz) == linear-gradient-1a.html linear-gradient-1-ref.html
 
 # this passes with cairo on 10.7 and 10.8 but not with azure for reasons unknown
-fails-if(/Mac\x20OS\x20X\x2010\.[56]/.test(http.oscpu)||(azureQuartz&&/Mac\x20OS\x20X\x2010\.[78]/.test(http.oscpu))) == linear-gradient-1b.html linear-gradient-1-ref.html
+fails-if(OSX==10.6||(azureQuartz&&(OSX==10.7||OSX==10.8))) == linear-gradient-1b.html linear-gradient-1-ref.html
 
 == zero-dimensions.html zero-dimensions-ref.html
 
 == evenodd-fill-sanity.html data:text/html,<body>Pass
 != evenodd-fill-1.html nonzero-fill-1.html
 == evenodd-fill-1.html evenodd-fill-ref.html
 == evenodd-fill-2.html evenodd-fill-ref.html
 == evenodd-fill-3.html nonzero-fill-2.html
 
 == dash-sanity.html data:text/html,<body>Pass
 fuzzy-if(azureSkia,9,470) random-if(Android) == dash-1.html dash-1-ref.svg  # Bug 668412 (really is android-specific, not IPC-specific)
 
 == ctm-sanity.html data:text/html,<body>Pass
 fails == ctm-singular-sanity.html data:text/html,<body>Pass  # Bug 612033
 == ctm-1.html ctm-1-ref.html
 
-fails-if(/Mac\x20OS\x20X\x2010\.[56]/.test(http.oscpu)) == 672646-alpha-radial-gradient.html 672646-alpha-radial-gradient-ref.html # Bug 673333
+fails-if(OSX==10.6) == 672646-alpha-radial-gradient.html 672646-alpha-radial-gradient-ref.html # Bug 673333
 == 674003-alpha-radial-gradient-superlum.html 674003-alpha-radial-gradient-superlum-ref.html
 
 != 693610-1.html 693610-1-notref.html # bug 693610: multiple glyph runs should not be overprinted
 
 == 726951-shadow-clips.html 726951-shadow-clips-ref.html
 
 == transformed-clip.html transformed-clip-ref.html
 == transformed-gradient.html transformed-gradient-ref.html
--- a/layout/reftests/css-gradients/reftest.list
+++ b/layout/reftests/css-gradients/reftest.list
@@ -92,17 +92,17 @@ fails-if(d2d) == repeating-linear-1b.htm
 == repeating-radial-1c.html repeating-radial-1-ref.html
 == repeating-radial-1d.html repeating-radial-1-ref.html
 == repeating-radial-1e.html repeating-radial-1-ref.html
 == repeating-radial-1f.html repeating-radial-1-ref.html
 == repeating-radial-2a.html repeating-radial-2-ref.html
 == repeating-radial-2b.html repeating-radial-2-ref.html
 == twostops-1a.html twostops-1-ref.html
 == twostops-1b.html twostops-1-ref.html
-fails-if(/Mac\x20OS\x20X\x2010\.[56]/.test(http.oscpu)) == twostops-1c.html twostops-1-ref.html # bug 524173
+fails-if(OSX==10.6) == twostops-1c.html twostops-1-ref.html # bug 524173
 == twostops-1d.html twostops-1-ref.html
 == twostops-1e.html twostops-1-ref.html
 == twostops-1f.html twostops-1-ref.html
 == twostops-1g.html twostops-1-ref.html
 
 # from http://www.xanthir.com/:4bhipd by way of http://a-ja.net/newgrad.html
 fuzzy-if(!contentSameGfxBackendAsCanvas,1,20000) fails-if(Android) == aja-linear-1a.html aja-linear-1-ref.html
 fails-if(!d2d) == aja-linear-1b.html aja-linear-1-ref.html # bug 526694
@@ -115,17 +115,17 @@ fuzzy-if(!contentSameGfxBackendAsCanvas,
 fuzzy-if(!contentSameGfxBackendAsCanvas,1,20000) fails-if(Android) == aja-linear-2b.html aja-linear-2-ref.html
 fails == aja-linear-2c.html aja-linear-2-ref.html # bug 522607
 fails-if(!d2d) == aja-linear-2d.html aja-linear-2-ref.html # bug 526694
 fuzzy-if(!contentSameGfxBackendAsCanvas,1,19999) fails-if(Android) == aja-linear-3a.html aja-linear-3-ref.html
 fuzzy-if(!contentSameGfxBackendAsCanvas,1,19999) fails-if(Android) == aja-linear-3b.html aja-linear-3-ref.html
 fuzzy-if(!contentSameGfxBackendAsCanvas,2,20000) fails-if(Android) == aja-linear-4a.html aja-linear-4-ref.html
 fuzzy-if(!contentSameGfxBackendAsCanvas,2,20000) fails-if(Android) == aja-linear-4b.html aja-linear-4-ref.html
 fuzzy-if(!contentSameGfxBackendAsCanvas,2,20000) fails-if(Android) == aja-linear-5a.html aja-linear-5-ref.html
-fuzzy-if(!contentSameGfxBackendAsCanvas,2,16477) fails-if(Android) fails-if(/Mac\x20OS\x20X\x2010\.5/.test(http.oscpu)) == aja-linear-6a.html aja-linear-6-ref.html # bug 526708
+fuzzy-if(!contentSameGfxBackendAsCanvas,2,16477) fails-if(Android) == aja-linear-6a.html aja-linear-6-ref.html # bug 526708
 fails == aja-linear-6b.html aja-linear-6-ref.html # bug 522607
 == height-dependence-1.html height-dependence-1-ref.html
 fails-if(cocoaWidget) == height-dependence-2.html height-dependence-2-ref.html # bug 535007
 == height-dependence-3.html height-dependence-3-ref.html
 
 fails-if(d2d) == linear-onestopposition-1.html linear-onestopposition-1-ref.html # bug 638664
 == linear-onestopposition-1.html linear-onestopposition-1-ref2.html
 fails-if(d2d) fails-if(cocoaWidget) == radial-onestopposition-1a.html radial-onestopposition-1-ref.html # bug 638664
--- a/layout/reftests/indic-shaping/reftest.list
+++ b/layout/reftests/indic-shaping/reftest.list
@@ -2,21 +2,21 @@ pref(gfx.font_rendering.harfbuzz.scripts
 pref(gfx.font_rendering.harfbuzz.scripts,-1) HTTP(..) != devanagari-1b.html devanagari-1-ref.html
 pref(gfx.font_rendering.harfbuzz.scripts,-1) HTTP(..) == devanagari-2.html devanagari-2-ref.html
 pref(gfx.font_rendering.harfbuzz.scripts,-1) HTTP(..) != devanagari-3a.html devanagari-3-ref.html
 pref(gfx.font_rendering.harfbuzz.scripts,-1) HTTP(..) == devanagari-3b.html devanagari-3-ref.html
 pref(gfx.font_rendering.harfbuzz.scripts,-1) HTTP(..) != devanagari-4.html devanagari-4-notref.html
 
 pref(gfx.font_rendering.harfbuzz.scripts,-1) HTTP(..) == gujarati-1a.html gujarati-1-ref.html
 pref(gfx.font_rendering.harfbuzz.scripts,-1) HTTP(..) != gujarati-1b.html gujarati-1-ref.html
-fails-if(/Mac\x20OS\x20X\x2010\.[56]/.test(http.oscpu)) pref(gfx.font_rendering.harfbuzz.scripts,-1) HTTP(..) == gujarati-2.html gujarati-2-ref.html # bug 760379
+fails-if(OSX==10.6) pref(gfx.font_rendering.harfbuzz.scripts,-1) HTTP(..) == gujarati-2.html gujarati-2-ref.html # bug 760379
 pref(gfx.font_rendering.harfbuzz.scripts,-1) HTTP(..) != gujarati-3a.html gujarati-3-ref.html
 pref(gfx.font_rendering.harfbuzz.scripts,-1) HTTP(..) == gujarati-3b.html gujarati-3-ref.html
 pref(gfx.font_rendering.harfbuzz.scripts,-1) HTTP(..) != gujarati-4.html gujarati-4-notref.html
 
-fails-if(/Mac\x20OS\x20X\x2010\.[56]/.test(http.oscpu)) pref(gfx.font_rendering.harfbuzz.scripts,-1) HTTP(..) == bengali-1a.html bengali-1-ref.html # bug 760379
+fails-if(OSX==10.6) pref(gfx.font_rendering.harfbuzz.scripts,-1) HTTP(..) == bengali-1a.html bengali-1-ref.html # bug 760379
 pref(gfx.font_rendering.harfbuzz.scripts,-1) HTTP(..) != bengali-1b.html bengali-1-ref.html
 pref(gfx.font_rendering.harfbuzz.scripts,-1) HTTP(..) != bengali-2a.html bengali-2-ref.html
-fails-if(/Mac\x20OS\x20X\x2010\.[56]/.test(http.oscpu)) pref(gfx.font_rendering.harfbuzz.scripts,-1) HTTP(..) != bengali-2b.html bengali-2-ref.html # bug 760379
-fails-if(/Mac\x20OS\x20X\x2010\.[56]/.test(http.oscpu)) pref(gfx.font_rendering.harfbuzz.scripts,-1) HTTP(..) == bengali-3a.html bengali-3-ref.html # bug 760379
+fails-if(OSX==10.6) pref(gfx.font_rendering.harfbuzz.scripts,-1) HTTP(..) != bengali-2b.html bengali-2-ref.html # bug 760379
+fails-if(OSX==10.6) pref(gfx.font_rendering.harfbuzz.scripts,-1) HTTP(..) == bengali-3a.html bengali-3-ref.html # bug 760379
 pref(gfx.font_rendering.harfbuzz.scripts,-1) HTTP(..) != bengali-3b.html bengali-3-ref.html
 pref(gfx.font_rendering.harfbuzz.scripts,-1) HTTP(..) != bengali-3c.html bengali-3-ref.html
-fails-if(/Mac\x20OS\x20X\x2010\.[56]/.test(http.oscpu)) pref(gfx.font_rendering.harfbuzz.scripts,-1) HTTP(..) != bengali-3c.html bengali-3b.html # bug 760379
+fails-if(OSX==10.6) pref(gfx.font_rendering.harfbuzz.scripts,-1) HTTP(..) != bengali-3c.html bengali-3b.html # bug 760379
--- a/layout/reftests/list-item/reftest.list
+++ b/layout/reftests/list-item/reftest.list
@@ -1,7 +1,7 @@
-== numbering-1.html numbering-1-ref.html
+fuzzy-if(OSX==10.8,55,4) == numbering-1.html numbering-1-ref.html
 == numbering-2.html numbering-2-ref.html
 == ol-reversed-1a.html ol-reversed-1-ref.html
 asserts(1) == ol-reversed-1b.html ol-reversed-1-ref.html # bug 478135
 == ol-reversed-1c.html ol-reversed-1-ref.html
 == ol-reversed-2.html ol-reversed-2-ref.html
 == ol-reversed-3.html ol-reversed-3-ref.html
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/as-image/img-fragment-1-ref.html
@@ -0,0 +1,18 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <link rel="stylesheet" type="text/css" href="svg-image-util.css" />
+  <script src="svg-image-util.js"></script>
+</head>
+<body>
+  <script>
+      var svgParams = {
+        viewBox: [0, 0, 20, 20],
+        meetOrSlice: "meet"
+      };
+
+      appendSVGArrayWithParams(svgParams, "img");
+  </script>
+  <!-- Body gets populated by script -->
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/as-image/img-fragment-1a.html
@@ -0,0 +1,21 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <link rel="stylesheet" type="text/css" href="svg-image-util.css" />
+  <script src="svg-image-util.js"></script>
+</head>
+<body>
+  <script>
+      var svgParams = {
+        view: {
+          viewBox: [0, 0, 20, 20],
+          meetOrSlice: "meet"
+        },
+        fragmentIdentifier: "view"
+      };
+
+      appendSVGArrayWithParams(svgParams, "img");
+  </script>
+  <!-- Body gets populated by script -->
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/as-image/img-fragment-1b.html
@@ -0,0 +1,19 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <link rel="stylesheet" type="text/css" href="svg-image-util.css" />
+  <script src="svg-image-util.js"></script>
+</head>
+<body>
+  <script>
+      var svgParams = {
+        viewBox: [0, 0, 20, 20],
+        meetOrSlice: "meet",
+        fragmentIdentifier: "view"
+      };
+
+      appendSVGArrayWithParams(svgParams, "img");
+  </script>
+  <!-- Body gets populated by script -->
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/as-image/img-fragment-1c.html
@@ -0,0 +1,23 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <link rel="stylesheet" type="text/css" href="svg-image-util.css" />
+  <script src="svg-image-util.js"></script>
+</head>
+<body>
+  <script>
+      var svgParams = {
+        view: {
+          viewBox: [0, 0, 20, 20],
+          meetOrSlice: "meet"
+        },
+        viewBox: [0, 0, 40, 40],
+        meetOrSlice: "slice",
+        fragmentIdentifier: "view"
+      };
+
+      appendSVGArrayWithParams(svgParams, "img");
+  </script>
+  <!-- Body gets populated by script -->
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/as-image/img-fragment-2-ref.html
@@ -0,0 +1,18 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <link rel="stylesheet" type="text/css" href="svg-image-util.css" />
+  <script src="svg-image-util.js"></script>
+</head>
+<body>
+  <script>
+      var svgParams = {
+        viewBox: [0, 0, 20, 20],
+        meetOrSlice: "slice"
+      };
+
+      appendSVGArrayWithParams(svgParams, "img");
+  </script>
+  <!-- Body gets populated by script -->
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/as-image/img-fragment-2a.html
@@ -0,0 +1,21 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <link rel="stylesheet" type="text/css" href="svg-image-util.css" />
+  <script src="svg-image-util.js"></script>
+</head>
+<body>
+  <script>
+      var svgParams = {
+        view: {
+          viewBox: [0, 0, 20, 20],
+          meetOrSlice: "slice"
+        },
+        fragmentIdentifier: "view"
+      };
+
+      appendSVGArrayWithParams(svgParams, "img");
+  </script>
+  <!-- Body gets populated by script -->
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/as-image/img-fragment-2b.html
@@ -0,0 +1,19 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <link rel="stylesheet" type="text/css" href="svg-image-util.css" />
+  <script src="svg-image-util.js"></script>
+</head>
+<body>
+  <script>
+      var svgParams = {
+        viewBox: [0, 0, 20, 20],
+        meetOrSlice: "slice",
+        fragmentIdentifier: "view"
+      };
+
+      appendSVGArrayWithParams(svgParams, "img");
+  </script>
+  <!-- Body gets populated by script -->
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/as-image/img-fragment-2c.html
@@ -0,0 +1,23 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <link rel="stylesheet" type="text/css" href="svg-image-util.css" />
+  <script src="svg-image-util.js"></script>
+</head>
+<body>
+  <script>
+      var svgParams = {
+        view: {
+          viewBox: [0, 0, 20, 20],
+          meetOrSlice: "slice"
+        },
+        viewBox: [0, 0, 40, 40],
+        meetOrSlice: "meet",
+        fragmentIdentifier: "view"
+      };
+
+      appendSVGArrayWithParams(svgParams, "img");
+  </script>
+  <!-- Body gets populated by script -->
+</body>
+</html>
--- a/layout/reftests/svg/as-image/reftest.list
+++ b/layout/reftests/svg/as-image/reftest.list
@@ -100,16 +100,24 @@ random == img-and-image-1.html img-and-i
 
 # Alternate versions of "only one dimension is non-%-valued" tests, but now
 # with no explicit viewBox, to trigger "synthesize-viewBox" behavior
 == img-novb-height-meet-1.html    img-novb-height-all-1-ref.html
 == img-novb-height-slice-1.html   img-novb-height-all-1-ref.html
 == img-novb-width-meet-1.html     img-novb-width-all-1-ref.html
 == img-novb-width-slice-1.html    img-novb-width-all-1-ref.html
 
+# with with a fragmentIdentifier viewBox
+== img-fragment-1a.html  img-fragment-1-ref.html
+== img-fragment-1b.html  img-fragment-1-ref.html
+== img-fragment-1c.html  img-fragment-1-ref.html
+== img-fragment-2a.html  img-fragment-2-ref.html
+== img-fragment-2b.html  img-fragment-2-ref.html
+== img-fragment-2c.html  img-fragment-2-ref.html
+
 == list-simple-1.html list-simple-1-ref.html
 
 == svg-image-simple-1.svg lime100x100.svg
 == svg-image-simple-2.svg lime100x100.svg
 == svg-image-simple-3.svg lime100x100.svg
 
 # tests for <svg> files that include themselves as an <image>
 == svg-image-recursive-1a.svg  svg-image-recursive-1-ref.svg
--- a/layout/reftests/svg/as-image/svg-image-util.js
+++ b/layout/reftests/svg/as-image/svg-image-util.js
@@ -15,57 +15,78 @@ const ALIGN_VALS = ["none",
 
 // All the possible values of "meetOrSlice"
 const MEETORSLICE_VALS = [ "meet", "slice" ];
 
 /**
  * Generates full data URI for an SVG document, with the given parameters
  * on the SVG element.
  *
- * @param aViewboxArr   An array of four numbers, representing the viewBox
- *                      attribute, or null for no viewBox.
- * @param aWidth        The width attribute, or null for no width.
- * @param aHeight       The height attribute, or null for no height.
- * @param aAlign        The 'align' component of the preserveAspectRatio
- *                      attribute, or null for none.
- * @param aMeetOrSlice  The 'meetOrSlice' component of the
- *                      preserveAspectRatio attribute, or null for
- *                      none. (If non-null, implies non-null value for
- *                      aAlign.)
+ * @param aViewboxArr         An array of four numbers, representing the
+ *                            viewBox attribute, or null for no viewBox.
+ * @param aWidth              The width attribute, or null for no width.
+ * @param aHeight             The height attribute, or null for no height.
+ * @param aAlign              The 'align' component of the
+ *                            preserveAspectRatio attribute, or null for none.
+ * @param aMeetOrSlice        The 'meetOrSlice' component of the
+ *                            preserveAspectRatio attribute, or null for
+ *                            none. (If non-null, implies non-null value for
+ *                            aAlign.)
+ * @param aViewParams         Parameters to use for the view element.
+ * @param aFragmentIdentifier The SVG fragment identifier.
  */
 function generateSVGDataURI(aViewboxArr, aWidth, aHeight,
-                            aAlign, aMeetOrSlice) {
+                            aAlign, aMeetOrSlice,
+                            aViewParams, aFragmentIdentifier) {
   // prefix
   var datauri = "data:image/svg+xml,"
   // Begin the SVG tag
   datauri += "%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20shape-rendering%3D%22crispEdges%22";
 
   // Append the custom chunk from our params
+  // If we're working with views, the align customisation is applied there instead
   datauri += generateSVGAttrsForParams(aViewboxArr, aWidth, aHeight,
-                                       aAlign, aMeetOrSlice);
+                                       aViewParams ? null : aAlign,
+                                       aMeetOrSlice);
+
+  // Add 'font-size' just in case the client wants to use ems
+  datauri += "%20font-size%3D%22" + "10px" + "%22";
+
+  // Put closing right bracket on SVG tag
+  datauri += "%3E";
 
-  // Put closing leftbracket on SVG tag
-  datauri += "%3E";
+  if (aViewParams) {
+    // Give the view the id of the fragment identifier
+    datauri += "%3Cview%20id%3D%22" + aFragmentIdentifier + "%22";
+
+    // Append the custom chunk from our view params
+    datauri += generateSVGAttrsForParams(aViewParams.viewBox, null, null,
+                                         aAlign, aViewParams.meetOrSlice);
+
+    datauri += "%2F%3E";
+  }
 
   // Add the rest of the SVG document
   datauri += "%3Crect%20x%3D%221%22%20y%3D%221%22%20height%3D%2218%22%20width%3D%2218%22%20stroke-width%3D%222%22%20stroke%3D%22black%22%20fill%3D%22yellow%22%2F%3E%3Ccircle%20cx%3D%2210%22%20cy%3D%2210%22%20r%3D%228%22%20style%3D%22fill%3A%20blue%22%2F%3E%3C%2Fsvg%3E";
 
   return datauri;
 }
 
 // Generates just the chunk of a data URI that's relevant to
 // the given params.
 function generateSVGAttrsForParams(aViewboxArr, aWidth, aHeight,
                                    aAlign, aMeetOrSlice) {
   var str = "";
   if (aViewboxArr) {
     str += "%20viewBox%3D%22";
     for (var i in aViewboxArr) {
-        var curVal = aViewboxArr[i];
-        str += curVal + "%20";
+        str += aViewboxArr[i];
+        if (i != aViewboxArr.length - 1) {
+          str += "%20";
+        }
     }
     str += "%22";
   }
   if (aWidth) {
     str += "%20width%3D%22"  + aWidth  + "%22";
   }
   if (aHeight) {
     str += "%20height%3D%22" + aHeight + "%22";
@@ -73,19 +94,16 @@ function generateSVGAttrsForParams(aView
   if (aAlign) {
     str += "%20preserveAspectRatio%3D%22" + aAlign;
     if (aMeetOrSlice) {
       str += "%20" + aMeetOrSlice;
     }
     str += "%22";
   }
 
-  // Add 'font-size' just in case the client wants to use ems
-  str += "%20font-size%3D%22" + "10px" + "%22";
-
   return str;
 }
 
 // Returns a newly-generated element with the given tagname, the given URI
 // for its |src| attribute, and the given width & height values.
 function generateHostNode(aHostNodeTagName, aUri,
                           aHostNodeWidth, aHostNodeHeight) {
   var elem = document.createElement(aHostNodeTagName);
@@ -127,17 +145,23 @@ function appendSVGSubArrayWithParams(aSV
     if (!aSVGParams.meetOrSlice) {
       alignVal = "none";
     }
 
     // Generate the Data URI
     var uri = generateSVGDataURI(aSVGParams.viewBox,
                                  aSVGParams.width, aSVGParams.height,
                                  alignVal,
-                                 aSVGParams.meetOrSlice);
+                                 aSVGParams.meetOrSlice,
+                                 aSVGParams.view,
+                                 aSVGParams.fragmentIdentifier);
+
+    if (aSVGParams.fragmentIdentifier) {
+      uri += "#" + aSVGParams.fragmentIdentifier;
+    }
 
     // Generate & append the host node element
     var hostNode = generateHostNode(aHostNodeTagName, uri,
                                     aHostNodeWidth, aHostNodeHeight);
     rootNode.appendChild(hostNode);
 
     // Cosmetic: Add a newline when we get halfway through the ALIGN_VALS
     // and then again when we reach the end
--- a/layout/reftests/svg/reftest.list
+++ b/layout/reftests/svg/reftest.list
@@ -252,17 +252,17 @@ random-if(gtk2Widget) == text-font-weigh
 random-if(winWidget) == text-gradient-02.svg text-gradient-02-ref.svg # see bug 590101
 == text-gradient-03.svg pass.svg
 == text-in-link-01.svg text-in-link-01-ref.svg
 == text-in-link-02.svg text-in-link-02-ref.svg
 == text-in-link-03.svg text-in-link-03-ref.svg
 # Tests for bug 546813: sanity-check using HTML text, then test SVG behavior.
 fails-if(Android) != text-language-00.xhtml text-language-00-ref.xhtml
 fails-if(Android) random-if(gtk2Widget) != text-language-01.xhtml text-language-01-ref.xhtml # Fails on Linux tryserver due to lack of CJK fonts.
-== text-layout-01.svg text-layout-01-ref.svg
+fuzzy-if(OSX==10.8,14,11) == text-layout-01.svg text-layout-01-ref.svg
 == text-layout-02.svg text-layout-02-ref.svg
 == text-layout-03.svg text-layout-03-ref.svg
 == text-layout-04.svg text-layout-04-ref.svg
 == text-layout-05.svg text-layout-05-ref.svg
 fuzzy-if(cocoaWidget&&layersGPUAccelerated,1,3) == text-layout-06.svg text-layout-06-ref.svg
 == text-layout-07.svg text-layout-07-ref.svg
 == text-scale-01.svg text-scale-01-ref.svg
 == text-stroke-scaling-01.svg text-stroke-scaling-01-ref.svg
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/smil/anim-view-01.svg
@@ -0,0 +1,35 @@
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+
+<svg xmlns="http://www.w3.org/2000/svg"
+     xmlns:xlink="http://www.w3.org/1999/xlink"
+     class="reftest-wait"
+     viewBox="0 0 100 100" preserveAspectRatio="none">
+
+  <title>Test animation of view on view overriding svg element</title>
+  <script xlink:href="smil-util.js" type="text/javascript"/>
+  <script>
+    window.addEventListener("MozReftestInvalidate", run, false);
+
+    setTimeout(run, 3000); // for non-gecko
+
+    function run() {
+      setTimeAndSnapshot(101, true);
+    }
+  </script>
+
+  <view id="view">
+    <animate attributeName="viewBox"
+             calcMode="linear"
+             begin="100s" dur="2s"
+             values="0 0 100 100;10 10 10 10;0 0 100 100"
+             fill="freeze"/>
+  </view>
+
+  <rect width="100%" height="100%" fill="red"/>
+
+  <rect x="10" y="10" width="10" height="10" fill="lime"/>
+
+</svg>
--- a/layout/reftests/svg/smil/reftest.list
+++ b/layout/reftests/svg/smil/reftest.list
@@ -134,16 +134,17 @@ fuzzy-if(/^Windows\x20NT\x205\.1/.test(h
 == anim-filter-filterUnits-01.svg lime.svg
 
 # animate some boolean attributes:
 == anim-feConvolveMatrix-preserveAlpha-01.svg lime.svg
 
 # animate some viewBox attributes
 == anim-svg-viewBox-01.svg lime.svg
 == anim-svg-viewBox-02.svg lime.svg
+== anim-view-01.svg#view lime.svg
 
 # animate some preserveAspectRatio attributes
 == anim-feImage-preserveAspectRatio-01.svg lime.svg
 == anim-svg-preserveAspectRatio-01.svg lime.svg
 
 # animate some string attributes:
 == anim-filter-href-01.svg lime.svg
 == anim-gradient-href-01.svg lime.svg
--- a/layout/reftests/svg/smil/transform/reftest.list
+++ b/layout/reftests/svg/smil/transform/reftest.list
@@ -6,10 +6,10 @@
 == rotate-angle-1.svg rotate-angle-ref.svg
 == rotate-angle-2.svg rotate-angle-ref.svg
 == rotate-angle-3.svg rotate-angle-ref.svg
 == rotate-angle-4.svg rotate-angle-ref.svg
 == rotate-angle-5.svg rotate-angle-ref.svg
 == scale-1.svg scale-1-ref.svg
 == skew-1.svg skew-1-ref.svg
 random-if(Android&&!browserIsRemote) == translate-clipPath-1.svg lime.svg # bug 760266
-fails-if(/Mac\x20OS\x20X\x2010\.[56]/.test(http.oscpu)) == translate-gradient-1.svg lime.svg
+fails-if(OSX==10.6) == translate-gradient-1.svg lime.svg
 == translate-pattern-1.svg lime.svg
--- a/layout/reftests/transform-3d/reftest.list
+++ b/layout/reftests/transform-3d/reftest.list
@@ -16,18 +16,18 @@ fails-if(cocoaWidget) random-if(/^Window
 == preserve3d-2a.html preserve3d-2-ref.html
 == preserve3d-2b.html preserve3d-2-ref.html
 == preserve3d-2c.html preserve3d-2-ref.html
 == preserve3d-2d.html preserve3d-2-ref.html
 == preserve3d-3a.html preserve3d-3-ref.html
 == preserve3d-4a.html green-rect.html
 == preserve3d-5a.html preserve3d-5-ref.html
 == scale3d-z.html scalez-1-ref.html
-fuzzy-if(d2d&&layersGPUAccelerated,52,680) fails-if(Android) == scale3d-all.html scale3d-1-ref.html # subpixel AA
-fuzzy-if(d2d&&layersGPUAccelerated,52,680) fails-if(Android) == scale3d-all-separate.html scale3d-1-ref.html # subpixel AA
+fuzzy-if(d2d&&layersGPUAccelerated,52,680) fails-if(Android) fuzzy-if(OSX==10.8,145,752) == scale3d-all.html scale3d-1-ref.html # subpixel AA
+fuzzy-if(d2d&&layersGPUAccelerated,52,680) fails-if(Android) fuzzy-if(OSX==10.8,145,752) == scale3d-all-separate.html scale3d-1-ref.html # subpixel AA
 == scale3d-xz.html scale3d-1-ref.html
 == translatez-1a.html translatez-1-ref.html
 != translatez-1b.html translatez-1-ref.html
 == translate3d-1a.html translate3d-1-ref.html
 == matrix3d-1a.html matrix3d-1-ref.html
 == matrix3d-2a.html matrix3d-2-ref.html
 == rotate3d-1a.html rotatex-1-ref.html
 == rotate3d-2a.html rotatey-1-ref.html
--- a/layout/style/nsCSSScanner.cpp
+++ b/layout/style/nsCSSScanner.cpp
@@ -401,19 +401,19 @@ nsCSSScanner::OutputError()
       mWindowIDCached = true;
     }
 
     nsresult rv;
     nsCOMPtr<nsIScriptError> errorObject =
       do_CreateInstance(gScriptErrorFactory, &rv);
 
     if (NS_SUCCEEDED(rv)) {
-      rv = errorObject->InitWithWindowID(mError.get(),
-                                         NS_ConvertUTF8toUTF16(mFileName).get(),
-                                         EmptyString().get(),
+      rv = errorObject->InitWithWindowID(mError,
+                                         NS_ConvertUTF8toUTF16(mFileName),
+                                         EmptyString(),
                                          mErrorLineNumber,
                                          mErrorColNumber,
                                          nsIScriptError::warningFlag,
                                          "CSS Parser",
                                          mInnerWindowID);
       if (NS_SUCCEEDED(rv)) {
         gConsoleService->LogMessage(errorObject);
       }
--- a/layout/style/nsFontFaceLoader.cpp
+++ b/layout/style/nsFontFaceLoader.cpp
@@ -774,19 +774,19 @@ nsUserFontSet::LogMessage(gfxProxyFontEn
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   nsCOMPtr<nsIScriptError> scriptError =
     do_CreateInstance(NS_SCRIPTERROR_CONTRACTID, &rv);
   NS_ENSURE_SUCCESS(rv, rv);
 
   uint64_t innerWindowID = GetPresContext()->Document()->InnerWindowID();
-  rv = scriptError->InitWithWindowID(NS_ConvertUTF8toUTF16(msg).get(),
-                                     href.get(),   // file
-                                     text.get(),   // src line
+  rv = scriptError->InitWithWindowID(NS_ConvertUTF8toUTF16(msg),
+                                     href,         // file
+                                     text,         // src line
                                      0, 0,         // line & column number
                                      aFlags,       // flags
                                      "CSS Loader", // category (make separate?)
                                      innerWindowID);
   if (NS_SUCCEEDED(rv)) {
     console->LogMessage(scriptError);
   }
 
--- a/layout/style/ua.css
+++ b/layout/style/ua.css
@@ -215,17 +215,17 @@
 @media print {
 
   * {
     cursor: default !important;
   }
 
 }
 
-*|*:-moz-full-screen {
+*|*:not(:root):-moz-full-screen {
   position: fixed !important;
   top: 0 !important;
   left: 0 !important;
   right: 0 !important;
   bottom: 0 !important;
   z-index: 2147483647 !important;
   background: black;
   width: 100% !important;
--- a/layout/svg/base/src/Makefile.in
+++ b/layout/svg/base/src/Makefile.in
@@ -16,44 +16,45 @@ LIBXUL_LIBRARY	= 1
 FAIL_ON_WARNINGS = 1
 
 
 CPPSRCS		= \
 		nsSVGAFrame.cpp \
 		nsSVGClipPathFrame.cpp \
 		nsSVGContainerFrame.cpp \
 		nsSVGEffects.cpp \
+		SVGFEContainerFrame.cpp \
+		SVGFEImageFrame.cpp \
+		SVGFELeafFrame.cpp \
+		SVGFEUnstyledLeafFrame.cpp \
 		nsSVGFilterFrame.cpp \
 		nsSVGFilterInstance.cpp \
 		nsSVGForeignObjectFrame.cpp \
 		nsSVGGFrame.cpp \
 		nsSVGGenericContainerFrame.cpp \
 		nsSVGGeometryFrame.cpp \
 		nsSVGGlyphFrame.cpp \
 		nsSVGGradientFrame.cpp \
 		nsSVGImageFrame.cpp \
 		nsSVGInnerSVGFrame.cpp \
 		nsSVGIntegrationUtils.cpp \
-		SVGFEContainerFrame.cpp \
-		SVGFEImageFrame.cpp \
-		SVGFELeafFrame.cpp \
-		SVGFEUnstyledLeafFrame.cpp \
 		nsSVGMarkerFrame.cpp \
 		nsSVGMaskFrame.cpp \
 		nsSVGOuterSVGFrame.cpp \
 		nsSVGPaintServerFrame.cpp \
 		nsSVGPathGeometryFrame.cpp \
 		nsSVGPatternFrame.cpp \
 		nsSVGStopFrame.cpp \
 		nsSVGSwitchFrame.cpp \
 		nsSVGTextContainerFrame.cpp \
 		nsSVGTextFrame.cpp \
 		nsSVGTextPathFrame.cpp \
 		nsSVGTSpanFrame.cpp \
 		nsSVGUseFrame.cpp \
+		SVGViewFrame.cpp \
 		nsSVGUtils.cpp \
 		$(NULL)
 
 include $(topsrcdir)/config/config.mk
 
 # we don't want the shared lib, but we want to force the creation of a static lib.
 FORCE_STATIC_LIB = 1
 
new file mode 100644
--- /dev/null
+++ b/layout/svg/base/src/SVGViewFrame.cpp
@@ -0,0 +1,128 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// Keep in (case-insensitive) order:
+#include "nsFrame.h"
+#include "nsGkAtoms.h"
+#include "nsSVGOuterSVGFrame.h"
+#include "nsSVGSVGElement.h"
+#include "nsSVGViewElement.h"
+
+typedef nsFrame SVGViewFrameBase;
+
+/**
+ * While views are not directly rendered in SVG they can be linked to
+ * and thereby override attributes of an <svg> element via a fragment
+ * identifier. The SVGViewFrame class passes on any attribute changes
+ * the view receives to the overridden <svg> element (if there is one).
+ **/
+class SVGViewFrame : public SVGViewFrameBase
+{
+  friend nsIFrame*
+  NS_NewSVGViewFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
+protected:
+  SVGViewFrame(nsStyleContext* aContext)
+    : SVGViewFrameBase(aContext)
+  {
+    AddStateBits(NS_STATE_SVG_NONDISPLAY_CHILD);
+  }
+
+public:
+  NS_DECL_FRAMEARENA_HELPERS
+
+#ifdef DEBUG
+  NS_IMETHOD Init(nsIContent* aContent,
+                  nsIFrame*   aParent,
+                  nsIFrame*   aPrevInFlow);
+#endif
+
+  virtual bool IsFrameOfType(uint32_t aFlags) const
+  {
+    return SVGViewFrameBase::IsFrameOfType(aFlags & ~(nsIFrame::eSVG));
+  }
+
+#ifdef DEBUG
+  NS_IMETHOD GetFrameName(nsAString& aResult) const
+  {
+    return MakeFrameName(NS_LITERAL_STRING("SVGView"), aResult);
+  }
+#endif
+
+  /**
+   * Get the "type" of the frame
+   *
+   * @see nsGkAtoms::svgFELeafFrame
+   */
+  virtual nsIAtom* GetType() const;
+
+  NS_IMETHOD AttributeChanged(int32_t  aNameSpaceID,
+                              nsIAtom* aAttribute,
+                              int32_t  aModType);
+
+  virtual bool UpdateOverflow() {
+    // We don't maintain a visual overflow rect
+    return false;
+  }
+};
+
+nsIFrame*
+NS_NewSVGViewFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
+{
+  return new (aPresShell) SVGViewFrame(aContext);
+}
+
+NS_IMPL_FRAMEARENA_HELPERS(SVGViewFrame)
+
+#ifdef DEBUG
+NS_IMETHODIMP
+SVGViewFrame::Init(nsIContent* aContent,
+                   nsIFrame* aParent,
+                   nsIFrame* aPrevInFlow)
+{
+  nsCOMPtr<nsIDOMSVGViewElement> elem = do_QueryInterface(aContent);
+  NS_ASSERTION(elem, "Content is not an SVG view");
+
+  return SVGViewFrameBase::Init(aContent, aParent, aPrevInFlow);
+}
+#endif /* DEBUG */
+
+nsIAtom *
+SVGViewFrame::GetType() const
+{
+  return nsGkAtoms::svgViewFrame;
+}
+
+NS_IMETHODIMP
+SVGViewFrame::AttributeChanged(int32_t  aNameSpaceID,
+                               nsIAtom* aAttribute,
+                               int32_t  aModType)
+{
+  // Ignore zoomAndPan as it does not cause the <svg> element to re-render
+    
+  if (aNameSpaceID == kNameSpaceID_None &&
+      (aAttribute == nsGkAtoms::preserveAspectRatio ||
+       aAttribute == nsGkAtoms::viewBox ||
+       aAttribute == nsGkAtoms::viewTarget)) {
+
+    nsSVGOuterSVGFrame *outerSVGFrame = nsSVGUtils::GetOuterSVGFrame(this);
+    NS_ASSERTION(outerSVGFrame->GetContent()->Tag() == nsGkAtoms::svg,
+                 "Expecting an <svg> element");
+
+    nsSVGSVGElement* svgElement =
+      static_cast<nsSVGSVGElement*>(outerSVGFrame->GetContent());
+
+    nsAutoString viewID;
+    mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::id, viewID);
+
+    if (svgElement->IsOverriddenBy(viewID)) {
+      // We're the view that's providing overrides, so pretend that the frame
+      // we're overriding was updated.
+      outerSVGFrame->AttributeChanged(aNameSpaceID, aAttribute, aModType);
+    }
+  }
+
+  return SVGViewFrameBase::AttributeChanged(aNameSpaceID,
+                                            aAttribute, aModType);
+}
--- a/layout/svg/base/src/nsSVGOuterSVGFrame.cpp
+++ b/layout/svg/base/src/nsSVGOuterSVGFrame.cpp
@@ -16,16 +16,17 @@
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsIObjectLoadingContent.h"
 #include "nsRenderingContext.h"
 #include "nsStubMutationObserver.h"
 #include "nsSVGIntegrationUtils.h"
 #include "nsSVGForeignObjectFrame.h"
 #include "nsSVGSVGElement.h"
 #include "nsSVGTextFrame.h"
+#include "nsSVGViewElement.h"
 
 namespace dom = mozilla::dom;
 
 class nsSVGMutationObserver : public nsStubMutationObserver
 {
 public:
   // nsIMutationObserver interface
   NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED
@@ -279,20 +280,29 @@ nsSVGOuterSVGFrame::GetIntrinsicRatio()
       ratio.width = 0;
     }
     if (ratio.height < 0) {
       ratio.height = 0;
     }
     return ratio;
   }
 
-  if (content->HasViewBox()) {
-    const nsSVGViewBoxRect viewbox = content->mViewBox.GetAnimValue();
-    float viewBoxWidth = viewbox.width;
-    float viewBoxHeight = viewbox.height;
+  nsSVGViewElement* viewElement = content->GetCurrentViewElement();
+  const nsSVGViewBoxRect* viewbox = nullptr;
+
+  // The logic here should match HasViewBox().
+  if (viewElement && viewElement->mViewBox.IsExplicitlySet()) {
+    viewbox = &viewElement->mViewBox.GetAnimValue();
+  } else if (content->mViewBox.IsExplicitlySet()) {
+    viewbox = &content->mViewBox.GetAnimValue();
+  }
+
+  if (viewbox) {
+    float viewBoxWidth = viewbox->width;
+    float viewBoxHeight = viewbox->height;
 
     if (viewBoxWidth < 0.0f) {
       viewBoxWidth = 0.0f;
     }
     if (viewBoxHeight < 0.0f) {
       viewBoxHeight = 0.0f;
     }
     return nsSize(NSToCoordRoundWithClamp(viewBoxWidth),
--- a/layout/tools/reftest/reftest.js
+++ b/layout/tools/reftest/reftest.js
@@ -539,16 +539,21 @@ function BuildConditionSandbox(aURL) {
     sandbox.http = { __exposedProps__: {} };
     for each (var prop in [ "userAgent", "appName", "appVersion",
                             "vendor", "vendorSub",
                             "product", "productSub",
                             "platform", "oscpu", "language", "misc" ]) {
         sandbox.http[prop] = hh[prop];
         sandbox.http.__exposedProps__[prop] = "r";
     }
+
+    // Set OSX to the Mac OS X version for Mac, and 0 otherwise.
+    var osxmatch = /Mac OS X (\d+.\d+)$/.exec(hh.oscpu);
+    sandbox.OSX = osxmatch ? parseFloat(osxmatch[1]) : 0;
+
     // see if we have the test plugin available,
     // and set a sandox prop accordingly
     sandbox.haveTestPlugin = false;
 
     var navigator = gContainingWindow.navigator;
     for (var i = 0; i < navigator.mimeTypes.length; i++) {
         if (navigator.mimeTypes[i].type == "application/x-test" &&
             navigator.mimeTypes[i].enabledPlugin != null &&
--- a/mobile/android/base/BrowserApp.java
+++ b/mobile/android/base/BrowserApp.java
@@ -539,25 +539,21 @@ abstract public class BrowserApp extends
                         new Favicons.OnFaviconLoadedListener() {
 
             public void onFaviconLoaded(String pageUrl, Drawable favicon) {
                 // Leave favicon UI untouched if we failed to load the image
                 // for some reason.
                 if (favicon == null)
                     return;
 
-                Log.i(LOGTAG, "Favicon successfully loaded for URL = " + pageUrl);
-
                 // The tab might be pointing to another URL by the time the
                 // favicon is finally loaded, in which case we simply ignore it.
                 if (!tab.getURL().equals(pageUrl))
                     return;
 
-                Log.i(LOGTAG, "Favicon is for current URL = " + pageUrl);
-
                 tab.updateFavicon(favicon);
                 tab.setFaviconLoadId(Favicons.NOT_LOADING);
 
                 if (Tabs.getInstance().isSelectedTab(tab))
                     mBrowserToolbar.setFavicon(tab.getFavicon());
 
                 Tabs.getInstance().notifyListeners(tab, Tabs.TabEvents.FAVICON);
             }
--- a/mobile/android/base/Favicons.java
+++ b/mobile/android/base/Favicons.java
@@ -86,18 +86,16 @@ public class Favicons {
             // Drop table completely
             db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME);
 
             // Recreate database
             onCreate(db);
         }
 
         public String getFaviconUrlForPageUrl(String pageUrl) {
-            Log.d(LOGTAG, "Calling getFaviconUrlForPageUrl() for " + pageUrl);
-
             SQLiteDatabase db = mDbHelper.getReadableDatabase();
 
             SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
             qb.setTables(TABLE_NAME);
 
             Cursor c = qb.query(
                 db,
                 new String[] { COLUMN_FAVICON_URL },
@@ -112,18 +110,16 @@ public class Favicons {
             }
 
             String url = c.getString(c.getColumnIndexOrThrow(COLUMN_FAVICON_URL));
             c.close();
             return url;
         }
 
         public void setFaviconUrlForPageUrl(String pageUrl, String faviconUrl) {
-            Log.d(LOGTAG, "Calling setFaviconUrlForPageUrl() for " + pageUrl);
-
             SQLiteDatabase db = mDbHelper.getWritableDatabase();
 
             ContentValues values = new ContentValues();
             values.put(COLUMN_FAVICON_URL, faviconUrl);
             values.put(COLUMN_PAGE_URL, pageUrl);
 
             db.replace(TABLE_NAME, null, values);
         }
@@ -168,20 +164,16 @@ public class Favicons {
 
         LoadFaviconTask task = new LoadFaviconTask(pageUrl, faviconUrl, listener);
 
         long taskId = task.getId();
         mLoadTasks.put(taskId, task);
 
         task.execute();
 
-        Log.d(LOGTAG, "Calling loadFavicon() with URL = " + pageUrl +
-                        " and favicon URL = " + faviconUrl +
-                        " (" + taskId + ")");
-
         return taskId;
     }
 
     public boolean cancelFaviconLoad(long taskId) {
         Log.d(LOGTAG, "Requesting cancelation of favicon load (" + taskId + ")");
 
         boolean cancelled = false;
         synchronized (mLoadTasks) {
@@ -226,62 +218,49 @@ public class Favicons {
         public LoadFaviconTask(String pageUrl, String faviconUrl, OnFaviconLoadedListener listener) {
             synchronized(this) {
                 mId = ++mNextFaviconLoadId;
             }
 
             mPageUrl = pageUrl;
             mFaviconUrl = faviconUrl;
             mListener = listener;
-
-            Log.d(LOGTAG, "Creating LoadFaviconTask with URL = " + pageUrl +
-                          " and favicon URL = " + faviconUrl);
         }
 
         // Runs in background thread
         private BitmapDrawable loadFaviconFromDb() {
-            Log.d(LOGTAG, "Loading favicon from DB for URL = " + mPageUrl);
-
             ContentResolver resolver = mContext.getContentResolver();
             BitmapDrawable favicon = BrowserDB.getFaviconForUrl(resolver, mPageUrl);
 
-            if (favicon != null)
-                Log.d(LOGTAG, "Loaded favicon from DB successfully for URL = " + mPageUrl);
-
             return favicon;
         }
 
         // Runs in background thread
         private void saveFaviconToDb(BitmapDrawable favicon) {
             // since the Async task can run this on any number of threads in the
             // pool, we need to protect against inserting the same url twice
             synchronized(mDbHelper) {
-                Log.d(LOGTAG, "Saving favicon on browser database for URL = " + mPageUrl);
                 ContentResolver resolver = mContext.getContentResolver();
                 BrowserDB.updateFaviconForUrl(resolver, mPageUrl, favicon);
 
-                Log.d(LOGTAG, "Saving favicon URL for URL = " + mPageUrl);
                 mDbHelper.setFaviconUrlForPageUrl(mPageUrl, mFaviconUrl);
             }
         }
 
         // Runs in background thread
         private BitmapDrawable downloadFavicon(URL faviconUrl) {
-            Log.d(LOGTAG, "Downloading favicon for URL = " + mPageUrl +
-                          " with favicon URL = " + mFaviconUrl);
-
             if (mFaviconUrl.startsWith("jar:jar:")) {
                 return GeckoJarReader.getBitmapDrawable(GeckoApp.mAppContext.getResources(), mFaviconUrl);
             }
 
             URI uri;
             try {
                 uri = faviconUrl.toURI();
             } catch (URISyntaxException e) {
-                Log.d(LOGTAG, "Could not get URI for favicon URL: " + mFaviconUrl);
+                Log.d(LOGTAG, "Could not get URI for favicon");
                 return null;
             }
 
             // only get favicons for HTTP/HTTPS
             String scheme = uri.getScheme();
             if (!"http".equals(scheme) && !"https".equals(scheme))
                 return null;
 
@@ -319,67 +298,58 @@ public class Favicons {
                     pageUrl = new URL(mPageUrl);
 
                     faviconUrl = new URL(pageUrl.getProtocol(), pageUrl.getAuthority(), "/favicon.ico");
                     mFaviconUrl = faviconUrl.toString();
                 } else {
                     faviconUrl = new URL(mFaviconUrl);
                 }
             } catch (MalformedURLException e) {
-                Log.d(LOGTAG, "The provided favicon URL is not valid", e);
+                Log.d(LOGTAG, "The provided favicon URL is not valid");
                 return null;
             }
 
-            Log.d(LOGTAG, "Favicon URL is now: " + mFaviconUrl);
-
             if (isCancelled())
                 return null;
 
             String storedFaviconUrl = mDbHelper.getFaviconUrlForPageUrl(mPageUrl);
             if (storedFaviconUrl != null && storedFaviconUrl.equals(mFaviconUrl)) {
                 image = loadFaviconFromDb();
                 if (image != null)
                     return image;
             }
 
             if (isCancelled())
                 return null;
 
             image = downloadFavicon(faviconUrl);
 
             if (image != null) {
-                Log.d(LOGTAG, "Downloaded favicon successfully for URL = " + mPageUrl);
                 saveFaviconToDb(image);
             }
 
             return image;
         }
 
         @Override
         protected void onPostExecute(final BitmapDrawable image) {
-            Log.d(LOGTAG, "LoadFaviconTask finished for URL = " + mPageUrl +
-                          " (" + mId + ")");
-
             mLoadTasks.remove(mId);
 
             if (mListener != null) {
                 // We want to always run the listener on UI thread
                 GeckoApp.mAppContext.runOnUiThread(new Runnable() {
                     public void run() {
                         mListener.onFaviconLoaded(mPageUrl, image);
                     }
                 });
             }
         }
 
         @Override
         protected void onCancelled() {
-            Log.d(LOGTAG, "LoadFaviconTask cancelled for URL = " + mPageUrl +
-                          " (" + mId + ")");
-
             mLoadTasks.remove(mId);
 
             // Note that we don't call the listener callback if the
             // favicon load is cancelled.
         }
 
         public long getId() {
             return mId;
--- a/mobile/android/base/GeckoApp.java
+++ b/mobile/android/base/GeckoApp.java
@@ -836,23 +836,21 @@ abstract public class GeckoApp
                 LayerView layerView = mLayerView;
                 if (layerView != null && Tabs.getInstance().isSelectedTab(tab)) {
                     layerView.setCheckerboardColor(tab.getCheckerboardColor());
                 }
             } else if (event.equals("DOMTitleChanged")) {
                 final int tabId = message.getInt("tabID");
                 final String title = message.getString("title");
                 handleTitleChanged(tabId, title);
-                Log.i(LOGTAG, "title - " + title);
             } else if (event.equals("DOMLinkAdded")) {
                 final int tabId = message.getInt("tabID");
                 final String rel = message.getString("rel");
                 final String href = message.getString("href");
                 final int size = message.getInt("size");
-                Log.i(LOGTAG, "link rel - " + rel + ", href - " + href + ", size - " + size);
                 handleLinkAdded(tabId, rel, href, size);
             } else if (event.equals("DOMWindowClose")) {
                 final int tabId = message.getInt("tabID");
                 handleWindowClose(tabId);
             } else if (event.equals("log")) {
                 // generic log listener
                 final String msg = message.getString("msg");
                 Log.i(LOGTAG, "Log: " + msg);
@@ -959,17 +957,16 @@ abstract public class GeckoApp
                 final String type = message.getString("shortcutType");
                 GeckoAppShell.removeShortcut(title, url, origin, type);
             } else if (event.equals("WebApps:Open")) {
                 String url = message.getString("uri");
                 String origin = message.getString("origin");
                 Intent intent = GeckoAppShell.getWebAppIntent(url, origin, false);
                 if (intent == null)
                     return;
-                Log.i(LOGTAG, "Open " + url + " (" + origin + ")");
                 startActivity(intent);
             } else if (event.equals("WebApps:Install")) {
                 String name = message.getString("name");
                 String launchPath = message.getString("launchPath");
                 String iconURL = message.getString("iconURL");
                 String uniqueURI = message.getString("uniqueURI");
 
                 // installWebapp will return a File object pointing to the profile directory of the webapp
@@ -1706,17 +1703,16 @@ abstract public class GeckoApp
         }
 
         private void afterLoad() { }
 
         public void run() {
             try {
                 // this class should only be initialized with an intent with non-null data
                 URL url = new URL(mIntent.getData().toString());
-                Log.i(LOGTAG, "xxx - Loading: " + url);
                 // data url should have an http scheme
                 mConnection = (HttpURLConnection) url.openConnection();
                 mConnection.setRequestProperty("User-Agent", getUAStringForHost(url.getHost()));
                 mConnection.setInstanceFollowRedirects(false);
                 mConnection.setRequestMethod("GET");
                 mConnection.connect();
                 afterLoad();
             } catch (Exception e) {
@@ -1827,32 +1823,28 @@ abstract public class GeckoApp
 
         if (Intent.ACTION_MAIN.equals(action)) {
             Log.i(LOGTAG, "Intent : ACTION_MAIN");
             GeckoAppShell.sendEventToGecko(GeckoEvent.createURILoadEvent(""));
         }
         else if (ACTION_LOAD.equals(action)) {
             String uri = intent.getDataString();
             loadUrl(uri, AwesomeBar.Target.CURRENT_TAB);
-            Log.i(LOGTAG,"onNewIntent: " + uri);
         }
         else if (Intent.ACTION_VIEW.equals(action)) {
             String uri = intent.getDataString();
             GeckoAppShell.sendEventToGecko(GeckoEvent.createURILoadEvent(uri));
-            Log.i(LOGTAG,"onNewIntent: " + uri);
         }
         else if (action != null && action.startsWith(ACTION_WEBAPP_PREFIX)) {
             String uri = getURIFromIntent(intent);
             GeckoAppShell.sendEventToGecko(GeckoEvent.createWebappLoadEvent(uri));
-            Log.i(LOGTAG,"Intent : WEBAPP (" + action + ") - " + uri);
         }
         else if (ACTION_BOOKMARK.equals(action)) {
             String uri = getURIFromIntent(intent);
             GeckoAppShell.sendEventToGecko(GeckoEvent.createBookmarkLoadEvent(uri));
-            Log.i(LOGTAG,"Intent : BOOKMARK - " + uri);
         }
         else if (ACTION_ALERT_CALLBACK.equals(action)) {
             String alertName = "";
             String alertCookie = "";
             Uri data = intent.getData();
             if (data != null) {
                 alertName = data.getQueryParameter("name");
                 if (alertName == null)
--- a/mobile/android/base/GeckoProfile.java
+++ b/mobile/android/base/GeckoProfile.java
@@ -364,17 +364,16 @@ public final class GeckoProfile {
         profileSection.setProperty("Path", saltedName);
 
         if (parser.getSection("General") == null) {
             INISection generalSection = new INISection("General");
             generalSection.setProperty("StartWithLastProfile", 1);
             parser.addSection(generalSection);
 
             // only set as default if this is the first profile we're creating
-            Log.i(LOGTAG, "WESJ - SET DEFAULT");
             profileSection.setProperty("Default", 1);
         }
 
         parser.addSection(profileSection);
         parser.write();
 
         return profileDir;
     }
--- a/mobile/android/base/GeckoThread.java
+++ b/mobile/android/base/GeckoThread.java
@@ -73,16 +73,16 @@ public class GeckoThread extends Thread 
 
         // if this isn't the default BrowserApp, send the apps default profile to gecko
         if (!(app instanceof BrowserApp)) {
             String profile = app.getDefaultProfileName();
             args = (args != null ? args : "") + "-P " + profile;
         }
 
         // and then fire us up
-        Log.i(LOGTAG, "RunGecko - URI = " + mUri + " args = " + args);
+        Log.i(LOGTAG, "RunGecko - args = " + args);
         GeckoAppShell.runGecko(app.getApplication().getPackageResourcePath(),
                                args,
                                mUri,
                                type,
                                mRestoreMode);
     }
 }
--- a/mobile/android/base/ProfileMigrator.java
+++ b/mobile/android/base/ProfileMigrator.java
@@ -899,17 +899,17 @@ public class ProfileMigrator {
                 } else {
                     // PNG images can be passed directly. Well, aside
                     // from having to convert them into a byte[].
                     newData = data;
                 }
 
                 mDB.updateFaviconInBatch(mCr, mOperations, url, faviconUrl, faviconGuid, newData);
             } catch (SQLException e) {
-                Log.i(LOGTAG, "Migrating favicon failed: " + mime + " URL: " + url
+                Log.i(LOGTAG, "Migrating favicon failed: " + mime
                       + " error:" + e.getMessage());
             }
         }
 
         protected void doMigrateHistoryBatch(SQLiteBridge db,
                                              int maxEntries, int currentEntries) {
             final ArrayList<String> placesHistory = new ArrayList<String>();
             mOperations = new ArrayList<ContentProviderOperation>();
--- a/mobile/android/base/db/LocalBrowserDB.java
+++ b/mobile/android/base/db/LocalBrowserDB.java
@@ -404,17 +404,17 @@ public class LocalBrowserDB implements B
                                 Bookmarks.URL + " = ? AND " +
                                 Bookmarks.PARENT + " != ?",
                                 new String[] { uri,
                                                String.valueOf(Bookmarks.FIXED_READING_LIST_ID) },
                                 Bookmarks.URL);
             count = c.getCount();
             c.close();
         } catch (NullPointerException e) {
-            Log.e(LOGTAG, "NullPointerException in isBookmark for " + uri);
+            Log.e(LOGTAG, "NullPointerException in isBookmark");
         }
 
         return (count > 0);
     }
 
     public boolean isReadingListItem(ContentResolver cr, String uri) {
         Cursor cursor = cr.query(mBookmarksUriWithProfile,
                                  new String[] { Bookmarks._ID },
--- a/parser/htmlparser/src/nsExpatDriver.cpp
+++ b/parser/htmlparser/src/nsExpatDriver.cpp
@@ -923,19 +923,19 @@ nsExpatDriver::HandleError()
 
   nsAutoString sourceText(mLastLine);
   AppendErrorPointer(colNumber, mLastLine.get(), sourceText);
 
   // Try to create and initialize the script error.
   nsCOMPtr<nsIScriptError> serr(do_CreateInstance(NS_SCRIPTERROR_CONTRACTID));
   nsresult rv = NS_ERROR_FAILURE;
   if (serr) {
-    rv = serr->InitWithWindowID(description.get(),
-                                mURISpec.get(),
-                                mLastLine.get(),
+    rv = serr->InitWithWindowID(description,
+                                mURISpec,
+                                mLastLine,
                                 lineNumber, colNumber,
                                 nsIScriptError::errorFlag, "malformed-xml",
                                 mInnerWindowID);
   }
 
   // If it didn't initialize, we can't do any logging.
   bool shouldReportError = NS_SUCCEEDED(rv);
 
--- a/xpcom/build/nsXPComInit.cpp
+++ b/xpcom/build/nsXPComInit.cpp
@@ -386,19 +386,17 @@ NS_InitXPCOM2(nsIServiceManager* *result
 #endif
 
     NS_TIME_FUNCTION_MARK("Next: startup local file");
 
     NS_StartupLocalFile();
 
     StartupSpecialSystemDirectory();
 
-    rv = nsDirectoryService::RealInit();
-    if (NS_FAILED(rv))
-        return rv;
+    nsDirectoryService::RealInit();
 
     bool value;
 
     if (binDirectory)
     {
         rv = binDirectory->IsDirectory(&value);
 
         if (NS_SUCCEEDED(rv) && value) {
--- a/xpcom/components/ManifestParser.cpp
+++ b/xpcom/components/ManifestParser.cpp
@@ -165,18 +165,18 @@ void LogMessageWithContext(FileLocation 
     return;
   }
 
   nsCOMPtr<nsIConsoleService> console =
     do_GetService(NS_CONSOLESERVICE_CONTRACTID);
   if (!console)
     return;
 
-  nsresult rv = error->Init(NS_ConvertUTF8toUTF16(formatted).get(),
-			    NS_ConvertUTF8toUTF16(file).get(), NULL,
+  nsresult rv = error->Init(NS_ConvertUTF8toUTF16(formatted),
+			    NS_ConvertUTF8toUTF16(file), EmptyString(),
 			    aLineNumber, 0, nsIScriptError::warningFlag,
 			    "chrome registration");
   if (NS_FAILED(rv))
     return;
 
   console->LogMessage(error);
 }
 
--- a/xpcom/io/nsDirectoryService.cpp
+++ b/xpcom/io/nsDirectoryService.cpp
@@ -69,16 +69,18 @@ nsDirectoryService::GetCurrentProcessDir
         return NS_ERROR_FAILURE;
 
     nsresult rv; 
  
     nsCOMPtr<nsIProperties> dirService;
     rv = nsDirectoryService::Create(nullptr, 
                                     NS_GET_IID(nsIProperties), 
                                     getter_AddRefs(dirService));  // needs to be around for life of product
+    if (NS_FAILED(rv))
+        return rv;
 
     if (dirService)
     {
       nsCOMPtr <nsIFile> aLocalFile;
       dirService->Get(NS_XPCOM_INIT_CURRENT_PROCESS_DIR, NS_GET_IID(nsIFile), getter_AddRefs(aLocalFile));
       if (aLocalFile)
       {
         *aFile = aLocalFile;
@@ -252,47 +254,31 @@ static const nsStaticAtom directory_atom
 
 NS_IMETHODIMP
 nsDirectoryService::Init()
 {
     NS_NOTREACHED("nsDirectoryService::Init() for internal use only!");
     return NS_OK;
 }
 
-nsresult
+void
 nsDirectoryService::RealInit()
 {
     NS_ASSERTION(!gService, 
                  "nsDirectoryService::RealInit Mustn't initialize twice!");
 
-    nsresult rv;
-
     nsRefPtr<nsDirectoryService> self = new nsDirectoryService();
-    if (!self)
-        return NS_ERROR_OUT_OF_MEMORY;
-
-    rv = NS_NewISupportsArray(getter_AddRefs(((nsDirectoryService*) self)->mProviders));
-    if (NS_FAILED(rv))
-        return rv;
 
     NS_RegisterStaticAtoms(directory_atoms);
     
     // Let the list hold the only reference to the provider.
     nsAppFileLocationProvider *defaultProvider = new nsAppFileLocationProvider;
-    if (!defaultProvider)
-        return NS_ERROR_OUT_OF_MEMORY;
-    // AppendElement returns true for success.
-    rv = static_cast<bool>(((nsDirectoryService*) self)
-                           ->mProviders->AppendElement(defaultProvider))
-        ? NS_OK : NS_ERROR_FAILURE;
-    if (NS_FAILED(rv))
-        return rv;
+    self->mProviders.AppendElement(defaultProvider);
 
     self.swap(gService);
-    return NS_OK;
 }
 
 bool
 nsDirectoryService::ReleaseValues(nsHashKey* key, void* data, void* closure)
 {
     nsISupports* value = (nsISupports*)data;
     NS_IF_RELEASE(value);
     return true;
@@ -334,57 +320,53 @@ struct FileData
     uuid(aUUID) {}
     
   const char*   property;
   nsISupports*  data;
   bool          persistent;
   const nsIID&  uuid;
 };
 
-static bool FindProviderFile(nsISupports* aElement, void *aData)
+static bool FindProviderFile(nsIDirectoryServiceProvider* aElement,
+                             FileData* aData)
 {
   nsresult rv;
-  FileData* fileData = (FileData*)aData;
-  if (fileData->uuid.Equals(NS_GET_IID(nsISimpleEnumerator)))
-  {
+  if (aData->uuid.Equals(NS_GET_IID(nsISimpleEnumerator))) {
       // Not all providers implement this iface
       nsCOMPtr<nsIDirectoryServiceProvider2> prov2 = do_QueryInterface(aElement);
       if (prov2)
       {
           nsCOMPtr<nsISimpleEnumerator> newFiles;
-          rv = prov2->GetFiles(fileData->property, getter_AddRefs(newFiles));
+          rv = prov2->GetFiles(aData->property, getter_AddRefs(newFiles));
           if (NS_SUCCEEDED(rv) && newFiles) {
-              if (fileData->data) {
+              if (aData->data) {
                   nsCOMPtr<nsISimpleEnumerator> unionFiles;
 
                   NS_NewUnionEnumerator(getter_AddRefs(unionFiles),
-                                        (nsISimpleEnumerator*) fileData->data, newFiles);
+                                        (nsISimpleEnumerator*) aData->data, newFiles);
 
                   if (unionFiles)
-                      unionFiles.swap(* (nsISimpleEnumerator**) &fileData->data);
+                      unionFiles.swap(* (nsISimpleEnumerator**) &aData->data);
               }
               else
               {
-                  NS_ADDREF(fileData->data = newFiles);
+                  NS_ADDREF(aData->data = newFiles);
               }
                   
-              fileData->persistent = false; // Enumerators can never be persistent
+              aData->persistent = false; // Enumerators can never be persistent
               return rv == NS_SUCCESS_AGGREGATE_RESULT;
           }
       }
   }
   else
   {
-      nsCOMPtr<nsIDirectoryServiceProvider> prov = do_QueryInterface(aElement);
-      if (prov)
-      {
-          rv = prov->GetFile(fileData->property, &fileData->persistent, (nsIFile **)&fileData->data);
-          if (NS_SUCCEEDED(rv) && fileData->data)
-              return false;
-      }
+      rv = aElement->GetFile(aData->property, &aData->persistent,
+                             (nsIFile **)&aData->data);
+      if (NS_SUCCEEDED(rv) && aData->data)
+          return false;
   }
 
   return true;
 }
 
 NS_IMETHODIMP
 nsDirectoryService::Get(const char* prop, const nsIID & uuid, void* *result)
 {
@@ -403,17 +385,21 @@ nsDirectoryService::Get(const char* prop
 
         cachedFile->Clone(getter_AddRefs(cloneFile));
         return cloneFile->QueryInterface(uuid, result);
     }
 
     // it is not one of our defaults, lets check any providers
     FileData fileData(prop, uuid);
 
-    mProviders->EnumerateBackwards(FindProviderFile, &fileData);
+    for (int32_t i = mProviders.Length() - 1; i >= 0; i--) {
+        if (!FindProviderFile(mProviders[i], &fileData)) {
+            break;
+        }
+    }
     if (fileData.data)
     {
         if (fileData.persistent)
         {
             Set(prop, static_cast<nsIFile*>(fileData.data));
         }
         nsresult rv = (fileData.data)->QueryInterface(uuid, result);
         NS_RELEASE(fileData.data);  // addref occurs in FindProviderFile()
@@ -475,28 +461,21 @@ nsDirectoryService::Has(const char *prop
     }
     
     return rv;
 }
 
 NS_IMETHODIMP
 nsDirectoryService::RegisterProvider(nsIDirectoryServiceProvider *prov)
 {
-    nsresult rv;
     if (!prov)
         return NS_ERROR_FAILURE;
-    if (!mProviders)
-        return NS_ERROR_NOT_INITIALIZED;
 
-    nsCOMPtr<nsISupports> supports = do_QueryInterface(prov, &rv);
-    if (NS_FAILED(rv)) return rv;
-
-    // AppendElement returns true for success.
-    return static_cast<bool>(mProviders->AppendElement(supports))
-        ? NS_OK : NS_ERROR_FAILURE;
+    mProviders.AppendElement(prov);
+    return NS_OK;
 }
 
 void
 nsDirectoryService::RegisterCategoryProviders()
 {
     nsCOMPtr<nsICategoryManager> catman
         (do_GetService(NS_CATEGORYMANAGER_CONTRACTID));
     if (!catman)
@@ -524,28 +503,21 @@ nsDirectoryService::RegisterCategoryProv
                 RegisterProvider(provider);
         }
     }
 }
 
 NS_IMETHODIMP
 nsDirectoryService::UnregisterProvider(nsIDirectoryServiceProvider *prov)
 {
-    nsresult rv;
     if (!prov)
         return NS_ERROR_FAILURE;
-    if (!mProviders)
-        return NS_ERROR_NOT_INITIALIZED;
 
-    nsCOMPtr<nsISupports> supports = do_QueryInterface(prov, &rv);
-    if (NS_FAILED(rv)) return rv;
-
-    // RemoveElement returns true for success.
-    return static_cast<bool>(mProviders->RemoveElement(supports))
-        ? NS_OK : NS_ERROR_FAILURE;
+    mProviders.RemoveElement(prov);
+    return NS_OK;
 }
 
 // DO NOT ADD ANY LOCATIONS TO THIS FUNCTION UNTIL YOU TALK TO: dougt@netscape.com.
 // This is meant to be a place of xpcom or system specific file locations, not
 // application specific locations.  If you need the later, register a callback for
 // your application.  
 
 NS_IMETHODIMP
--- a/xpcom/io/nsDirectoryService.h
+++ b/xpcom/io/nsDirectoryService.h
@@ -32,30 +32,30 @@ class nsDirectoryService MOZ_FINAL : pub
 
   NS_DECL_NSIDIRECTORYSERVICEPROVIDER
   
   NS_DECL_NSIDIRECTORYSERVICEPROVIDER2
 
   nsDirectoryService();
    ~nsDirectoryService();
 
-  static nsresult RealInit();
+  static void RealInit();
   void RegisterCategoryProviders();
 
   static nsresult
   Create(nsISupports *aOuter, REFNSIID aIID, void **aResult);
 
   static nsDirectoryService* gService;
 
 private:
     nsresult GetCurrentProcessDirectory(nsIFile** aFile);
     
     static bool ReleaseValues(nsHashKey* key, void* data, void* closure);
     nsSupportsHashtable mHashtable;
-    nsCOMPtr<nsISupportsArray> mProviders;
+    nsTArray<nsCOMPtr<nsIDirectoryServiceProvider> > mProviders;
 
 public:
 
 #define DIR_ATOM(name_, value_) static nsIAtom* name_;
 #include "nsDirectoryServiceAtomList.h"
 #undef DIR_ATOM
 
 };