Merge from mozilla-inbound.
authorJan de Mooij <jdemooij@mozilla.com>
Thu, 14 Mar 2013 10:50:28 +0100
changeset 127420 301858db6ab500e433ee2072faca8da7f5fa67ec
parent 127419 a34329707345620838352716f281a13c39039aaa (current diff)
parent 124779 6bbd7ce8308e4aeaeca73b2540f557bab3905681 (diff)
child 127421 725c137c988a161aac0a5c3e545676a4d6ea53a6
push id24503
push userjandemooij@gmail.com
push dateWed, 03 Apr 2013 15:43:00 +0000
treeherderautoland@b5cb88ccd907 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone22.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge from mozilla-inbound.
browser/devtools/commandline/test/browser_cmd_integrate.js
browser/devtools/commandline/test/browser_cmd_screenshot_perwindowpb.js
browser/devtools/commandline/test/browser_dbg_cmd.html
browser/devtools/commandline/test/browser_dbg_cmd.js
browser/devtools/commandline/test/browser_dbg_cmd_break.html
browser/devtools/commandline/test/browser_dbg_cmd_break.js
browser/devtools/commandline/test/helpers_perwindowpb.js
browser/devtools/shared/test/helpers.js
content/svg/content/src/SVGFEPointLightElement.cpp
content/svg/content/src/SVGFEPointLightElement.h
content/svg/content/src/nsSVGFilters.cpp
extensions/pref/autoconfig/public/nsILDAPSyncQuery.idl
extensions/pref/autoconfig/src/nsLDAPSyncQuery.cpp
extensions/pref/autoconfig/src/nsLDAPSyncQuery.h
js/public/MemoryMetrics.h
js/src/Makefile.in
js/src/gc/Root.h
js/src/gc/RootMarking.cpp
js/src/ion/Bailouts.cpp
js/src/ion/BaselineBailouts.cpp
js/src/ion/BaselineCompiler.cpp
js/src/ion/BaselineIC.cpp
js/src/ion/Ion.cpp
js/src/ion/IonBuilder.cpp
js/src/ion/IonCaches.cpp
js/src/ion/IonFrames.cpp
js/src/ion/IonMacroAssembler.cpp
js/src/ion/IonSpewer.cpp
js/src/ion/MIR.h
js/src/ion/ParallelArrayAnalysis.cpp
js/src/ion/arm/MacroAssembler-arm.cpp
js/src/ion/shared/BaselineCompiler-shared.h
js/src/ion/x86/Assembler-x86.h
js/src/jsapi.h
js/src/jscntxt.cpp
js/src/jscntxt.h
js/src/jscompartment.cpp
js/src/jsinfer.cpp
js/src/jsinferinlines.h
js/src/jsobj.cpp
js/src/jsopcode.cpp
js/src/jsscript.cpp
js/src/jsscript.h
js/src/jsscriptinlines.h
js/src/methodjit/Compiler.cpp
js/src/shell/js.cpp
js/src/vm/Debugger.cpp
js/src/vm/ParallelDo.cpp
js/xpconnect/src/XPCJSRuntime.cpp
mobile/android/base/resources/drawable/highlight.xml
modules/libpref/src/init/all.js
--- a/CLOBBER
+++ b/CLOBBER
@@ -10,9 +10,9 @@
 #                  O   <-- Users coming from both parents need to Clobber
 #               /     \
 #          O               O
 #          |               |
 #          O <-- Clobber   O  <-- Clobber
 #
 # Note: The description below will be part of the error message shown to users.
 #
-Bug 825341 appears to need a clobber
+Bug 847890 appears to need a clobber
--- a/accessible/public/Makefile.in
+++ b/accessible/public/Makefile.in
@@ -8,38 +8,14 @@ topsrcdir = @top_srcdir@
 srcdir    = @srcdir@
 VPATH   = @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 MODULE    = accessibility
 GRE_MODULE	= 1
 
-XPIDLSRCS = \
-      nsIAccessibleTypes.idl \
-      nsIAccessibleRetrieval.idl \
-      nsIAccessible.idl \
-      nsIAccessibleApplication.idl \
-      nsIAccessibleRelation.idl \
-      nsIAccessibleRole.idl \
-      nsIAccessibleStates.idl \
-      nsIAccessibleDocument.idl \
-      nsIAccessibleProvider.idl \
-      nsIAccessibleSelectable.idl \
-      nsIAccessibleCursorable.idl \
-      nsIAccessibleEvent.idl \
-      nsIAccessibleEditableText.idl \
-      nsIAccessibleHyperLink.idl \
-      nsIAccessibleHyperText.idl \
-      nsIAccessiblePivot.idl \
-      nsIAccessibleTable.idl \
-      nsIAccessibleText.idl \
-      nsIAccessibleValue.idl \
-      nsIAccessibleImage.idl \
-      nsIXBLAccessible.idl \
-      $(NULL)
-
 EXPORTS		= \
       nsIAccessibilityService.h \
       $(NULL)
 
 include $(topsrcdir)/config/rules.mk
 
--- a/accessible/public/moz.build
+++ b/accessible/public/moz.build
@@ -1,7 +1,32 @@
 # vim: set filetype=python:
 # 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/.
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
     DIRS += ['msaa', 'ia2']
+
+XPIDL_SOURCES += [
+    'nsIAccessible.idl',
+    'nsIAccessibleApplication.idl',
+    'nsIAccessibleCursorable.idl',
+    'nsIAccessibleDocument.idl',
+    'nsIAccessibleEditableText.idl',
+    'nsIAccessibleEvent.idl',
+    'nsIAccessibleHyperLink.idl',
+    'nsIAccessibleHyperText.idl',
+    'nsIAccessibleImage.idl',
+    'nsIAccessiblePivot.idl',
+    'nsIAccessibleProvider.idl',
+    'nsIAccessibleRelation.idl',
+    'nsIAccessibleRetrieval.idl',
+    'nsIAccessibleRole.idl',
+    'nsIAccessibleSelectable.idl',
+    'nsIAccessibleStates.idl',
+    'nsIAccessibleTable.idl',
+    'nsIAccessibleText.idl',
+    'nsIAccessibleTypes.idl',
+    'nsIAccessibleValue.idl',
+    'nsIXBLAccessible.idl',
+]
+
--- a/accessible/src/base/DocManager.cpp
+++ b/accessible/src/base/DocManager.cpp
@@ -347,16 +347,19 @@ DocManager::AddListeners(nsIDocument* aD
 #endif
   }
 }
 
 void
 DocManager::RemoveListeners(nsIDocument* aDocument)
 {
   nsPIDOMWindow* window = aDocument->GetWindow();
+  if (!window)
+    return;
+
   nsIDOMEventTarget* target = window->GetChromeEventHandler();
   nsEventListenerManager* elm = target->GetListenerManager(true);
   elm->RemoveEventListenerByType(this, NS_LITERAL_STRING("pagehide"),
                                  dom::TrustedEventsAtCapture());
 
   elm->RemoveEventListenerByType(this, NS_LITERAL_STRING("DOMContentLoaded"),
                                  dom::TrustedEventsAtCapture());
 }
--- a/accessible/src/generic/ARIAGridAccessible.cpp
+++ b/accessible/src/generic/ARIAGridAccessible.cpp
@@ -351,30 +351,30 @@ ARIAGridAccessible::SelectedRowIndices(n
 
 void
 ARIAGridAccessible::SelectRow(uint32_t aRowIdx)
 {
   AccIterator rowIter(this, filters::GetRow);
 
   Accessible* row = nullptr;
   for (int32_t rowIdx = 0; (row = rowIter.Next()); rowIdx++) {
-    nsresult rv = SetARIASelected(row, rowIdx == aRowIdx);
+    DebugOnly<nsresult> rv = SetARIASelected(row, rowIdx == aRowIdx);
     NS_ASSERTION(NS_SUCCEEDED(rv), "SetARIASelected() Shouldn't fail!");
   }
 }
 
 void
 ARIAGridAccessible::SelectCol(uint32_t aColIdx)
 {
   AccIterator rowIter(this, filters::GetRow);
 
   Accessible* row = nullptr;
   while ((row = rowIter.Next())) {
     // Unselect all cells in the row.
-    nsresult rv = SetARIASelected(row, false);
+    DebugOnly<nsresult> rv = SetARIASelected(row, false);
     NS_ASSERTION(NS_SUCCEEDED(rv), "SetARIASelected() Shouldn't fail!");
 
     // Select cell at the column index.
     Accessible* cell = GetCellInRowAt(row, aColIdx);
     if (cell)
       SetARIASelected(cell, true);
   }
 }
--- a/accessible/src/html/HTMLTableAccessible.cpp
+++ b/accessible/src/html/HTMLTableAccessible.cpp
@@ -708,30 +708,30 @@ HTMLTableAccessible::IsCellSelected(uint
 
   nsTableCellFrame* cellFrame = tableFrame->GetCellFrameAt(aRowIdx, aColIdx);
   return cellFrame ? cellFrame->IsSelected() : false;
 }
 
 void
 HTMLTableAccessible::SelectRow(uint32_t aRowIdx)
 {
-  nsresult rv =
+  DebugOnly<nsresult> rv =
     RemoveRowsOrColumnsFromSelection(aRowIdx,
                                      nsISelectionPrivate::TABLESELECTION_ROW,
                                      true);
   NS_ASSERTION(NS_SUCCEEDED(rv),
                "RemoveRowsOrColumnsFromSelection() Shouldn't fail!");
 
   AddRowOrColumnToSelection(aRowIdx, nsISelectionPrivate::TABLESELECTION_ROW);
 }
 
 void
 HTMLTableAccessible::SelectCol(uint32_t aColIdx)
 {
-  nsresult rv =
+  DebugOnly<nsresult> rv =
     RemoveRowsOrColumnsFromSelection(aColIdx,
                                      nsISelectionPrivate::TABLESELECTION_COLUMN,
                                      true);
   NS_ASSERTION(NS_SUCCEEDED(rv),
                "RemoveRowsOrColumnsFromSelection() Shouldn't fail!");
 
   AddRowOrColumnToSelection(aColIdx, nsISelectionPrivate::TABLESELECTION_COLUMN);
 }
--- a/accessible/src/xul/XULListboxAccessible.cpp
+++ b/accessible/src/xul/XULListboxAccessible.cpp
@@ -364,17 +364,17 @@ XULListboxAccessible::SelectedCells(nsTA
                "Doesn't implement nsIDOMXULMultiSelectControlElement.");
 
   nsCOMPtr<nsIDOMNodeList> selectedItems;
   control->GetSelectedItems(getter_AddRefs(selectedItems));
   if (!selectedItems)
     return;
 
   uint32_t selectedItemsCount = 0;
-  nsresult rv = selectedItems->GetLength(&selectedItemsCount);
+  DebugOnly<nsresult> rv = selectedItems->GetLength(&selectedItemsCount);
   NS_ASSERTION(NS_SUCCEEDED(rv), "GetLength() Shouldn't fail!");
 
   for (uint32_t index = 0; index < selectedItemsCount; index++) {
     nsCOMPtr<nsIDOMNode> itemNode;
     selectedItems->Item(index, getter_AddRefs(itemNode));
     nsCOMPtr<nsIContent> itemContent(do_QueryInterface(itemNode));
     Accessible* item = mDoc->GetAccessible(itemContent);
 
@@ -398,17 +398,17 @@ XULListboxAccessible::SelectedCellIndice
                "Doesn't implement nsIDOMXULMultiSelectControlElement.");
 
   nsCOMPtr<nsIDOMNodeList> selectedItems;
   control->GetSelectedItems(getter_AddRefs(selectedItems));
   if (!selectedItems)
     return;
 
   uint32_t selectedItemsCount = 0;
-  nsresult rv = selectedItems->GetLength(&selectedItemsCount);
+  DebugOnly<nsresult> rv = selectedItems->GetLength(&selectedItemsCount);
   NS_ASSERTION(NS_SUCCEEDED(rv), "GetLength() Shouldn't fail!");
 
   uint32_t colCount = ColCount();
   aCells->SetCapacity(selectedItemsCount * colCount);
   aCells->AppendElements(selectedItemsCount * colCount);
 
   for (uint32_t selItemsIdx = 0, cellsIdx = 0;
        selItemsIdx < selectedItemsCount; selItemsIdx++) {
@@ -447,17 +447,17 @@ XULListboxAccessible::SelectedRowIndices
                "Doesn't implement nsIDOMXULMultiSelectControlElement.");
 
   nsCOMPtr<nsIDOMNodeList> selectedItems;
   control->GetSelectedItems(getter_AddRefs(selectedItems));
   if (!selectedItems)
     return;
 
   uint32_t rowCount = 0;
-  nsresult rv = selectedItems->GetLength(&rowCount);
+  DebugOnly<nsresult> rv = selectedItems->GetLength(&rowCount);
   NS_ASSERTION(NS_SUCCEEDED(rv), "GetLength() Shouldn't fail!");
 
   if (!rowCount)
     return;
 
   aRows->SetCapacity(rowCount);
   aRows->AppendElements(rowCount);
 
--- a/accessible/tests/mochitest/focus/test_focusedChild.html
+++ b/accessible/tests/mochitest/focus/test_focusedChild.html
@@ -52,20 +52,22 @@
       }
     }
 
     gA11yEventDumpToConsole = true;
     var gQueue = null;
 
     function doTest()
     {
+      enableLogging("focus");
       gQueue = new eventQueue();
 
       gQueue.push(new openWnd());
 
+      gQueue.onFinish = function() { disableLogging(); }
       gQueue.invoke(); // Will call SimpleTest.finish();
     }
 
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(doTest);
   </script>
 </head>
 
--- a/accessible/tests/mochitest/focus/test_takeFocus.html
+++ b/accessible/tests/mochitest/focus/test_takeFocus.html
@@ -42,16 +42,17 @@
     ////////////////////////////////////////////////////////////////////////////
     // Test
 
     //gA11yEventDumpToConsole = true; // debug stuff
 
     var gQueue = null;
     function doTest()
     {
+      disableLogging(); // from test_focusedChild
       gQueue = new eventQueue();
 
       gQueue.push(new takeFocusInvoker("aria-link"));
       gQueue.push(new takeFocusInvoker("aria-link2"));
       gQueue.push(new takeFocusInvoker("link"));
       gQueue.push(new takeFocusInvoker("item2"));
       gQueue.push(new takeFocusInvoker("plugin"));
       gQueue.push(new takeFocusInvoker(document));
--- a/accessible/tests/mochitest/text/Makefile.in
+++ b/accessible/tests/mochitest/text/Makefile.in
@@ -11,16 +11,17 @@ relativesrcdir  = accessible/text
 
 include $(DEPTH)/config/autoconf.mk
 
 MOCHITEST_A11Y_FILES = \
 		doc.html \
 		test_doc.html \
 		test_hypertext.html \
 		test_label.xul \
+		test_multiline.html \
 		test_passwords.html \
 		test_selection.html \
 		test_singleline.html \
 		test_whitespaces.html \
 		test_words.html \
 		$(NULL)
 
 include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/accessible/tests/mochitest/text/test_multiline.html
@@ -0,0 +1,564 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <title>nsIAccessibleText getText related function in multiline text</title>
+  <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+  <script type="application/javascript"
+          src="chrome://mochikit/content/MochiKit/packed.js"></script>
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript"
+          src="../common.js"></script>
+  <script type="application/javascript"
+          src="../text.js"></script>
+  <script type="application/javascript">
+
+    function doTest()
+    {
+      SimpleTest.expectAssertions(60);
+
+      // __o__n__e__w__o__r__d__\n
+      //  0  1  2  3  4  5  6  7
+      // __\n
+      //  8
+      // __t__w__o__ __w__o__r__d__s__\n
+      //  9 10 11 12 13 14 15 16 17 18
+
+      // Note: HTML textarea adds an extra "\n" at the end of the text at
+      //  the layount/content level.
+
+      ////////////////////////////////////////////////////////////////////////
+      // getText
+
+      var IDs = ["div", "divbr", "editable", "editablebr", "textarea"];
+      testText(IDs, 0, 19, "oneword\n\ntwo words\n");
+
+      ////////////////////////////////////////////////////////////////////////
+      // getTextAfterOffset
+
+      // BOUNDARY_CHAR
+      testTextAfterOffset(6, BOUNDARY_CHAR, "\n", 7, 8,
+                          "div", kOk, kOk, kOk,
+                          "divbr", kOk, kOk, kOk,
+                          "editable", kOk, kOk, kOk,
+                          "editablebr", kOk, kOk, kOk,
+                          "textarea", kOk, kOk, kOk);
+      testTextAfterOffset(7, BOUNDARY_CHAR, "\n", 8, 9,
+                          "div", kOk, kOk, kOk,
+                          "divbr", kOk, kOk, kOk,
+                          "editable", kOk, kOk, kOk,
+                          "editablebr", kOk, kOk, kOk,
+                          "textarea", kOk, kOk, kOk);
+      testTextAfterOffset(8, BOUNDARY_CHAR, "t", 9, 10,
+                          "div", kOk, kOk, kOk,
+                          "divbr", kOk, kOk, kOk,
+                          "editable", kOk, kOk, kOk,
+                          "editablebr", kOk, kOk, kOk,
+                          "textarea", kOk, kOk, kOk);
+
+      // BOUNDARY_WORD_START
+      testTextAfterOffset(0, BOUNDARY_WORD_START, "two ", 9, 13,
+                          "div", kTodo, kTodo, kTodo,
+                          "divbr", kTodo, kTodo, kTodo,
+                          "editable", kTodo, kTodo, kTodo,
+                          "editablebr", kTodo, kTodo, kTodo,
+                          "textarea", kTodo, kTodo, kTodo);
+      testTextAfterOffset(8, BOUNDARY_WORD_START, "two ", 9, 13,
+                          "div", kTodo, kTodo, kTodo,
+                          "divbr", kTodo, kTodo, kTodo,
+                          "editable", kTodo, kTodo, kTodo,
+                          "editablebr", kTodo, kTodo, kTodo,
+                          "textarea", kTodo, kTodo, kTodo);
+      testTextAfterOffset(9, BOUNDARY_WORD_START, "words\n", 13, 19,
+                          "div", kTodo, kTodo, kTodo,
+                          "divbr", kTodo, kTodo, kTodo,
+                          "editable", kTodo, kTodo, kTodo,
+                          "editablebr", kTodo, kTodo, kTodo,
+                          "textarea", kTodo, kTodo, kTodo);
+
+      // BOUNDARY_WORD_END
+      testTextAfterOffset(0, BOUNDARY_WORD_END, "\n\ntwo", 7, 12,
+                          "div", kTodo, kTodo, kTodo,
+                          "divbr", kTodo, kTodo, kTodo,
+                          "editable", kTodo, kTodo, kTodo,
+                          "editablebr", kTodo, kTodo, kTodo,
+                          "textarea", kTodo, kTodo, kTodo);
+      testTextAfterOffset(6, BOUNDARY_WORD_END, "\n\ntwo", 7, 12,
+                          "div", kTodo, kTodo, kTodo,
+                          "divbr", kTodo, kTodo, kTodo,
+                          "editable", kTodo, kTodo, kTodo,
+                          "editablebr", kTodo, kTodo, kTodo,
+                          "textarea", kTodo, kTodo, kTodo);
+      testTextAfterOffset(7, BOUNDARY_WORD_END, "\n\ntwo", 7, 12,
+                          "div", kOk, kOk, kOk,
+                          "divbr", kOk, kOk, kOk,
+                          "editable", kOk, kOk, kOk,
+                          "editablebr", kOk, kOk, kOk,
+                          "textarea", kOk, kOk, kOk);
+      testTextAfterOffset(8, BOUNDARY_WORD_END, " words", 12, 18,
+                          "div", kTodo, kTodo, kTodo,
+                          "divbr", kTodo, kTodo, kTodo,
+                          "editable", kTodo, kTodo, kTodo,
+                          "editablebr", kTodo, kTodo, kTodo,
+                          "textarea", kTodo, kTodo, kTodo);
+
+      // BOUNDARY_LINE_START
+      testTextAfterOffset(0, BOUNDARY_LINE_START, "\n", 8, 9,
+                          "div", kTodo, kTodo, kTodo,
+                          "divbr", kTodo, kTodo, kTodo,
+                          "editable", kTodo, kTodo, kTodo,
+                          "editablebr", kTodo, kTodo, kTodo,
+                          "textarea", kTodo, kTodo, kTodo);
+      testTextAfterOffset(7, BOUNDARY_LINE_START, "\n", 8, 9,
+                          "div", kOk, kTodo, kTodo,
+                          "divbr", kOk, kTodo, kTodo,
+                          "editable", kOk, kTodo, kTodo,
+                          "editablebr", kOk, kTodo, kTodo,
+                          "textarea", kOk, kTodo, kTodo);
+      testTextAfterOffset(8, BOUNDARY_LINE_START, "two words\n", 9, 19,
+                          "div", kTodo, kTodo, kTodo,
+                          "divbr", kTodo, kTodo, kTodo,
+                          "editable", kTodo, kTodo, kTodo,
+                          "editablebr", kTodo, kTodo, kTodo,
+                          "textarea", kTodo, kTodo, kTodo);
+      testTextAfterOffset(9, BOUNDARY_LINE_START, "", 19, 19,
+                          "div", kTodo, kTodo, kTodo,
+                          "divbr", kTodo, kTodo, kOk,
+                          "editable", kTodo, kTodo, kTodo,
+                          "editablebr", kTodo, kTodo, kOk,
+                          "textarea", kTodo, kTodo, kOk);
+      testTextAfterOffset(19, BOUNDARY_LINE_START, "", 19, 19,
+                          "div", undefined, undefined, undefined,
+                          "divbr", undefined, undefined, undefined,
+                          "editable", undefined, undefined, undefined,
+                          "editablebr", undefined, undefined, undefined,
+                          "textarea", kTodo, undefined, kTodo);
+
+      // BOUNDARY_LINE_END
+      testTextAfterOffset(0, BOUNDARY_LINE_END, "\n", 7, 8,
+                          "div", kTodo, kTodo, kTodo,
+                          "divbr", kTodo, kTodo, kTodo,
+                          "editable", kTodo, kTodo, kTodo,
+                          "editablebr", kTodo, kTodo, kTodo,
+                          "textarea", kTodo, kTodo, kTodo);
+      testTextAfterOffset(7, BOUNDARY_LINE_END, "\n", 7, 8,
+                          "div", kOk, kOk, kOk,
+                          "divbr", kOk, kOk, kOk,
+                          "editable", kOk, kOk, kOk,
+                          "editablebr", kOk, kOk, kOk,
+                          "textarea", kOk, kOk, kOk);
+      testTextAfterOffset(8, BOUNDARY_LINE_END, "\ntwo words", 8, 18,
+                          "div", kTodo, kOk, kTodo,
+                          "divbr", kOk, kOk, kOk,
+                          "editable", kTodo, kOk, kTodo,
+                          "editablebr", kOk, kOk, kOk,
+                          "textarea", kOk, kOk, kOk);
+      testTextAfterOffset(9, BOUNDARY_LINE_END, "\n", 18, 19,
+                          "div", kTodo, kTodo, kTodo,
+                          "divbr", kTodo, kTodo, kTodo,
+                          "editable", kTodo, kTodo, kTodo,
+                          "editablebr", kTodo, kTodo, kTodo,
+                          "textarea", kTodo, kTodo, kTodo);
+      testTextAfterOffset(18, BOUNDARY_LINE_END, "\n", 18, 19,
+                          "div", kTodo, kTodo, kTodo,
+                          "divbr", kTodo, kTodo, kTodo,
+                          "editable", kTodo, kTodo, kTodo,
+                          "editablebr", kTodo, kTodo, kTodo,
+                          "textarea", kTodo, kTodo, kTodo);
+      testTextAfterOffset(19, BOUNDARY_LINE_END, "", 19, 19,
+                          "div", kOk, kTodo, kTodo,
+                          "divbr", kOk, kTodo, kTodo,
+                          "editable", kOk, kTodo, kTodo,
+                          "editablebr", kOk, kTodo, kTodo,
+                          "textarea", kOk, kTodo, kTodo);
+
+      ////////////////////////////////////////////////////////////////////////
+      // getTextBeforeOffset
+
+      // BOUNDARY_CHAR
+      testTextBeforeOffset(8, BOUNDARY_CHAR, "\n", 7, 8,
+                           "div", kOk, kOk, kOk,
+                           "divbr", kOk, kOk, kOk,
+                           "editable", kOk, kOk, kOk,
+                           "editablebr", kOk, kOk, kOk,
+                           "textarea", kOk, kOk, kOk);
+      testTextBeforeOffset(9, BOUNDARY_CHAR, "\n", 8, 9,
+                           "div", kOk, kOk, kOk,
+                           "divbr", kOk, kOk, kOk,
+                           "editable", kOk, kOk, kOk,
+                           "editablebr", kOk, kOk, kOk,
+                           "textarea", kOk, kOk, kOk);
+      testTextBeforeOffset(10, BOUNDARY_CHAR, "t", 9, 10,
+                           "div", kOk, kOk, kOk,
+                           "divbr", kOk, kOk, kOk,
+                           "editable", kOk, kOk, kOk,
+                           "editablebr", kOk, kOk, kOk,
+                           "textarea", kOk, kOk, kOk);
+
+      // BOUNDARY_WORD_START
+      testTextBeforeOffset(0, BOUNDARY_WORD_START, "", 0, 0,
+                           "div", kOk, kOk, kOk,
+                           "divbr", kOk, kOk, kOk,
+                           "editable", kOk, kOk, kOk,
+                           "editablebr", kOk, kOk, kOk,
+                           "textarea", kOk, kOk, kOk);
+      testTextBeforeOffset(7, BOUNDARY_WORD_START, "", 0, 0,
+                           "div", kTodo, kOk, kTodo,
+                           "divbr", kTodo, kOk, kTodo,
+                           "editable", kTodo, kOk, kTodo,
+                           "editablebr", kTodo, kOk, kTodo,
+                           "textarea", kTodo, kOk, kTodo);
+      testTextBeforeOffset(8, BOUNDARY_WORD_START, "", 0, 0,
+                           "div", kTodo, kOk, kTodo,
+                           "divbr", kTodo, kOk, kTodo,
+                           "editable", kTodo, kOk, kTodo,
+                           "editablebr", kTodo, kOk, kTodo,
+                           "textarea", kTodo, kOk, kTodo);
+      testTextBeforeOffset(9, BOUNDARY_WORD_START, "oneword\n\n", 0, 9,
+                           "div", kOk, kOk, kOk,
+                           "divbr", kOk, kOk, kOk,
+                           "editable", kOk, kOk, kOk,
+                           "editablebr", kOk, kOk, kOk,
+                           "textarea", kOk, kOk, kOk);
+      testTextBeforeOffset(13, BOUNDARY_WORD_START, "two ", 9, 13,
+                           "div", kOk, kOk, kOk,
+                           "divbr", kOk, kOk, kOk,
+                           "editable", kOk, kOk, kOk,
+                           "editablebr", kOk, kOk, kOk,
+                           "textarea", kOk, kOk, kOk);
+      testTextBeforeOffset(18, BOUNDARY_WORD_START, "two ", 9, 13,
+                           "div", kTodo, kTodo, kTodo,
+                           "divbr", kTodo, kTodo, kTodo,
+                           "editable", kTodo, kTodo, kTodo,
+                           "editablebr", kTodo, kTodo, kTodo,
+                           "textarea", kTodo, kTodo, kTodo);
+      testTextBeforeOffset(19, BOUNDARY_WORD_START, "two ", 9, 13,
+                           "div", kTodo, kTodo, kTodo,
+                           "divbr", kTodo, kTodo, kTodo,
+                           "editable", kTodo, kTodo, kTodo,
+                           "editablebr", kTodo, kTodo, kTodo,
+                           "textarea", kTodo, kTodo, kTodo);
+
+      // BOUNDARY_WORD_END
+      testTextBeforeOffset(0, BOUNDARY_WORD_END, "", 0, 0,
+                           "div", kOk, kOk, kOk,
+                           "divbr", kOk, kOk, kOk,
+                           "editable", kOk, kOk, kOk,
+                           "editablebr", kOk, kOk, kOk,
+                           "textarea", kOk, kOk, kOk);
+      testTextBeforeOffset(7, BOUNDARY_WORD_END, "", 0, 0,
+                           "div", kTodo, kOk, kTodo,
+                           "divbr", kTodo, kOk, kTodo,
+                           "editable", kTodo, kOk, kTodo,
+                           "editablebr", kTodo, kOk, kTodo,
+                           "textarea", kTodo, kOk, kTodo);
+      testTextBeforeOffset(8, BOUNDARY_WORD_END, "oneword", 0, 7,
+                           "div", kTodo, kTodo, kTodo,
+                           "divbr", kTodo, kOk, kTodo,
+                           "editable", kTodo, kTodo, kTodo,
+                           "editablebr", kTodo, kOk, kTodo,
+                           "textarea", kTodo, kTodo, kTodo);
+      testTextBeforeOffset(9, BOUNDARY_WORD_END, "oneword", 0, 7,
+                           "div", kTodo, kTodo, kTodo,
+                           "divbr", kTodo, kOk, kTodo,
+                           "editable", kTodo, kTodo, kTodo,
+                           "editablebr", kTodo, kOk, kTodo,
+                           "textarea", kTodo, kTodo, kTodo);
+      testTextBeforeOffset(12, BOUNDARY_WORD_END, "oneword", 0, 7,
+                           "div", kTodo, kTodo, kTodo,
+                           "divbr", kTodo, kTodo, kTodo,
+                           "editable", kTodo, kTodo, kTodo,
+                           "editablebr", kTodo, kTodo, kTodo,
+                           "textarea", kTodo, kTodo, kTodo);
+      testTextBeforeOffset(13, BOUNDARY_WORD_END, "\n\ntwo", 7, 12,
+                           "div", kTodo, kTodo, kTodo,
+                           "divbr", kTodo, kTodo, kTodo,
+                           "editable", kTodo, kTodo, kTodo,
+                           "editablebr", kTodo, kTodo, kTodo,
+                           "textarea", kTodo, kTodo, kTodo);
+      testTextBeforeOffset(18, BOUNDARY_WORD_END, "\n\ntwo", 7, 12,
+                           "div", kTodo, kTodo, kTodo,
+                           "divbr", kTodo, kTodo, kTodo,
+                           "editable", kTodo, kTodo, kTodo,
+                           "editablebr", kTodo, kTodo, kTodo,
+                           "textarea", kTodo, kTodo, kTodo);
+      testTextBeforeOffset(19, BOUNDARY_WORD_END, " words", 13, 18,
+                           "div", kTodo, kTodo, kTodo,
+                           "divbr", kTodo, kTodo, kTodo,
+                           "editable", kTodo, kTodo, kTodo,
+                           "editablebr", kTodo, kTodo, kTodo,
+                           "textarea", kTodo, kTodo, kTodo);
+
+      // BOUNDARY_LINE_START
+      testTextBeforeOffset(0, BOUNDARY_LINE_START, "", 0, 0,
+                           "div", kOk, kOk, kOk,
+                           "divbr", kOk, kOk, kOk,
+                           "editable", kOk, kOk, kOk,
+                           "editablebr", kOk, kOk, kOk,
+                           "textarea", kOk, kOk, kOk);
+      testTextBeforeOffset(8, BOUNDARY_LINE_START, "oneword\n", 0, 8,
+                           "div", kTodo, kTodo, kOk,
+                           "divbr", kTodo, kTodo, kOk,
+                           "editable", kTodo, kTodo, kOk,
+                           "editablebr", kTodo, kTodo, kOk,
+                           "textarea", kTodo, kTodo, kOk);
+      testTextBeforeOffset(9, BOUNDARY_LINE_START, "\n", 8, 9,
+                           "div", kTodo, kTodo, kOk,
+                           "divbr", kTodo, kTodo, kOk,
+                           "editable", kTodo, kTodo, kOk,
+                           "editablebr", kTodo, kTodo, kOk,
+                           "textarea", kTodo, kTodo, kOk);
+      testTextBeforeOffset(18, BOUNDARY_LINE_START, "\n", 8, 9,
+                           "div", kTodo, kTodo, kTodo,
+                           "divbr", kTodo, kTodo, kTodo,
+                           "editable", kTodo, kTodo, kTodo,
+                           "editablebr", kTodo, kTodo, kTodo,
+                           "textarea", kTodo, kTodo, kTodo);
+      testTextBeforeOffset(19, BOUNDARY_LINE_START, "two words \n", 9, 19,
+                           "div", kTodo, kOk, kOk,
+                           "divbr", kTodo, kOk, kOk,
+                           "editable", kTodo, kOk, kOk,
+                           "editablebr", kTodo, kOk, kOk,
+                           "textarea", kTodo, kOk, kOk);
+
+      // BOUNDARY_LINE_END
+      testTextBeforeOffset(0, BOUNDARY_LINE_END, "", 0, 0,
+                           "div", kOk, kOk, kOk,
+                           "divbr", kOk, kOk, kOk,
+                           "editable", kOk, kOk, kOk,
+                           "editablebr", kOk, kOk, kOk,
+                           "textarea", kOk, kOk, kOk);
+      testTextBeforeOffset(7, BOUNDARY_LINE_END, "", 0, 0,
+                           "div", kOk, kTodo, kTodo,
+                           "divbr", kOk, kTodo, kTodo,
+                           "editable", kOk, kTodo, kTodo,
+                           "editablebr", kOk, kTodo, kTodo,
+                           "textarea", kOk, kTodo, kTodo);
+      testTextBeforeOffset(8, BOUNDARY_LINE_END, "oneword", 0, 7,
+                           "div", kTodo, kTodo, kTodo,
+                           "divbr", kTodo, kTodo, kTodo,
+                           "editable", kTodo, kTodo, kTodo,
+                           "editablebr", kTodo, kTodo, kTodo,
+                           "textarea", kTodo, kTodo, kTodo);
+      testTextBeforeOffset(9, BOUNDARY_LINE_END, "\n", 7, 8,
+                           "div", kOk, kTodo, kTodo,
+                           "divbr", kOk, kTodo, kTodo,
+                           "editable", kOk, kTodo, kTodo,
+                           "editablebr", kOk, kTodo, kTodo,
+                           "textarea", kOk, kTodo, kTodo);
+      testTextBeforeOffset(18, BOUNDARY_LINE_END, "\n", 7, 8,
+                           "div", kTodo, kTodo, kTodo,
+                           "divbr", kTodo, kTodo, kTodo,
+                           "editable", kTodo, kTodo, kTodo,
+                           "editablebr", kTodo, kTodo, kTodo,
+                           "textarea", kTodo, kTodo, kTodo);
+      testTextBeforeOffset(19, BOUNDARY_LINE_END, "\ntwo words", 8, 18,
+                           "div", kTodo, kTodo, kTodo,
+                           "divbr", kTodo, kTodo, kTodo,
+                           "editable", kTodo, kTodo, kTodo,
+                           "editablebr", kTodo, kTodo, kTodo,
+                           "textarea", kTodo, kTodo, kTodo);
+
+      ////////////////////////////////////////////////////////////////////////
+      // getTextAtOffset
+
+      // BOUNDARY_CHAR
+      testTextAtOffset(7, BOUNDARY_CHAR, "\n", 7, 8,
+                       "div", kOk, kOk, kOk,
+                       "divbr", kOk, kOk, kOk,
+                       "editable", kOk, kOk, kOk,
+                       "editablebr", kOk, kOk, kOk,
+                       "textarea", kOk, kOk, kOk);
+      testTextAtOffset(8, BOUNDARY_CHAR, "\n", 8, 9,
+                       "div", kOk, kOk, kOk,
+                       "divbr", kOk, kOk, kOk,
+                       "editable", kOk, kOk, kOk,
+                       "editablebr", kOk, kOk, kOk,
+                       "textarea", kOk, kOk, kOk);
+      testTextAtOffset(9, BOUNDARY_CHAR, "t", 9, 10,
+                       "div", kOk, kOk, kOk,
+                       "divbr", kOk, kOk, kOk,
+                       "editable", kOk, kOk, kOk,
+                       "editablebr", kOk, kOk, kOk,
+                       "textarea", kOk, kOk, kOk);
+
+      // BOUNDARY_WORD_START
+      testTextAtOffset(0, BOUNDARY_WORD_START, "oneword\n\n", 0, 9,
+                       "div", kTodo, kOk, kTodo,
+                       "divbr", kOk, kOk, kOk,
+                       "editable", kTodo, kOk, kTodo,
+                       "editablebr", kOk, kOk, kOk,
+                       "textarea", kTodo, kOk, kTodo);
+      testTextAtOffset(8, BOUNDARY_WORD_START, "oneword\n\n", 0, 9,
+                       "div", kTodo, kTodo, kOk,
+                       "divbr", kOk, kOk, kOk,
+                       "editable", kTodo, kTodo, kOk,
+                       "editablebr", kOk, kOk, kOk,
+                       "textarea", kTodo, kTodo, kOk);
+      testTextAtOffset(9, BOUNDARY_WORD_START, "two ", 9, 13,
+                       "div", kTodo, kTodo, kTodo,
+                       "divbr", kOk, kOk, kOk,
+                       "editable", kTodo, kTodo, kTodo,
+                       "editablebr", kOk, kOk, kOk,
+                       "textarea", kTodo, kTodo, kTodo);
+      testTextAtOffset(13, BOUNDARY_WORD_START, "words\n", 13, 19,
+                       "div", kOk, kOk, kOk,
+                       "divbr", kOk, kOk, kOk,
+                       "editable", kOk, kOk, kOk,
+                       "editablebr", kOk, kOk, kOk,
+                       "textarea", kOk, kOk, kOk);
+
+      // BOUNDARY_WORD_END
+      testTextAtOffset(0, BOUNDARY_WORD_END, "oneword", 0, 7,
+                       "div", kOk, kOk, kOk,
+                       "divbr", kOk, kOk, kOk,
+                       "editable", kOk, kOk, kOk,
+                       "editablebr", kOk, kOk, kOk,
+                       "textarea", kOk, kOk, kOk);
+      testTextAtOffset(8, BOUNDARY_WORD_END, "\n\ntwo", 7, 12,
+                       "div", kOk, kOk, kOk,
+                       "divbr", kTodo, kTodo, kTodo,
+                       "editable", kOk, kOk, kOk,
+                       "editablebr", kTodo, kTodo, kTodo,
+                       "textarea", kOk, kOk, kOk);
+      testTextAtOffset(9, BOUNDARY_WORD_END, "\n\ntwo", 7, 12,
+                       "div", kTodo, kTodo, kOk,
+                       "divbr", kTodo, kTodo, kTodo,
+                       "editable", kTodo, kTodo, kOk,
+                       "editablebr", kTodo, kTodo, kTodo,
+                       "textarea", kTodo, kTodo, kOk);
+      testTextAtOffset(12, BOUNDARY_WORD_END, "\n\ntwo", 7, 12,
+                       "div", kTodo, kTodo, kTodo,
+                       "divbr", kTodo, kTodo, kTodo,
+                       "editable", kTodo, kTodo, kTodo,
+                       "editablebr", kTodo, kTodo, kTodo,
+                       "textarea", kTodo, kTodo, kTodo);
+      testTextAtOffset(13, BOUNDARY_WORD_END, " words", 12, 18,
+                       "div", kOk, kOk, kOk,
+                       "divbr", kOk, kOk, kOk,
+                       "editable", kOk, kOk, kOk,
+                       "editablebr", kOk, kOk, kOk,
+                       "textarea", kOk, kOk, kOk);
+
+      // BOUNDARY_LINE_START
+      testTextAtOffset(0, BOUNDARY_LINE_START, "oneword\n", 0, 8,
+                       "div", kTodo, kOk, kTodo,
+                       "divbr", kOk, kOk, kOk,
+                       "editable", kTodo, kOk, kTodo,
+                       "editablebr", kOk, kOk, kOk,
+                       "textarea", kTodo, kOk, kTodo);
+      testTextAtOffset(7, BOUNDARY_LINE_START, "oneword\n", 0, 8,
+                       "div", kOk, kOk, kOk,
+                       "divbr", kOk, kOk, kOk,
+                       "editable", kOk, kOk, kOk,
+                       "editablebr", kOk, kOk, kOk,
+                       "textarea", kOk, kOk, kOk);
+      testTextAtOffset(8, BOUNDARY_LINE_START, "\n", 8, 9,
+                       "div", kOk, kOk, kOk,
+                       "divbr", kOk, kOk, kOk,
+                       "editable", kOk, kOk, kOk,
+                       "editablebr", kOk, kOk, kOk,
+                       "textarea", kOk, kOk, kOk);
+      testTextAtOffset(9, BOUNDARY_LINE_START, "two words\n", 9, 19,
+                       "div", kTodo, kOk, kTodo,
+                       "divbr", kOk, kOk, kOk,
+                       "editable", kTodo, kOk, kTodo,
+                       "editablebr", kOk, kOk, kOk,
+                       "textarea", kOk, kOk, kOk);
+      testTextAtOffset(13, BOUNDARY_LINE_START, "two words\n", 9, 19,
+                       "div", kTodo, kOk, kTodo,
+                       "divbr", kOk, kOk, kOk,
+                       "editable", kTodo, kOk, kTodo,
+                       "editablebr", kOk, kOk, kOk,
+                       "textarea", kOk, kOk, kOk);
+      testTextAtOffset(18, BOUNDARY_LINE_START, "two words\n", 9, 19,
+                       "div", kOk, kOk, kOk,
+                       "divbr", kOk, kOk, kOk,
+                       "editable", kOk, kOk, kOk,
+                       "editablebr", kOk, kOk, kOk,
+                       "textarea", kOk, kOk, kOk);
+      testTextAtOffset(19, BOUNDARY_LINE_START, "", 19, 19,
+                       "div", kTodo, kTodo, kTodo,
+                       "divbr", kTodo, kTodo, kOk,
+                       "editable", kTodo, kTodo, kTodo,
+                       "editablebr", kTodo, kTodo, kOk,
+                       "textarea", kTodo, kTodo, kOk);
+
+      // BOUNDARY_LINE_END
+      testTextAtOffset(0, BOUNDARY_LINE_END, "oneword", 0, 7,
+                       "div", kTodo, kOk, kTodo,
+                       "divbr", kOk, kOk, kOk,
+                       "editable", kTodo, kOk, kTodo,
+                       "editablebr", kOk, kOk, kOk,
+                       "textarea", kTodo, kOk, kTodo);
+      testTextAtOffset(7, BOUNDARY_LINE_END, "oneword", 0, 7,
+                       "div", kTodo, kTodo, kTodo,
+                       "divbr", kTodo, kTodo, kTodo,
+                       "editable", kTodo, kTodo, kTodo,
+                       "editablebr", kTodo, kTodo, kTodo,
+                       "textarea", kTodo, kTodo, kTodo);
+      testTextAtOffset(8, BOUNDARY_LINE_END, "\n", 7, 8,
+                       "div", kTodo, kTodo, kTodo,
+                       "divbr", kTodo, kTodo, kTodo,
+                       "editable", kTodo, kTodo, kTodo,
+                       "editablebr", kTodo, kTodo, kTodo,
+                       "textarea", kTodo, kTodo, kTodo);
+      testTextAtOffset(9, BOUNDARY_LINE_END, "\ntwo words", 8, 18,
+                       "div", kTodo, kOk, kTodo,
+                       "divbr", kOk, kOk, kOk,
+                       "editable", kTodo, kOk, kTodo,
+                       "editablebr", kOk, kOk, kOk,
+                       "textarea", kOk, kOk, kOk);
+      testTextAtOffset(17, BOUNDARY_LINE_END, "\ntwo words", 8, 18,
+                       "div", kOk, kOk, kOk,
+                       "divbr", kOk, kOk, kOk,
+                       "editable", kOk, kOk, kOk,
+                       "editablebr", kOk, kOk, kOk,
+                       "textarea", kOk, kOk, kOk);
+      testTextAtOffset(18, BOUNDARY_LINE_END, "\ntwo words", 8, 18,
+                       "div", kTodo, kTodo, kTodo,
+                       "divbr", kTodo, kTodo, kTodo,
+                       "editable", kTodo, kTodo, kTodo,
+                       "editablebr", kTodo, kTodo, kTodo,
+                       "textarea", kTodo, kTodo, kTodo);
+      testTextAtOffset(19, BOUNDARY_LINE_END, "\n", 18, 19,
+                       "div", kTodo, kTodo, kTodo,
+                       "divbr", kTodo, kTodo, kTodo,
+                       "editable", kTodo, kTodo, kTodo,
+                       "editablebr", kTodo, kTodo, kTodo,
+                       "textarea", kTodo, kTodo, kTodo);
+
+      SimpleTest.finish();
+    }
+
+    SimpleTest.waitForExplicitFinish();
+    addA11yLoadEvent(doTest);
+  </script>
+</head>
+<body>
+
+  <a target="_blank"
+     title="nsIAccessibleText getText related functions test in multiline text"
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=612331">
+   Bug 612331
+  </a>
+  <p id="display"></p>
+  <div id="content" style="display: none"></div>
+  <pre id="test">
+  <div id="div">oneword
+
+two words
+</div>
+  <div id="divbr">oneword<br/><br/>two words<br/></div>
+  <div id="editable" contenteditable="true">oneword
+
+two words
+</div>
+  <div id="editablebr" contenteditable="true">oneword<br/><br/>two words<br/></div>
+  <textarea id="textarea" cols="300">oneword
+
+two words</textarea>
+  </pre>
+</body>
+</html>
--- a/addon-sdk/source/test/test-app-strings.js
+++ b/addon-sdk/source/test/test-app-strings.js
@@ -16,22 +16,22 @@ exports.testStringBundle = function(test
   let appLocale = Cc["@mozilla.org/intl/nslocaleservice;1"].
                   getService(Ci.nsILocaleService).
                   getApplicationLocale();
 
   let stringBundle = Cc["@mozilla.org/intl/stringbundle;1"].
                      getService(Ci.nsIStringBundleService).
                      createBundle(url, appLocale);
 
-  let (name = "Yes") {
+  let (name = "CheckMessage") {
     test.assertEqual(strings.get(name), stringBundle.GetStringFromName(name),
                      "getting a string returns the string");
   }
 
-  let (name = "ExtensionCapability", args = ["foo"]) {
+  let (name = "CreateWrapperDenied", args = ["foo"]) {
     test.assertEqual(strings.get(name, args),
                      stringBundle.formatStringFromName(name, args, args.length),
                      "getting a formatted string returns the formatted string");
   }
 
   test.assertRaises(function () strings.get("nonexistentString"),
                     "String 'nonexistentString' could not be retrieved from " +
                     "the bundle due to an unknown error (it doesn't exist?).",
--- a/b2g/components/Makefile.in
+++ b/b2g/components/Makefile.in
@@ -6,20 +6,16 @@ DEPTH      = @DEPTH@
 topsrcdir  = @top_srcdir@
 srcdir     = @srcdir@
 VPATH      = @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 MODULE = B2GComponents
 
-XPIDLSRCS = \
-        b2g.idl \
-        $(NULL)
-
 EXTRA_PP_COMPONENTS = \
         ActivitiesGlue.js \
         AlertsService.js \
         B2GAboutRedirector.js \
         B2GComponents.manifest \
         ContentHandler.js \
         ContentPermissionPrompt.js \
         DirectoryProvider.js \
--- a/b2g/components/moz.build
+++ b/b2g/components/moz.build
@@ -1,6 +1,11 @@
 # vim: set filetype=python:
 # 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/.
 
 TEST_DIRS += ['test']
+
+XPIDL_SOURCES += [
+    'b2g.idl',
+]
+
--- a/b2g/config/otoro/releng-otoro.tt
+++ b/b2g/config/otoro/releng-otoro.tt
@@ -1,12 +1,12 @@
 [
 {
-"size": 896095824,
-"digest": "c19f6ab2af72a3156c60daf061c6845ab1ae2071cf04fadcfaa9dddb15f9f58d4a67022f8ec8a978ddf7f6f08e1bb86fdc35d5b5a935bb90dec983280d2878e2",
+"size": 896434664,
+"digest": "bce8b7264948f1c97749a1c678b7635a9fc13c73ab6c2cf4557737b12cc523c0c51a52efbc73854f8d63d35667e8853345172f7c61a6d4f200fd3c295e483e3e",
 "algorithm": "sha512",
 "filename": "gonk.tar.xz"
 },
 {
 "size": 4139008,
 "digest": "6f65553e882316582b944e46c659915a1b907c4a326104cb31d81356330dddacba757e3eafbd282063da0e670c3c5d6b9a0905ab88da84b47848d810c37571cb",
 "algorithm": "sha512",
 "filename": "boot.img"
--- a/b2g/config/otoro/sources.xml
+++ b/b2g/config/otoro/sources.xml
@@ -2,95 +2,94 @@
   <!-- This is only a record of which revisions were pulled to generate the
        gonk.tar.xz snapshot referred to by releng-otoro.tt -->
 
   <remote fetch="https://android.googlesource.com/" name="aosp"/>
   <remote fetch="https://git.mozilla.org/b2g" name="b2g"/>
   <remote fetch="git://github.com/mozilla-b2g/" name="b2ggithub"/>
   <remote fetch="git://github.com/mozilla/" name="mozilla"/>
   <remote fetch="git://codeaurora.org/" name="caf"/>
-  <remote fetch="git://android.git.linaro.org/" name="linaro"/>
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <default remote="caf" revision="ics_chocolate_rb4.2" sync-j="4"/>
 
   <!-- Gonk specific things and forks -->
-  <project name="platform_build" path="build" remote="b2g" revision="43434d6cdbf702e6197e0791f19406860284edf6">
+  <project name="platform_build" path="build" remote="b2g" revision="777bee02feb43f8f29644f9d09ea7fe01d03f127">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <!-- Information: fake-dalvik is tagged with B2G_1_0_0_20130125190500 --><project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="8e68d41728675fc72502dc572dec523c2c8abb8a"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="f634b3d50effdd42828cc757c01fdbf74e562a36"/>
   <!-- Information: librecovery is tagged with B2G_1_0_0_20130125190500 --><project name="librecovery" path="librecovery" remote="b2g" revision="601fc18b28c9d7cf6954b281ddd3b705c74a9215"/>
-  <project name="moztt" path="external/moztt" remote="b2g" revision="62f94a26d34c1f1e1846efd58d34363c051e8c66"/>
+  <project name="moztt" path="external/moztt" remote="b2g" revision="e1569feeb10891a55193e36b4a3939742151b05f"/>
 
   <!-- Stock Android things -->
-  <!-- Information: platform/abi/cpp is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY_V1.01.00.01.19.032 --><project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
+  <!-- Information: platform/abi/cpp is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY_V1.01.00.01.19.039 --><project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
   <!-- Information: platform/bionic is tagged with M8960AAAAANLYA100715A --><project name="platform/bionic" path="bionic" revision="cd5dfce80bc3f0139a56b58aca633202ccaee7f8"/>
   <!-- Information: platform/bootable/recovery is tagged with M8960AAAAANLYA100715A --><project name="platform/bootable/recovery" path="bootable/recovery" revision="e0a9ac010df3afaa47ba107192c05ac8b5516435"/>
   <!-- Information: platform/development is tagged with M8960AAAAANLYA100715A --><project name="platform/development" path="development" revision="a384622f5fcb1d2bebb9102591ff7ae91fe8ed2d"/>
   <!-- Information: device/common is tagged with AU_LINUX_ANDROID_ICS_CHOCOLATE.04.00.04.05.324 --><project name="device/common" path="device/common" revision="7c65ea240157763b8ded6154a17d3c033167afb7"/>
   <!-- Information: device/sample is tagged with M8960AAAAANLYA100715A --><project name="device/sample" path="device/sample" revision="c328f3d4409db801628861baa8d279fb8855892f"/>
   <project name="platform_external_apriori" path="external/apriori" remote="b2ggithub" revision="2c3a7113299eb789a076be23449d868b3bfa07fd"/>
   <!-- Information: platform/external/bluetooth/bluez is tagged with M76XXUSNEKNLYA2040 --><project name="platform/external/bluetooth/bluez" path="external/bluetooth/bluez" revision="1023c91c66e9c3bd1132480051993bf7827770f6"/>
-  <!-- Information: platform/external/bluetooth/glib is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY_V1.01.00.01.19.032 --><project name="platform/external/bluetooth/glib" path="external/bluetooth/glib" revision="c6b49241cc1a8950723a5f74f8f4b4f4c3fa970e"/>
+  <!-- Information: platform/external/bluetooth/glib is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY_V1.01.00.01.19.039 --><project name="platform/external/bluetooth/glib" path="external/bluetooth/glib" revision="c6b49241cc1a8950723a5f74f8f4b4f4c3fa970e"/>
   <!-- Information: platform/external/bluetooth/hcidump is tagged with AU_LINUX_ANDROID_ICS_CHOCOLATE.04.00.04.05.324 --><project name="platform/external/bluetooth/hcidump" path="external/bluetooth/hcidump" revision="02b1eb24fbb3d0135a81edb4a2175b1397308d7d"/>
-  <!-- Information: platform/external/bsdiff is tagged with M8930AAAAANLYA2217182 --><project name="platform/external/bsdiff" path="external/bsdiff" revision="81872540236d9bb15cccf963d05b9de48baa5375"/>
-  <!-- Information: platform/external/bzip2 is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY_V1.01.00.01.19.032 --><project name="platform/external/bzip2" path="external/bzip2" revision="048dacdca43eed1534689ececcf2781c63e1e4ba"/>
+  <!-- Information: platform/external/bsdiff is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY_V1.01.00.01.19.039 --><project name="platform/external/bsdiff" path="external/bsdiff" revision="81872540236d9bb15cccf963d05b9de48baa5375"/>
+  <!-- Information: platform/external/bzip2 is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY_V1.01.00.01.19.039 --><project name="platform/external/bzip2" path="external/bzip2" revision="048dacdca43eed1534689ececcf2781c63e1e4ba"/>
   <!-- Information: platform/external/dbus is tagged with M8960AAAAANLYA100715A --><project name="platform/external/dbus" path="external/dbus" revision="c7517b6195dc6926728352113e6cc335da3f9c9e"/>
   <!-- Information: platform/external/dhcpcd is tagged with M8960AAAAANLYA100715A --><project name="platform/external/dhcpcd" path="external/dhcpcd" revision="1e00fb67022d0921af0fead263f81762781b9ffa"/>
-  <!-- Information: platform/external/dnsmasq is tagged with M8930AAAAANLYA2217182 --><project name="platform/external/dnsmasq" path="external/dnsmasq" revision="f621afad94df46204c25fc2593a19d704d2637f5"/>
+  <!-- Information: platform/external/dnsmasq is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY_V1.01.00.01.19.039 --><project name="platform/external/dnsmasq" path="external/dnsmasq" revision="f621afad94df46204c25fc2593a19d704d2637f5"/>
   <project name="platform_external_elfcopy" path="external/elfcopy" remote="b2ggithub" revision="62c1bed1c4505369cac2e72fbe30452a598fb690"/>
   <project name="platform_external_elfutils" path="external/elfutils" remote="b2ggithub" revision="72940dec691fa3255e13df01f8c53b620e446066"/>
-  <!-- Information: platform/external/e2fsprogs is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY_V1.01.00.01.19.032 --><project name="platform/external/e2fsprogs" path="external/e2fsprogs" revision="d5f550bb2f556c5d287f7c8d2b77223654bcec37"/>
-  <!-- Information: platform/external/expat is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY_V1.01.00.01.19.032 --><project name="platform/external/expat" path="external/expat" revision="6df134250feab71edb5915ecaa6268210bca76c5"/>
-  <!-- Information: platform/external/fdlibm is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY_V1.01.00.01.19.032 --><project name="platform/external/fdlibm" path="external/fdlibm" revision="988ffeb12a6e044ae3504838ef1fee3fe0716934"/>
-  <!-- Information: platform/external/flac is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY_V1.01.00.01.19.032 --><project name="platform/external/flac" path="external/flac" revision="5893fbe890f5dab8e4146d2baa4bd2691c0739e0"/>
-  <!-- Information: platform/external/freetype is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY_V1.01.00.01.19.032 --><project name="platform/external/freetype" path="external/freetype" revision="aeb407daf3711a10a27f3bc2223c5eb05158076e"/>
-  <!-- Information: platform/external/giflib is tagged with M8930AAAAANLYA2217182 --><project name="platform/external/giflib" path="external/giflib" revision="b2597268aef084202a8c349d1cc072c03c6e22eb"/>
-  <project name="platform/external/gtest" path="external/gtest" remote="linaro" revision="8c212ebe53bb2baab3575f03069016f1fb11e449"/>
-  <!-- Information: platform/external/harfbuzz is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY_V1.01.00.01.19.032 --><project name="platform/external/harfbuzz" path="external/harfbuzz" revision="116610d63a859521dacf00fb6818ee9ab2e666f6"/>
-  <!-- Information: platform/external/icu4c is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY_V1.01.00.01.19.032 --><project name="platform/external/icu4c" path="external/icu4c" revision="0fa67b93b831c6636ca18b152a1b1b14cc99b034"/>
-  <!-- Information: platform/external/iptables is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY_V1.01.00.01.19.032 --><project name="platform/external/iptables" path="external/iptables" revision="3b2deb17f065c5664bb25e1a28489e5792eb63ff"/>
+  <!-- Information: platform/external/e2fsprogs is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY_V1.01.00.01.19.039 --><project name="platform/external/e2fsprogs" path="external/e2fsprogs" revision="d5f550bb2f556c5d287f7c8d2b77223654bcec37"/>
+  <!-- Information: platform/external/expat is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY_V1.01.00.01.19.039 --><project name="platform/external/expat" path="external/expat" revision="6df134250feab71edb5915ecaa6268210bca76c5"/>
+  <!-- Information: platform/external/fdlibm is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY_V1.01.00.01.19.039 --><project name="platform/external/fdlibm" path="external/fdlibm" revision="988ffeb12a6e044ae3504838ef1fee3fe0716934"/>
+  <!-- Information: platform/external/flac is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY_V1.01.00.01.19.039 --><project name="platform/external/flac" path="external/flac" revision="5893fbe890f5dab8e4146d2baa4bd2691c0739e0"/>
+  <!-- Information: platform/external/freetype is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY_V1.01.00.01.19.039 --><project name="platform/external/freetype" path="external/freetype" revision="aeb407daf3711a10a27f3bc2223c5eb05158076e"/>
+  <!-- Information: platform/external/giflib is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY_V1.01.00.01.19.039 --><project name="platform/external/giflib" path="external/giflib" revision="b2597268aef084202a8c349d1cc072c03c6e22eb"/>
+  <project name="platform/external/gtest" path="external/gtest" revision="8c212ebe53bb2baab3575f03069016f1fb11e449"/>
+  <!-- Information: platform/external/harfbuzz is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY_V1.01.00.01.19.039 --><project name="platform/external/harfbuzz" path="external/harfbuzz" revision="116610d63a859521dacf00fb6818ee9ab2e666f6"/>
+  <!-- Information: platform/external/icu4c is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY_V1.01.00.01.19.039 --><project name="platform/external/icu4c" path="external/icu4c" revision="0fa67b93b831c6636ca18b152a1b1b14cc99b034"/>
+  <!-- Information: platform/external/iptables is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY_V1.01.00.01.19.039 --><project name="platform/external/iptables" path="external/iptables" revision="3b2deb17f065c5664bb25e1a28489e5792eb63ff"/>
   <!-- Information: platform/external/jpeg is tagged with AU_LINUX_ANDROID_ICS_CHOCOLATE.04.00.04.05.324 --><project name="platform/external/jpeg" path="external/jpeg" revision="a62e464d672a4623233180e4023034bf825f066e"/>
-  <!-- Information: platform/external/libgsm is tagged with M8930AAAAANLYA2217182 --><project name="platform/external/libgsm" path="external/libgsm" revision="5e4516958690b9a1b2c98f88eeecba3edd2dbda4"/>
-  <!-- Information: platform/external/liblzf is tagged with M8930AAAAANLYA2217182 --><project name="platform/external/liblzf" path="external/liblzf" revision="6946aa575b0949d045722794850896099d937cbb"/>
-  <!-- Information: platform/external/libnfc-nxp is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY_V1.01.00.01.19.032 --><project name="platform/external/libnfc-nxp" path="external/libnfc-nxp" revision="3a912b065a31a72c63ad56ac224cfeaa933423b6"/>
-  <!-- Information: platform/external/libnl-headers is tagged with M8930AAAAANLYA2217182 --><project name="platform/external/libnl-headers" path="external/libnl-headers" revision="6ccf7349d61f73ac26a0675d735d903ab919c658"/>
+  <!-- Information: platform/external/libgsm is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY_V1.01.00.01.19.039 --><project name="platform/external/libgsm" path="external/libgsm" revision="5e4516958690b9a1b2c98f88eeecba3edd2dbda4"/>
+  <!-- Information: platform/external/liblzf is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY_V1.01.00.01.19.039 --><project name="platform/external/liblzf" path="external/liblzf" revision="6946aa575b0949d045722794850896099d937cbb"/>
+  <!-- Information: platform/external/libnfc-nxp is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.01.00.19.028 --><project name="platform/external/libnfc-nxp" path="external/libnfc-nxp" revision="3a912b065a31a72c63ad56ac224cfeaa933423b6"/>
+  <!-- Information: platform/external/libnl-headers is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY_V1.01.00.01.19.039 --><project name="platform/external/libnl-headers" path="external/libnl-headers" revision="6ccf7349d61f73ac26a0675d735d903ab919c658"/>
   <!-- Information: platform/external/libpng is tagged with M8960AAAAANLYA100715A --><project name="platform/external/libpng" path="external/libpng" revision="9c3730f0efa69f580f03463c237cd928f3196404"/>
   <!-- Information: platform/external/libvpx is tagged with M8960AAAAANLYA1519349 --><project name="platform/external/libvpx" path="external/libvpx" revision="3a40da0d96da5c520e7707aa14f48a80956e20d7"/>
   <!-- Information: platform/external/llvm is tagged with AU_LINUX_ANDROID_ICS_CHOCOLATE.04.00.04.05.324 --><project name="platform/external/llvm" path="external/llvm" revision="bff5923831940309f7d8ddbff5826ca6ed2dc050"/>
   <!-- Information: platform/external/mksh is tagged with AU_LINUX_ANDROID_ICS_CHOCOLATE.04.00.04.05.324 --><project name="platform/external/mksh" path="external/mksh" revision="ec646e8f5e7dac9a77d1de549c6ed92c04d0cd4b"/>
   <!-- Information: platform_external_opensans is tagged with B2G_1_0_0_20130125190500 --><project name="platform_external_opensans" path="external/opensans" remote="b2g" revision="b5b4c226ca1d71e936153cf679dda6d3d60e2354"/>
   <!-- Information: platform/external/openssl is tagged with AU_LINUX_ANDROID_ICS.04.00.04.00.110 --><project name="platform/external/openssl" path="external/openssl" revision="27d333cce9a31c806b4bfa042925f045c727aecd"/>
-  <!-- Information: platform/external/protobuf is tagged with M8930AAAAANLYA2217182 --><project name="platform/external/protobuf" path="external/protobuf" revision="e217977611c52bccde7f7c78e1d3c790c6357431"/>
-  <!-- Information: platform/external/safe-iop is tagged with M8930AAAAANLYA2217182 --><project name="platform/external/safe-iop" path="external/safe-iop" revision="07073634e2e3aa4f518e36ed5dec3aabc549d5fb"/>
+  <!-- Information: platform/external/protobuf is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY_V1.01.00.01.19.039 --><project name="platform/external/protobuf" path="external/protobuf" revision="e217977611c52bccde7f7c78e1d3c790c6357431"/>
+  <!-- Information: platform/external/safe-iop is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY_V1.01.00.01.19.039 --><project name="platform/external/safe-iop" path="external/safe-iop" revision="07073634e2e3aa4f518e36ed5dec3aabc549d5fb"/>
   <!-- Information: screencap-gonk is tagged with B2G_1_0_0_20130125190500 --><project name="screencap-gonk" path="external/screencap-gonk" remote="b2g" revision="e6403c71e9eca8cb943739d5a0a192deac60fc51"/>
   <!-- Information: platform/external/skia is tagged with M8960AAAAANLYA100715A --><project name="platform/external/skia" path="external/skia" revision="7d90c85f2c0e3b747f7c7eff8bc9253b0063b439"/>
   <!-- Information: platform/external/sonivox is tagged with AU_LINUX_ANDROID_ICS_CHOCOLATE.04.00.04.05.324 --><project name="platform/external/sonivox" path="external/sonivox" revision="7c967779dfc61ac1f346e972de91d4bfce7dccbb"/>
-  <!-- Information: platform/external/speex is tagged with M8930AAAAANLYA2217182 --><project name="platform/external/speex" path="external/speex" revision="ebe6230a7f7c69f5a4389f2b09b7b19ef9e94f32"/>
+  <!-- Information: platform/external/speex is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY_V1.01.00.01.19.039 --><project name="platform/external/speex" path="external/speex" revision="ebe6230a7f7c69f5a4389f2b09b7b19ef9e94f32"/>
   <project name="platform/external/sqlite" path="external/sqlite" revision="fb30e613139b8836fdc8e81e166cf3a76e5fa17f"/>
-  <!-- Information: platform/external/stlport is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY_V1.01.00.01.19.032 --><project name="platform/external/stlport" path="external/stlport" revision="a6734e0645fce81c9610de0488b729207bfa576e"/>
-  <!-- Information: platform/external/strace is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY_V1.01.00.01.19.032 --><project name="platform/external/strace" path="external/strace" revision="c9fd2e5ef7d002e12e7cf2512506c84a9414b0fd"/>
-  <!-- Information: platform/external/tagsoup is tagged with M8930AAAAANLYA2217182 --><project name="platform/external/tagsoup" path="external/tagsoup" revision="68c2ec9e0acdb3214b7fb91dbab8c9fab8736817"/>
+  <!-- Information: platform/external/stlport is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY_V1.01.00.01.19.039 --><project name="platform/external/stlport" path="external/stlport" revision="a6734e0645fce81c9610de0488b729207bfa576e"/>
+  <!-- Information: platform/external/strace is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY_V1.01.00.01.19.039 --><project name="platform/external/strace" path="external/strace" revision="c9fd2e5ef7d002e12e7cf2512506c84a9414b0fd"/>
+  <!-- Information: platform/external/tagsoup is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY_V1.01.00.01.19.039 --><project name="platform/external/tagsoup" path="external/tagsoup" revision="68c2ec9e0acdb3214b7fb91dbab8c9fab8736817"/>
   <!-- Information: platform/external/tinyalsa is tagged with AU_LINUX_ANDROID_ICS_CHOCOLATE.04.00.04.05.324 --><project name="platform/external/tinyalsa" path="external/tinyalsa" revision="06cc244ee512c1352215e543615738bc8ac82814"/>
-  <!-- Information: platform/external/tremolo is tagged with M8930AAAAANLYA2217182 --><project name="platform/external/tremolo" path="external/tremolo" revision="25bd78d2392dbdc879ae53382cde9d019f79cf6f"/>
+  <!-- Information: platform/external/tremolo is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY_V1.01.00.01.19.039 --><project name="platform/external/tremolo" path="external/tremolo" revision="25bd78d2392dbdc879ae53382cde9d019f79cf6f"/>
   <!-- Information: unbootimg is tagged with B2G_1_0_0_20130125190500 --><project name="unbootimg" path="external/unbootimg" remote="b2g" revision="9464623d92eb8668544916dc5a8f4f6337d0bc08"/>
-  <!-- Information: platform/external/webp is tagged with M8930AAAAANLYA2217182 --><project name="platform/external/webp" path="external/webp" revision="88fe2b83c4b9232cd08729556fd0485d6a6a92cd"/>
-  <!-- Information: platform/external/webrtc is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY_V1.01.00.01.19.032 --><project name="platform/external/webrtc" path="external/webrtc" revision="137024dc8a2e9251a471e20518a9c3ae06f81f23"/>
-  <!-- Information: platform/external/wpa_supplicant is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY_V1.01.00.01.19.032 --><project name="platform/external/wpa_supplicant" path="external/wpa_supplicant" revision="a01d37870bbf9892d43e792e5de0683ca41c5497"/>
+  <!-- Information: platform/external/webp is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY_V1.01.00.01.19.039 --><project name="platform/external/webp" path="external/webp" revision="88fe2b83c4b9232cd08729556fd0485d6a6a92cd"/>
+  <!-- Information: platform/external/webrtc is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY_V1.01.00.01.19.039 --><project name="platform/external/webrtc" path="external/webrtc" revision="137024dc8a2e9251a471e20518a9c3ae06f81f23"/>
+  <!-- Information: platform/external/wpa_supplicant is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY_V1.01.00.01.19.039 --><project name="platform/external/wpa_supplicant" path="external/wpa_supplicant" revision="a01d37870bbf9892d43e792e5de0683ca41c5497"/>
   <!-- Information: platform/external/hostap is tagged with M8960AAAAANLYA1047 --><project name="platform/external/hostap" path="external/hostap" revision="bf04b0faadbdeb4b7943f2e2c4c5aa59df872bb1"/>
   <!-- Information: platform/external/zlib is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.01.19.008 --><project name="platform/external/zlib" path="external/zlib" revision="f96a1d1ebfdf1cd582210fd09c23d8f59e0ae094"/>
-  <!-- Information: platform/external/yaffs2 is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY_V1.01.00.01.19.032 --><project name="platform/external/yaffs2" path="external/yaffs2" revision="0afa916204c664b3114429637b63af1321a0aeca"/>
+  <!-- Information: platform/external/yaffs2 is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY_V1.01.00.01.19.039 --><project name="platform/external/yaffs2" path="external/yaffs2" revision="0afa916204c664b3114429637b63af1321a0aeca"/>
   <!-- Information: platform/frameworks/base is tagged with M76XXUSNEKNLYA2040 --><project name="platform/frameworks/base" path="frameworks/base" revision="eb2bc75803ca179353c24c364a9c8a8ce23e8b78"/>
-  <!-- Information: platform/frameworks/opt/emoji is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY_V1.01.00.01.19.032 --><project name="platform/frameworks/opt/emoji" path="frameworks/opt/emoji" revision="a95d8db002770469d72dfaf59ff37ac96db29a87"/>
+  <!-- Information: platform/frameworks/opt/emoji is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY_V1.01.00.01.19.039 --><project name="platform/frameworks/opt/emoji" path="frameworks/opt/emoji" revision="a95d8db002770469d72dfaf59ff37ac96db29a87"/>
   <!-- Information: platform/frameworks/support is tagged with AU_LINUX_ANDROID_ICS_CHOCOLATE.04.00.04.05.324 --><project name="platform/frameworks/support" path="frameworks/support" revision="27208692b001981f1806f4f396434f4eac78b909"/>
   <!-- Information: platform/hardware/libhardware is tagged with M8960AAAAANLYA1049B --><project name="platform/hardware/libhardware" path="hardware/libhardware" revision="4a619901847621f8a7305edf42dd07347a140484"/>
   <!-- Information: platform/hardware/libhardware_legacy is tagged with M8960AAAAANLYA153611 --><project name="platform/hardware/libhardware_legacy" path="hardware/libhardware_legacy" revision="87b4d7afa8f854b445e2d0d95091f6f6069f2b30"/>
   <!-- Information: platform/libcore is tagged with M8960AAAAANLYA100715A --><project name="platform/libcore" path="libcore" revision="30841f9fba9ccd5c54f4f079f495994db97f283e"/>
-  <!-- Information: platform/ndk is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY_V1.01.00.01.19.032 --><project name="platform/ndk" path="ndk" revision="9f555971e1481854d5b4dc11b3e6af9fff4f241f"/>
+  <!-- Information: platform/ndk is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY_V1.01.00.01.19.039 --><project name="platform/ndk" path="ndk" revision="9f555971e1481854d5b4dc11b3e6af9fff4f241f"/>
   <!-- Information: platform/prebuilt is tagged with AU_LINUX_ANDROID_ICS_CHOCOLATE.04.00.04.05.324 --><project name="platform/prebuilt" path="prebuilt" revision="447ea790fcc957dde59730ecc2a65ca263bdc733"/>
   <!-- Information: platform/system/bluetooth is tagged with M8960AAAAANLYA100703 --><project name="platform/system/bluetooth" path="system/bluetooth" revision="7772cad4823f1f427ce1d4df84a55982386d6d18"/>
   <!-- Information: platform/system/core is tagged with M76XXUSNEKNLYA2040 --><project name="platform/system/core" path="system/core" revision="bf1970408676ce570b8f4dc3efa038e47552137f"/>
   <!-- Information: platform/system/extras is tagged with AU_LINUX_ANDROID_ICS_CHOCOLATE.04.00.04.05.324 --><project name="platform/system/extras" path="system/extras" revision="01db6c1254e1407740a543f24317fc540fc4c049"/>
   <!-- Information: platform/system/media is tagged with AU_LINUX_ANDROID_ICS_CHOCOLATE.04.00.04.05.324 --><project name="platform/system/media" path="system/media" revision="7f71c7fd362bbd992ff2e0e80f7af5859ad116ad"/>
   <!-- Information: platform/system/netd is tagged with M8960AAAAANLYA1049 --><project name="platform/system/netd" path="system/netd" revision="306e765248e3900041bf2737e9f57b1b5694a4ce"/>
   <!-- Information: platform/system/vold is tagged with M8960AAAAANLYA100715A --><project name="platform/system/vold" path="system/vold" revision="99fff257d53cc045d1460841edca5d901dacfcf5"/>
 
--- a/b2g/config/panda/releng-pandaboard.tt
+++ b/b2g/config/panda/releng-pandaboard.tt
@@ -1,12 +1,12 @@
 [
 {
-"size": 677418796,
-"digest": "34f8e675382cbd6acc8803a540c9ecd51641f24679b3803ff585176c1ba5b869989bed070ee6db79f70d4875d16ad6b4cb2e676e24caca4012874574d3899d2c",
+"size": 677817512,
+"digest": "746a6acd08be2065a077ba860df4be8ca53bca16ea868911407b8d3dc4fc2becd8cee2c88d51e8bfce4f07caa37184b91ba4d6afba937a25b424142a6e605fdc",
 "algorithm": "sha512",
 "filename": "gonk.tar.xz"
 },
 {
 "size": 2116507,
 "digest": "be67a012963a5c162834f9fcb989bcebd2d047dcb4e17ee23031b694dcf7cdfd6d7a6545d7a1f5e7293b6d24415403972f4ea1ab8c6c78fefcabfaf3f6875214",
 "algorithm": "sha512",
 "filename": "download-panda.tar.bz2"
--- a/b2g/config/panda/sources.xml
+++ b/b2g/config/panda/sources.xml
@@ -1,101 +1,102 @@
 <?xml version="1.0" ?><manifest>
   <!-- This is only a record of which revisions were pulled to generate the
        gonk.tar.xz snapshot referred to by releng-pandaboard.tt -->
 
   <remote fetch="https://android.googlesource.com/" name="aosp"/>
   <remote fetch="git://github.com/mozilla-b2g/" name="b2g"/>
-  <remote fetch="git://android.git.linaro.org/" name="linaro"/>
+  <remote fetch="http://android.git.linaro.org/git-ro/" name="linaro"/>
+  <remote fetch="git://codeaurora.org/" name="caf"/>
   <remote fetch="git://github.com/mozilla/" name="mozilla"/>
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
-  <default remote="linaro" revision="refs/tags/android-4.0.4_r2.1" sync-j="4"/>
+  <default remote="caf" revision="refs/tags/android-4.0.4_r2.1" sync-j="4"/>
 
   <!-- Gonk specific things and forks -->
-  <project name="platform_build" path="build" remote="b2g" revision="43434d6cdbf702e6197e0791f19406860284edf6">
+  <project name="platform_build" path="build" remote="b2g" revision="777bee02feb43f8f29644f9d09ea7fe01d03f127">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <!-- Information: fake-dalvik is tagged with B2G_1_0_0_20130125190500 --><project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="c6fc2e70b2586fe45db4b676567be2aa94cf420e"/>
+  <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="8e68d41728675fc72502dc572dec523c2c8abb8a"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="f634b3d50effdd42828cc757c01fdbf74e562a36"/>
-  <project name="moztt" path="external/moztt" remote="b2g" revision="62f94a26d34c1f1e1846efd58d34363c051e8c66"/>
+  <project name="moztt" path="external/moztt" remote="b2g" revision="e1569feeb10891a55193e36b4a3939742151b05f"/>
 
   <!-- Stock Android things -->
-  <!-- Information: platform/abi/cpp is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.01.00.19.013 --><project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
+  <!-- Information: platform/abi/cpp is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY_V1.01.00.01.19.039 --><project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
   <project name="platform/bionic" path="bionic" revision="c7bab8cb8483e7869eabdbd4add7c9e5beeecc80"/>
   <!-- Information: platform/bootable/recovery is tagged with android-4.0.4_r2.1 --><project name="platform/bootable/recovery" path="bootable/recovery" revision="fadc5ac81d6400ebdd041f7d4ea64021596d6b7d"/>
   <!-- Information: device/common is tagged with android-sdk-adt_r20 --><project name="device/common" path="device/common" revision="7d4526582f88808a3194e1a3b304abb369d2745c"/>
   <!-- Information: device/sample is tagged with android-4.0.4_r2.1 --><project name="device/sample" path="device/sample" revision="ef228b8b377a9663e94be4b1aeb6c2bf7a07d098"/>
   <project name="platform_external_apriori" path="external/apriori" remote="b2g" revision="2c3a7113299eb789a076be23449d868b3bfa07fd"/>
   <!-- Information: platform/external/bluetooth/bluez is tagged with android-4.0.4_r2.1 --><project name="platform/external/bluetooth/bluez" path="external/bluetooth/bluez" revision="966afbd88f0bfc325bf80274ad2723c238883fa1"/>
-  <!-- Information: platform/external/bluetooth/glib is tagged with android-4.1.1_r6.1 --><project name="platform/external/bluetooth/glib" path="external/bluetooth/glib" revision="1143b9918eab068401b604eb11c3f651f4e38b25"/>
-  <!-- Information: platform/external/bluetooth/hcidump is tagged with android-4.1.1_r6.1 --><project name="platform/external/bluetooth/hcidump" path="external/bluetooth/hcidump" revision="7322661808c2006b7848e79e6bb72b37fbcf6710"/>
-  <!-- Information: platform/external/bsdiff is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.01.00.19.013 --><project name="platform/external/bsdiff" path="external/bsdiff" revision="81872540236d9bb15cccf963d05b9de48baa5375"/>
+  <!-- Information: platform/external/bluetooth/glib is tagged with android-cts-4.1_r2 --><project name="platform/external/bluetooth/glib" path="external/bluetooth/glib" revision="1143b9918eab068401b604eb11c3f651f4e38b25"/>
+  <!-- Information: platform/external/bluetooth/hcidump is tagged with android-cts-4.1_r2 --><project name="platform/external/bluetooth/hcidump" path="external/bluetooth/hcidump" revision="7322661808c2006b7848e79e6bb72b37fbcf6710"/>
+  <!-- Information: platform/external/bsdiff is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY_V1.01.00.01.19.039 --><project name="platform/external/bsdiff" path="external/bsdiff" revision="81872540236d9bb15cccf963d05b9de48baa5375"/>
   <project name="platform/external/busybox" path="external/busybox" remote="linaro" revision="2e461c8029a50d986dfe4ab07ae5a1834b5c40f0"/>
-  <!-- Information: platform/external/bzip2 is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.01.00.19.013 --><project name="platform/external/bzip2" path="external/bzip2" revision="048dacdca43eed1534689ececcf2781c63e1e4ba"/>
-  <!-- Information: platform/external/dbus is tagged with android-4.1.1_r6.1 --><project name="platform/external/dbus" path="external/dbus" revision="537eaff5de9aace3348436166d4cde7adc1e488e"/>
+  <!-- Information: platform/external/bzip2 is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY_V1.01.00.01.19.039 --><project name="platform/external/bzip2" path="external/bzip2" revision="048dacdca43eed1534689ececcf2781c63e1e4ba"/>
+  <!-- Information: platform/external/dbus is tagged with android-cts-4.1_r2 --><project name="platform/external/dbus" path="external/dbus" revision="537eaff5de9aace3348436166d4cde7adc1e488e"/>
   <!-- Information: platform/external/dhcpcd is tagged with android-sdk-adt_r20 --><project name="platform/external/dhcpcd" path="external/dhcpcd" revision="ddaa48f57b54b2862b3e6dcf18a44c9647f3baaa"/>
-  <!-- Information: platform/external/dnsmasq is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.01.00.19.013 --><project name="platform/external/dnsmasq" path="external/dnsmasq" revision="f621afad94df46204c25fc2593a19d704d2637f5"/>
+  <!-- Information: platform/external/dnsmasq is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY_V1.01.00.01.19.039 --><project name="platform/external/dnsmasq" path="external/dnsmasq" revision="f621afad94df46204c25fc2593a19d704d2637f5"/>
   <project name="platform_external_elfcopy" path="external/elfcopy" remote="b2g" revision="62c1bed1c4505369cac2e72fbe30452a598fb690"/>
   <project name="platform_external_elfutils" path="external/elfutils" remote="b2g" revision="72940dec691fa3255e13df01f8c53b620e446066"/>
-  <!-- Information: platform/external/expat is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.01.00.19.013 --><project name="platform/external/expat" path="external/expat" revision="6df134250feab71edb5915ecaa6268210bca76c5"/>
-  <!-- Information: platform/external/fdlibm is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.01.00.19.013 --><project name="platform/external/fdlibm" path="external/fdlibm" revision="988ffeb12a6e044ae3504838ef1fee3fe0716934"/>
-  <!-- Information: platform/external/flac is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.01.00.19.013 --><project name="platform/external/flac" path="external/flac" revision="5893fbe890f5dab8e4146d2baa4bd2691c0739e0"/>
-  <!-- Information: platform/external/freetype is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.01.00.19.013 --><project name="platform/external/freetype" path="external/freetype" revision="aeb407daf3711a10a27f3bc2223c5eb05158076e"/>
-  <!-- Information: platform/external/giflib is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.01.00.19.013 --><project name="platform/external/giflib" path="external/giflib" revision="b2597268aef084202a8c349d1cc072c03c6e22eb"/>
-  <project name="platform/external/gtest" path="external/gtest" remote="linaro" revision="8c212ebe53bb2baab3575f03069016f1fb11e449"/>
+  <!-- Information: platform/external/expat is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY_V1.01.00.01.19.039 --><project name="platform/external/expat" path="external/expat" revision="6df134250feab71edb5915ecaa6268210bca76c5"/>
+  <!-- Information: platform/external/fdlibm is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY_V1.01.00.01.19.039 --><project name="platform/external/fdlibm" path="external/fdlibm" revision="988ffeb12a6e044ae3504838ef1fee3fe0716934"/>
+  <!-- Information: platform/external/flac is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY_V1.01.00.01.19.039 --><project name="platform/external/flac" path="external/flac" revision="5893fbe890f5dab8e4146d2baa4bd2691c0739e0"/>
+  <!-- Information: platform/external/freetype is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY_V1.01.00.01.19.039 --><project name="platform/external/freetype" path="external/freetype" revision="aeb407daf3711a10a27f3bc2223c5eb05158076e"/>
+  <!-- Information: platform/external/giflib is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY_V1.01.00.01.19.039 --><project name="platform/external/giflib" path="external/giflib" revision="b2597268aef084202a8c349d1cc072c03c6e22eb"/>
+  <project name="platform/external/gtest" path="external/gtest" revision="8c212ebe53bb2baab3575f03069016f1fb11e449"/>
   <!-- Information: platform/external/harfbuzz is tagged with android-sdk-adt_r20 --><project name="platform/external/harfbuzz" path="external/harfbuzz" revision="bae491c03a00757d83ede8d855b7d85d246bde3d"/>
-  <!-- Information: platform/external/icu4c is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.01.00.19.013 --><project name="platform/external/icu4c" path="external/icu4c" revision="0fa67b93b831c6636ca18b152a1b1b14cc99b034"/>
-  <!-- Information: platform/external/iptables is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.01.00.19.013 --><project name="platform/external/iptables" path="external/iptables" revision="3b2deb17f065c5664bb25e1a28489e5792eb63ff"/>
-  <!-- Information: platform/external/jhead is tagged with android-4.0.4_r2.1 --><project name="platform/external/jhead" path="external/jhead" revision="754078052c687f6721536009c816644c73e4f145"/>
-  <!-- Information: platform/external/jpeg is tagged with android-4.1.1_r6.1 --><project name="platform/external/jpeg" path="external/jpeg" revision="d4fad7f50f79626455d88523207e05b868819cd8"/>
-  <!-- Information: platform/external/libgsm is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.01.00.19.013 --><project name="platform/external/libgsm" path="external/libgsm" revision="5e4516958690b9a1b2c98f88eeecba3edd2dbda4"/>
-  <!-- Information: platform/external/liblzf is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.01.00.19.013 --><project name="platform/external/liblzf" path="external/liblzf" revision="6946aa575b0949d045722794850896099d937cbb"/>
+  <!-- Information: platform/external/icu4c is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY_V1.01.00.01.19.039 --><project name="platform/external/icu4c" path="external/icu4c" revision="0fa67b93b831c6636ca18b152a1b1b14cc99b034"/>
+  <!-- Information: platform/external/iptables is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY_V1.01.00.01.19.039 --><project name="platform/external/iptables" path="external/iptables" revision="3b2deb17f065c5664bb25e1a28489e5792eb63ff"/>
+  <!-- Information: platform/external/jhead is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY_V1.01.00.01.19.039 --><project name="platform/external/jhead" path="external/jhead" revision="754078052c687f6721536009c816644c73e4f145"/>
+  <!-- Information: platform/external/jpeg is tagged with android-cts-4.1_r2 --><project name="platform/external/jpeg" path="external/jpeg" revision="d4fad7f50f79626455d88523207e05b868819cd8"/>
+  <!-- Information: platform/external/libgsm is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY_V1.01.00.01.19.039 --><project name="platform/external/libgsm" path="external/libgsm" revision="5e4516958690b9a1b2c98f88eeecba3edd2dbda4"/>
+  <!-- Information: platform/external/liblzf is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY_V1.01.00.01.19.039 --><project name="platform/external/liblzf" path="external/liblzf" revision="6946aa575b0949d045722794850896099d937cbb"/>
   <!-- Information: platform/external/libnfc-nxp is tagged with android-4.0.4_r2.1 --><project name="platform/external/libnfc-nxp" path="external/libnfc-nxp" revision="533c14450e6239cce8acb74f4e4dea2c89f8f219"/>
-  <!-- Information: platform/external/libnl-headers is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.01.00.19.013 --><project name="platform/external/libnl-headers" path="external/libnl-headers" revision="6ccf7349d61f73ac26a0675d735d903ab919c658"/>
+  <!-- Information: platform/external/libnl-headers is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY_V1.01.00.01.19.039 --><project name="platform/external/libnl-headers" path="external/libnl-headers" revision="6ccf7349d61f73ac26a0675d735d903ab919c658"/>
   <!-- Information: platform/external/libpng is tagged with android-4.0.4_r2.1 --><project name="platform/external/libpng" path="external/libpng" revision="84d92c718ab9f48faec0f640747c4b6f7a995607"/>
   <!-- Information: platform/external/libvpx is tagged with M8960AAAAANLYA1519349 --><project name="platform/external/libvpx" path="external/libvpx" revision="3a40da0d96da5c520e7707aa14f48a80956e20d7"/>
   <!-- Information: platform/external/mksh is tagged with M8960AAAAANLYA1099D --><project name="platform/external/mksh" path="external/mksh" revision="5155f1c7438ef540d7b25eb70aa1639579795b07"/>
   <!-- Information: platform_external_opensans is tagged with B2G_1_0_0_20130125190500 --><project name="platform_external_opensans" path="external/opensans" remote="b2g" revision="b5b4c226ca1d71e936153cf679dda6d3d60e2354"/>
   <!-- Information: platform/external/openssl is tagged with android-4.0.4_r2.1 --><project name="platform/external/openssl" path="external/openssl" revision="ce96fb211b9a44bbd7fb5ef7ed0e6c1244045c2e"/>
-  <!-- Information: platform/external/protobuf is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.01.00.19.013 --><project name="platform/external/protobuf" path="external/protobuf" revision="e217977611c52bccde7f7c78e1d3c790c6357431"/>
-  <!-- Information: platform/external/safe-iop is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.01.00.19.013 --><project name="platform/external/safe-iop" path="external/safe-iop" revision="07073634e2e3aa4f518e36ed5dec3aabc549d5fb"/>
+  <!-- Information: platform/external/protobuf is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY_V1.01.00.01.19.039 --><project name="platform/external/protobuf" path="external/protobuf" revision="e217977611c52bccde7f7c78e1d3c790c6357431"/>
+  <!-- Information: platform/external/safe-iop is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY_V1.01.00.01.19.039 --><project name="platform/external/safe-iop" path="external/safe-iop" revision="07073634e2e3aa4f518e36ed5dec3aabc549d5fb"/>
   <!-- Information: screencap-gonk is tagged with B2G_1_0_0_20130125190500 --><project name="screencap-gonk" path="external/screencap-gonk" remote="b2g" revision="e6403c71e9eca8cb943739d5a0a192deac60fc51"/>
   <!-- Information: platform/external/skia is tagged with android-4.0.4_r2.1 --><project name="platform/external/skia" path="external/skia" revision="5c67a309e16bffe7013defda8f1217b3ce2420b4"/>
   <!-- Information: platform/external/sonivox is tagged with android-sdk-adt_r20 --><project name="platform/external/sonivox" path="external/sonivox" revision="5f9600971859fe072f31b38a51c38157f5f9b381"/>
-  <!-- Information: platform/external/speex is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.01.00.19.013 --><project name="platform/external/speex" path="external/speex" revision="ebe6230a7f7c69f5a4389f2b09b7b19ef9e94f32"/>
+  <!-- Information: platform/external/speex is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY_V1.01.00.01.19.039 --><project name="platform/external/speex" path="external/speex" revision="ebe6230a7f7c69f5a4389f2b09b7b19ef9e94f32"/>
   <!-- Information: platform/external/sqlite is tagged with android-4.0.4_r2.1 --><project name="platform/external/sqlite" path="external/sqlite" revision="c999ff8c12a4cf81cb9ad628f47b2720effba5e5"/>
-  <!-- Information: platform/external/stlport is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.01.00.19.013 --><project name="platform/external/stlport" path="external/stlport" revision="a6734e0645fce81c9610de0488b729207bfa576e"/>
-  <!-- Information: platform/external/strace is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.01.00.19.013 --><project name="platform/external/strace" path="external/strace" revision="c9fd2e5ef7d002e12e7cf2512506c84a9414b0fd"/>
-  <!-- Information: platform/external/tagsoup is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.01.00.19.013 --><project name="platform/external/tagsoup" path="external/tagsoup" revision="68c2ec9e0acdb3214b7fb91dbab8c9fab8736817"/>
+  <!-- Information: platform/external/stlport is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY_V1.01.00.01.19.039 --><project name="platform/external/stlport" path="external/stlport" revision="a6734e0645fce81c9610de0488b729207bfa576e"/>
+  <!-- Information: platform/external/strace is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY_V1.01.00.01.19.039 --><project name="platform/external/strace" path="external/strace" revision="c9fd2e5ef7d002e12e7cf2512506c84a9414b0fd"/>
+  <!-- Information: platform/external/tagsoup is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY_V1.01.00.01.19.039 --><project name="platform/external/tagsoup" path="external/tagsoup" revision="68c2ec9e0acdb3214b7fb91dbab8c9fab8736817"/>
   <!-- Information: platform/external/tinyalsa is tagged with android-4.0.4_r2.1 --><project name="platform/external/tinyalsa" path="external/tinyalsa" revision="495239e683a728957c890c124b239f9b7b8ef5a8"/>
-  <!-- Information: platform/external/tremolo is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.01.00.19.013 --><project name="platform/external/tremolo" path="external/tremolo" revision="25bd78d2392dbdc879ae53382cde9d019f79cf6f"/>
-  <!-- Information: platform/external/webp is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.01.00.19.013 --><project name="platform/external/webp" path="external/webp" revision="88fe2b83c4b9232cd08729556fd0485d6a6a92cd"/>
+  <!-- Information: platform/external/tremolo is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY_V1.01.00.01.19.039 --><project name="platform/external/tremolo" path="external/tremolo" revision="25bd78d2392dbdc879ae53382cde9d019f79cf6f"/>
+  <!-- Information: platform/external/webp is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY_V1.01.00.01.19.039 --><project name="platform/external/webp" path="external/webp" revision="88fe2b83c4b9232cd08729556fd0485d6a6a92cd"/>
   <!-- Information: platform/external/webrtc is tagged with android-sdk-adt_r20 --><project name="platform/external/webrtc" path="external/webrtc" revision="4b6dc1ec58105d17dc8c2f550124cc0621dc93b7"/>
-  <!-- Information: platform/external/wpa_supplicant is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.01.00.19.013 --><project name="platform/external/wpa_supplicant" path="external/wpa_supplicant" revision="a01d37870bbf9892d43e792e5de0683ca41c5497"/>
+  <!-- Information: platform/external/wpa_supplicant is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY_V1.01.00.01.19.039 --><project name="platform/external/wpa_supplicant" path="external/wpa_supplicant" revision="a01d37870bbf9892d43e792e5de0683ca41c5497"/>
   <project name="platform/external/wpa_supplicant_8" path="external/wpa_supplicant_8" revision="6dd24fc3792d71edccef9b09140f9a44b063a553"/>
   <!-- Information: platform/external/zlib is tagged with android-4.0.4_r2.1 --><project name="platform/external/zlib" path="external/zlib" revision="69e5801bd16a495e1c1666669fe827b1ddb8d56b"/>
   <!-- Information: platform/external/yaffs2 is tagged with android-4.0.4-aah_r1 --><project name="platform/external/yaffs2" path="external/yaffs2" revision="6232e2d5ab34a40d710e4b05ab0ec6e3727804e7"/>
   <!-- Information: platform/frameworks/base is tagged with android-4.0.4_r2.1 --><project name="platform/frameworks/base" path="frameworks/base" revision="df331873c8576e0ae34ae1ee3cc258beed373535"/>
-  <!-- Information: platform/frameworks/opt/emoji is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.01.00.19.013 --><project name="platform/frameworks/opt/emoji" path="frameworks/opt/emoji" revision="a95d8db002770469d72dfaf59ff37ac96db29a87"/>
+  <!-- Information: platform/frameworks/opt/emoji is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY_V1.01.00.01.19.039 --><project name="platform/frameworks/opt/emoji" path="frameworks/opt/emoji" revision="a95d8db002770469d72dfaf59ff37ac96db29a87"/>
   <!-- Information: platform/frameworks/support is tagged with android-4.0.4_r2.1 --><project name="platform/frameworks/support" path="frameworks/support" revision="bfc8e01b7b0d5ea70ce89d0409b72b7f7d540f43"/>
   <!-- Information: platform/hardware/libhardware is tagged with android-4.0.4_r2.1 --><project name="platform/hardware/libhardware" path="hardware/libhardware" revision="a9b677fce432b29ab8f61e13796f34880dc0fe0f"/>
   <!-- Information: platform/hardware/libhardware_legacy is tagged with android-4.0.4_r2.1 --><project name="platform/hardware/libhardware_legacy" path="hardware/libhardware_legacy" revision="153d0f1a27e0a157cabb6ca9d0d88248630f5695"/>
   <!-- Information: platform/hardware/ril is tagged with android-4.0.4_r2.1 --><project name="platform/hardware/ril" path="hardware/ril" revision="300105d1487f5238940c18792b879793826b61f4"/>
   <!-- Information: platform/libcore is tagged with android-4.0.4_r2.1 --><project name="platform/libcore" path="libcore" revision="fc294a48d80d9e2b2ac126edf93ad316f5f6cf72"/>
   <!-- Information: platform/ndk is tagged with android-4.0.4_r2.1 --><project name="platform/ndk" path="ndk" revision="2d77f5a05f60029c981f299b222cfe28db18ccf7"/>
   <!-- Information: platform/prebuilt is tagged with tungsten-bootloader-ics-aah --><project name="platform/prebuilt" path="prebuilt" revision="0e104261b6d33f87e9f86ff4249bcc0306ab278b"/>
   <!-- Information: platform/system/bluetooth is tagged with android-4.0.4_r2.1 --><project name="platform/system/bluetooth" path="system/bluetooth" revision="2588cd802f322650ed737dfb7a10e9ad94064e99"/>
   <!-- Information: platform/system/core is tagged with android-4.0.4_r2.1 --><project name="platform/system/core" path="system/core" revision="c2db4ffb874783220abf967ca4ccd0e6cf1ba57f"/>
   <!-- Information: platform/system/extras is tagged with android-4.0.4_r2.1 --><project name="platform/system/extras" path="system/extras" revision="fa351ab265957fa8815df3c4ca1f3c105f253e8b"/>
   <!-- Information: platform/system/media is tagged with android-4.0.4_r2.1 --><project name="platform/system/media" path="system/media" revision="a8eea50f80327f15cb04bbdfee2d1cfcc4c3ce4a"/>
   <!-- Information: platform/system/netd is tagged with android-4.0.4_r2.1 --><project name="platform/system/netd" path="system/netd" revision="3c903b555975fa59d6688a0a6417ac7512c202e7"/>
   <!-- Information: platform/system/vold is tagged with android-4.0.4_r2.1 --><project name="platform/system/vold" path="system/vold" revision="3ad9072a5d6f6bda32123b367545649364e3c11d"/>
 
   <!-- Pandaboard specific things -->
-  <project name="android-device-panda" path="device/ti/panda" remote="b2g" revision="eec93d3e9eb7765f63415e2ad42003a00b5996b4"/>
+  <project name="android-device-panda" path="device/ti/panda" remote="b2g" revision="b0cde710220dc884fbf92934a4d54456ecc0c693"/>
   <!-- Information: platform/hardware/ti/omap4xxx is tagged with android-4.0.4_r2.1 --><project name="platform/hardware/ti/omap4xxx" path="hardware/ti/omap4xxx" revision="8be8e9a68c96b6cf43c08a58e7ecd7708737c599"/>
   <project name="platform/hardware/ti/wlan" path="hardware/ti/wlan" revision="60dfeb6e4448bfed707946ebca6612980f525e69"/>
   <project name="platform/hardware/ti/wpan" path="hardware/ti/wpan" revision="3ece7d9e08052989401e008bc397dbcd2557cfd0"/>
   <project name="Negatus" path="external/negatus" remote="mozilla" revision="83e11def08ae4bc590adda8dfc1de661585adb53"/>
   <project name="orangutan" path="external/orangutan" remote="b2g" revision="1735c3c4d6008d7f9e929d55ed0e06b995156fa2"/>
 
 </manifest>
\ No newline at end of file
--- a/b2g/config/unagi/releng-unagi.tt
+++ b/b2g/config/unagi/releng-unagi.tt
@@ -1,12 +1,12 @@
 [
 {
-"size": 832974796,
-"digest": "0d400c33d769af9573299628c8cc33516ffd21558e4625b4e4d4ce79c23293ea10a62dbd5a6056d2df3ca059bb5950309a667a64d08f35f896c2f30ae63285f6",
+"size": 833575768,
+"digest": "c8362a7cbfa1aa99eb0931fe9b6432496918e362b9ac675107c4177ea1a7ae0cc094970580c804213fb4cbe7ca4c40c30c92e2031482a425dff0a17bea3c94da",
 "algorithm": "sha512",
 "filename": "gonk.tar.xz"
 },
 {
 "size": 8622080,
 "digest": "36681be904b20a52dbebf38b86466026430d59adb0e72428ae7557a442d037eb378d278aab181b04a753821ff0a99b6228380d59f86ddd5fbf291284fe54932b",
 "algorithm": "sha512",
 "filename": "boot.img"
--- a/b2g/config/unagi/sources.xml
+++ b/b2g/config/unagi/sources.xml
@@ -2,95 +2,94 @@
   <!-- This is only a record of which revisions were pulled to generate the
        gonk.tar.xz snapshot referred to by releng-unagi.tt -->
 
   <remote fetch="https://android.googlesource.com/" name="aosp"/>
   <remote fetch="https://git.mozilla.org/b2g" name="b2g"/>
   <remote fetch="git://github.com/mozilla-b2g/" name="b2ggithub"/>
   <remote fetch="git://github.com/mozilla/" name="mozilla"/>
   <remote fetch="git://codeaurora.org/" name="caf"/>
-  <remote fetch="git://android.git.linaro.org/" name="linaro"/>
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <default remote="caf" revision="ics_chocolate_rb4.2" sync-j="4"/>
 
   <!-- Gonk specific things and forks -->
-  <project name="platform_build" path="build" remote="b2g" revision="43434d6cdbf702e6197e0791f19406860284edf6">
+  <project name="platform_build" path="build" remote="b2g" revision="777bee02feb43f8f29644f9d09ea7fe01d03f127">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <!-- Information: fake-dalvik is tagged with B2G_1_0_0_20130125190500 --><project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="8e68d41728675fc72502dc572dec523c2c8abb8a"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="f634b3d50effdd42828cc757c01fdbf74e562a36"/>
   <!-- Information: librecovery is tagged with B2G_1_0_0_20130125190500 --><project name="librecovery" path="librecovery" remote="b2g" revision="601fc18b28c9d7cf6954b281ddd3b705c74a9215"/>
-  <project name="moztt" path="external/moztt" remote="b2g" revision="62f94a26d34c1f1e1846efd58d34363c051e8c66"/>
+  <project name="moztt" path="external/moztt" remote="b2g" revision="e1569feeb10891a55193e36b4a3939742151b05f"/>
 
   <!-- Stock Android things -->
-  <!-- Information: platform/abi/cpp is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY_V1.01.00.01.19.032 --><project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
+  <!-- Information: platform/abi/cpp is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.01.00.19.028 --><project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
   <!-- Information: platform/bionic is tagged with M8960AAAAANLYA100715A --><project name="platform/bionic" path="bionic" revision="cd5dfce80bc3f0139a56b58aca633202ccaee7f8"/>
   <!-- Information: platform/bootable/recovery is tagged with M8960AAAAANLYA100715A --><project name="platform/bootable/recovery" path="bootable/recovery" revision="e0a9ac010df3afaa47ba107192c05ac8b5516435"/>
   <!-- Information: platform/development is tagged with M8960AAAAANLYA100715A --><project name="platform/development" path="development" revision="a384622f5fcb1d2bebb9102591ff7ae91fe8ed2d"/>
   <!-- Information: device/common is tagged with AU_LINUX_ANDROID_ICS_CHOCOLATE.04.00.04.05.324 --><project name="device/common" path="device/common" revision="7c65ea240157763b8ded6154a17d3c033167afb7"/>
   <!-- Information: device/sample is tagged with M8960AAAAANLYA100715A --><project name="device/sample" path="device/sample" revision="c328f3d4409db801628861baa8d279fb8855892f"/>
   <project name="platform_external_apriori" path="external/apriori" remote="b2ggithub" revision="2c3a7113299eb789a076be23449d868b3bfa07fd"/>
   <!-- Information: platform/external/bluetooth/bluez is tagged with M76XXUSNEKNLYA2040 --><project name="platform/external/bluetooth/bluez" path="external/bluetooth/bluez" revision="1023c91c66e9c3bd1132480051993bf7827770f6"/>
-  <!-- Information: platform/external/bluetooth/glib is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY_V1.01.00.01.19.032 --><project name="platform/external/bluetooth/glib" path="external/bluetooth/glib" revision="c6b49241cc1a8950723a5f74f8f4b4f4c3fa970e"/>
+  <!-- Information: platform/external/bluetooth/glib is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY_V1.01.00.01.19.039 --><project name="platform/external/bluetooth/glib" path="external/bluetooth/glib" revision="c6b49241cc1a8950723a5f74f8f4b4f4c3fa970e"/>
   <!-- Information: platform/external/bluetooth/hcidump is tagged with AU_LINUX_ANDROID_ICS_CHOCOLATE.04.00.04.05.324 --><project name="platform/external/bluetooth/hcidump" path="external/bluetooth/hcidump" revision="02b1eb24fbb3d0135a81edb4a2175b1397308d7d"/>
-  <!-- Information: platform/external/bsdiff is tagged with M8930AAAAANLYA2217182 --><project name="platform/external/bsdiff" path="external/bsdiff" revision="81872540236d9bb15cccf963d05b9de48baa5375"/>
-  <!-- Information: platform/external/bzip2 is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY_V1.01.00.01.19.032 --><project name="platform/external/bzip2" path="external/bzip2" revision="048dacdca43eed1534689ececcf2781c63e1e4ba"/>
+  <!-- Information: platform/external/bsdiff is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY_V1.01.00.01.19.039 --><project name="platform/external/bsdiff" path="external/bsdiff" revision="81872540236d9bb15cccf963d05b9de48baa5375"/>
+  <!-- Information: platform/external/bzip2 is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY_V1.01.00.01.19.039 --><project name="platform/external/bzip2" path="external/bzip2" revision="048dacdca43eed1534689ececcf2781c63e1e4ba"/>
   <!-- Information: platform/external/dbus is tagged with M8960AAAAANLYA100715A --><project name="platform/external/dbus" path="external/dbus" revision="c7517b6195dc6926728352113e6cc335da3f9c9e"/>
   <!-- Information: platform/external/dhcpcd is tagged with M8960AAAAANLYA100715A --><project name="platform/external/dhcpcd" path="external/dhcpcd" revision="1e00fb67022d0921af0fead263f81762781b9ffa"/>
-  <!-- Information: platform/external/dnsmasq is tagged with M8930AAAAANLYA2217182 --><project name="platform/external/dnsmasq" path="external/dnsmasq" revision="f621afad94df46204c25fc2593a19d704d2637f5"/>
+  <!-- Information: platform/external/dnsmasq is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.01.00.19.028 --><project name="platform/external/dnsmasq" path="external/dnsmasq" revision="f621afad94df46204c25fc2593a19d704d2637f5"/>
   <project name="platform_external_elfcopy" path="external/elfcopy" remote="b2ggithub" revision="62c1bed1c4505369cac2e72fbe30452a598fb690"/>
   <project name="platform_external_elfutils" path="external/elfutils" remote="b2ggithub" revision="72940dec691fa3255e13df01f8c53b620e446066"/>
-  <!-- Information: platform/external/e2fsprogs is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY_V1.01.00.01.19.032 --><project name="platform/external/e2fsprogs" path="external/e2fsprogs" revision="d5f550bb2f556c5d287f7c8d2b77223654bcec37"/>
-  <!-- Information: platform/external/expat is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY_V1.01.00.01.19.032 --><project name="platform/external/expat" path="external/expat" revision="6df134250feab71edb5915ecaa6268210bca76c5"/>
-  <!-- Information: platform/external/fdlibm is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY_V1.01.00.01.19.032 --><project name="platform/external/fdlibm" path="external/fdlibm" revision="988ffeb12a6e044ae3504838ef1fee3fe0716934"/>
-  <!-- Information: platform/external/flac is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY_V1.01.00.01.19.032 --><project name="platform/external/flac" path="external/flac" revision="5893fbe890f5dab8e4146d2baa4bd2691c0739e0"/>
-  <!-- Information: platform/external/freetype is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY_V1.01.00.01.19.032 --><project name="platform/external/freetype" path="external/freetype" revision="aeb407daf3711a10a27f3bc2223c5eb05158076e"/>
-  <!-- Information: platform/external/giflib is tagged with M8930AAAAANLYA2217182 --><project name="platform/external/giflib" path="external/giflib" revision="b2597268aef084202a8c349d1cc072c03c6e22eb"/>
-  <project name="platform/external/gtest" path="external/gtest" remote="linaro" revision="8c212ebe53bb2baab3575f03069016f1fb11e449"/>
-  <!-- Information: platform/external/harfbuzz is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY_V1.01.00.01.19.032 --><project name="platform/external/harfbuzz" path="external/harfbuzz" revision="116610d63a859521dacf00fb6818ee9ab2e666f6"/>
-  <!-- Information: platform/external/icu4c is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY_V1.01.00.01.19.032 --><project name="platform/external/icu4c" path="external/icu4c" revision="0fa67b93b831c6636ca18b152a1b1b14cc99b034"/>
-  <!-- Information: platform/external/iptables is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY_V1.01.00.01.19.032 --><project name="platform/external/iptables" path="external/iptables" revision="3b2deb17f065c5664bb25e1a28489e5792eb63ff"/>
+  <!-- Information: platform/external/e2fsprogs is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY_V1.01.00.01.19.039 --><project name="platform/external/e2fsprogs" path="external/e2fsprogs" revision="d5f550bb2f556c5d287f7c8d2b77223654bcec37"/>
+  <!-- Information: platform/external/expat is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY_V1.01.00.01.19.039 --><project name="platform/external/expat" path="external/expat" revision="6df134250feab71edb5915ecaa6268210bca76c5"/>
+  <!-- Information: platform/external/fdlibm is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY_V1.01.00.01.19.039 --><project name="platform/external/fdlibm" path="external/fdlibm" revision="988ffeb12a6e044ae3504838ef1fee3fe0716934"/>
+  <!-- Information: platform/external/flac is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY_V1.01.00.01.19.039 --><project name="platform/external/flac" path="external/flac" revision="5893fbe890f5dab8e4146d2baa4bd2691c0739e0"/>
+  <!-- Information: platform/external/freetype is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY_V1.01.00.01.19.039 --><project name="platform/external/freetype" path="external/freetype" revision="aeb407daf3711a10a27f3bc2223c5eb05158076e"/>
+  <!-- Information: platform/external/giflib is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.01.00.19.028 --><project name="platform/external/giflib" path="external/giflib" revision="b2597268aef084202a8c349d1cc072c03c6e22eb"/>
+  <project name="platform/external/gtest" path="external/gtest" revision="8c212ebe53bb2baab3575f03069016f1fb11e449"/>
+  <!-- Information: platform/external/harfbuzz is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY_V1.01.00.01.19.039 --><project name="platform/external/harfbuzz" path="external/harfbuzz" revision="116610d63a859521dacf00fb6818ee9ab2e666f6"/>
+  <!-- Information: platform/external/icu4c is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY_V1.01.00.01.19.039 --><project name="platform/external/icu4c" path="external/icu4c" revision="0fa67b93b831c6636ca18b152a1b1b14cc99b034"/>
+  <!-- Information: platform/external/iptables is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY_V1.01.00.01.19.039 --><project name="platform/external/iptables" path="external/iptables" revision="3b2deb17f065c5664bb25e1a28489e5792eb63ff"/>
   <!-- Information: platform/external/jpeg is tagged with AU_LINUX_ANDROID_ICS_CHOCOLATE.04.00.04.05.324 --><project name="platform/external/jpeg" path="external/jpeg" revision="a62e464d672a4623233180e4023034bf825f066e"/>
-  <!-- Information: platform/external/libgsm is tagged with M8930AAAAANLYA2217182 --><project name="platform/external/libgsm" path="external/libgsm" revision="5e4516958690b9a1b2c98f88eeecba3edd2dbda4"/>
-  <!-- Information: platform/external/liblzf is tagged with M8930AAAAANLYA2217182 --><project name="platform/external/liblzf" path="external/liblzf" revision="6946aa575b0949d045722794850896099d937cbb"/>
-  <!-- Information: platform/external/libnfc-nxp is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY_V1.01.00.01.19.032 --><project name="platform/external/libnfc-nxp" path="external/libnfc-nxp" revision="3a912b065a31a72c63ad56ac224cfeaa933423b6"/>
-  <!-- Information: platform/external/libnl-headers is tagged with M8930AAAAANLYA2217182 --><project name="platform/external/libnl-headers" path="external/libnl-headers" revision="6ccf7349d61f73ac26a0675d735d903ab919c658"/>
+  <!-- Information: platform/external/libgsm is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY_V1.01.00.01.19.039 --><project name="platform/external/libgsm" path="external/libgsm" revision="5e4516958690b9a1b2c98f88eeecba3edd2dbda4"/>
+  <!-- Information: platform/external/liblzf is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY_V1.01.00.01.19.039 --><project name="platform/external/liblzf" path="external/liblzf" revision="6946aa575b0949d045722794850896099d937cbb"/>
+  <!-- Information: platform/external/libnfc-nxp is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.01.00.19.028 --><project name="platform/external/libnfc-nxp" path="external/libnfc-nxp" revision="3a912b065a31a72c63ad56ac224cfeaa933423b6"/>
+  <!-- Information: platform/external/libnl-headers is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY_V1.01.00.01.19.039 --><project name="platform/external/libnl-headers" path="external/libnl-headers" revision="6ccf7349d61f73ac26a0675d735d903ab919c658"/>
   <!-- Information: platform/external/libpng is tagged with M8960AAAAANLYA100715A --><project name="platform/external/libpng" path="external/libpng" revision="9c3730f0efa69f580f03463c237cd928f3196404"/>
   <!-- Information: platform/external/libvpx is tagged with M8960AAAAANLYA1519349 --><project name="platform/external/libvpx" path="external/libvpx" revision="3a40da0d96da5c520e7707aa14f48a80956e20d7"/>
   <!-- Information: platform/external/llvm is tagged with AU_LINUX_ANDROID_ICS_CHOCOLATE.04.00.04.05.324 --><project name="platform/external/llvm" path="external/llvm" revision="bff5923831940309f7d8ddbff5826ca6ed2dc050"/>
   <!-- Information: platform/external/mksh is tagged with AU_LINUX_ANDROID_ICS_CHOCOLATE.04.00.04.05.324 --><project name="platform/external/mksh" path="external/mksh" revision="ec646e8f5e7dac9a77d1de549c6ed92c04d0cd4b"/>
   <!-- Information: platform_external_opensans is tagged with B2G_1_0_0_20130125190500 --><project name="platform_external_opensans" path="external/opensans" remote="b2g" revision="b5b4c226ca1d71e936153cf679dda6d3d60e2354"/>
   <!-- Information: platform/external/openssl is tagged with AU_LINUX_ANDROID_ICS.04.00.04.00.110 --><project name="platform/external/openssl" path="external/openssl" revision="27d333cce9a31c806b4bfa042925f045c727aecd"/>
-  <!-- Information: platform/external/protobuf is tagged with M8930AAAAANLYA2217182 --><project name="platform/external/protobuf" path="external/protobuf" revision="e217977611c52bccde7f7c78e1d3c790c6357431"/>
-  <!-- Information: platform/external/safe-iop is tagged with M8930AAAAANLYA2217182 --><project name="platform/external/safe-iop" path="external/safe-iop" revision="07073634e2e3aa4f518e36ed5dec3aabc549d5fb"/>
+  <!-- Information: platform/external/protobuf is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY_V1.01.00.01.19.039 --><project name="platform/external/protobuf" path="external/protobuf" revision="e217977611c52bccde7f7c78e1d3c790c6357431"/>
+  <!-- Information: platform/external/safe-iop is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY_V1.01.00.01.19.039 --><project name="platform/external/safe-iop" path="external/safe-iop" revision="07073634e2e3aa4f518e36ed5dec3aabc549d5fb"/>
   <!-- Information: screencap-gonk is tagged with B2G_1_0_0_20130125190500 --><project name="screencap-gonk" path="external/screencap-gonk" remote="b2g" revision="e6403c71e9eca8cb943739d5a0a192deac60fc51"/>
   <!-- Information: platform/external/skia is tagged with M8960AAAAANLYA100715A --><project name="platform/external/skia" path="external/skia" revision="7d90c85f2c0e3b747f7c7eff8bc9253b0063b439"/>
   <!-- Information: platform/external/sonivox is tagged with AU_LINUX_ANDROID_ICS_CHOCOLATE.04.00.04.05.324 --><project name="platform/external/sonivox" path="external/sonivox" revision="7c967779dfc61ac1f346e972de91d4bfce7dccbb"/>
-  <!-- Information: platform/external/speex is tagged with M8930AAAAANLYA2217182 --><project name="platform/external/speex" path="external/speex" revision="ebe6230a7f7c69f5a4389f2b09b7b19ef9e94f32"/>
+  <!-- Information: platform/external/speex is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY_V1.01.00.01.19.039 --><project name="platform/external/speex" path="external/speex" revision="ebe6230a7f7c69f5a4389f2b09b7b19ef9e94f32"/>
   <project name="platform/external/sqlite" path="external/sqlite" revision="fb30e613139b8836fdc8e81e166cf3a76e5fa17f"/>
-  <!-- Information: platform/external/stlport is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY_V1.01.00.01.19.032 --><project name="platform/external/stlport" path="external/stlport" revision="a6734e0645fce81c9610de0488b729207bfa576e"/>
-  <!-- Information: platform/external/strace is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY_V1.01.00.01.19.032 --><project name="platform/external/strace" path="external/strace" revision="c9fd2e5ef7d002e12e7cf2512506c84a9414b0fd"/>
-  <!-- Information: platform/external/tagsoup is tagged with M8930AAAAANLYA2217182 --><project name="platform/external/tagsoup" path="external/tagsoup" revision="68c2ec9e0acdb3214b7fb91dbab8c9fab8736817"/>
+  <!-- Information: platform/external/stlport is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY_V1.01.00.01.19.039 --><project name="platform/external/stlport" path="external/stlport" revision="a6734e0645fce81c9610de0488b729207bfa576e"/>
+  <!-- Information: platform/external/strace is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY_V1.01.00.01.19.039 --><project name="platform/external/strace" path="external/strace" revision="c9fd2e5ef7d002e12e7cf2512506c84a9414b0fd"/>
+  <!-- Information: platform/external/tagsoup is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY_V1.01.00.01.19.039 --><project name="platform/external/tagsoup" path="external/tagsoup" revision="68c2ec9e0acdb3214b7fb91dbab8c9fab8736817"/>
   <!-- Information: platform/external/tinyalsa is tagged with AU_LINUX_ANDROID_ICS_CHOCOLATE.04.00.04.05.324 --><project name="platform/external/tinyalsa" path="external/tinyalsa" revision="06cc244ee512c1352215e543615738bc8ac82814"/>
-  <!-- Information: platform/external/tremolo is tagged with M8930AAAAANLYA2217182 --><project name="platform/external/tremolo" path="external/tremolo" revision="25bd78d2392dbdc879ae53382cde9d019f79cf6f"/>
+  <!-- Information: platform/external/tremolo is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY_V1.01.00.01.19.039 --><project name="platform/external/tremolo" path="external/tremolo" revision="25bd78d2392dbdc879ae53382cde9d019f79cf6f"/>
   <!-- Information: unbootimg is tagged with B2G_1_0_0_20130125190500 --><project name="unbootimg" path="external/unbootimg" remote="b2g" revision="9464623d92eb8668544916dc5a8f4f6337d0bc08"/>
-  <!-- Information: platform/external/webp is tagged with M8930AAAAANLYA2217182 --><project name="platform/external/webp" path="external/webp" revision="88fe2b83c4b9232cd08729556fd0485d6a6a92cd"/>
-  <!-- Information: platform/external/webrtc is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY_V1.01.00.01.19.032 --><project name="platform/external/webrtc" path="external/webrtc" revision="137024dc8a2e9251a471e20518a9c3ae06f81f23"/>
-  <!-- Information: platform/external/wpa_supplicant is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY_V1.01.00.01.19.032 --><project name="platform/external/wpa_supplicant" path="external/wpa_supplicant" revision="a01d37870bbf9892d43e792e5de0683ca41c5497"/>
+  <!-- Information: platform/external/webp is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.01.00.19.028 --><project name="platform/external/webp" path="external/webp" revision="88fe2b83c4b9232cd08729556fd0485d6a6a92cd"/>
+  <!-- Information: platform/external/webrtc is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY_V1.01.00.01.19.039 --><project name="platform/external/webrtc" path="external/webrtc" revision="137024dc8a2e9251a471e20518a9c3ae06f81f23"/>
+  <!-- Information: platform/external/wpa_supplicant is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY_V1.01.00.01.19.039 --><project name="platform/external/wpa_supplicant" path="external/wpa_supplicant" revision="a01d37870bbf9892d43e792e5de0683ca41c5497"/>
   <!-- Information: platform/external/hostap is tagged with M8960AAAAANLYA1047 --><project name="platform/external/hostap" path="external/hostap" revision="bf04b0faadbdeb4b7943f2e2c4c5aa59df872bb1"/>
   <!-- Information: platform/external/zlib is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.01.19.008 --><project name="platform/external/zlib" path="external/zlib" revision="f96a1d1ebfdf1cd582210fd09c23d8f59e0ae094"/>
-  <!-- Information: platform/external/yaffs2 is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY_V1.01.00.01.19.032 --><project name="platform/external/yaffs2" path="external/yaffs2" revision="0afa916204c664b3114429637b63af1321a0aeca"/>
+  <!-- Information: platform/external/yaffs2 is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY_V1.01.00.01.19.039 --><project name="platform/external/yaffs2" path="external/yaffs2" revision="0afa916204c664b3114429637b63af1321a0aeca"/>
   <!-- Information: platform/frameworks/base is tagged with M76XXUSNEKNLYA2040 --><project name="platform/frameworks/base" path="frameworks/base" revision="eb2bc75803ca179353c24c364a9c8a8ce23e8b78"/>
-  <!-- Information: platform/frameworks/opt/emoji is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY_V1.01.00.01.19.032 --><project name="platform/frameworks/opt/emoji" path="frameworks/opt/emoji" revision="a95d8db002770469d72dfaf59ff37ac96db29a87"/>
+  <!-- Information: platform/frameworks/opt/emoji is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY_V1.01.00.01.19.039 --><project name="platform/frameworks/opt/emoji" path="frameworks/opt/emoji" revision="a95d8db002770469d72dfaf59ff37ac96db29a87"/>
   <!-- Information: platform/frameworks/support is tagged with AU_LINUX_ANDROID_ICS_CHOCOLATE.04.00.04.05.324 --><project name="platform/frameworks/support" path="frameworks/support" revision="27208692b001981f1806f4f396434f4eac78b909"/>
   <!-- Information: platform/hardware/libhardware is tagged with M8960AAAAANLYA1049B --><project name="platform/hardware/libhardware" path="hardware/libhardware" revision="4a619901847621f8a7305edf42dd07347a140484"/>
   <!-- Information: platform/hardware/libhardware_legacy is tagged with M8960AAAAANLYA153611 --><project name="platform/hardware/libhardware_legacy" path="hardware/libhardware_legacy" revision="87b4d7afa8f854b445e2d0d95091f6f6069f2b30"/>
   <!-- Information: platform/libcore is tagged with M8960AAAAANLYA100715A --><project name="platform/libcore" path="libcore" revision="30841f9fba9ccd5c54f4f079f495994db97f283e"/>
-  <!-- Information: platform/ndk is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY_V1.01.00.01.19.032 --><project name="platform/ndk" path="ndk" revision="9f555971e1481854d5b4dc11b3e6af9fff4f241f"/>
+  <!-- Information: platform/ndk is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY_V1.01.00.01.19.039 --><project name="platform/ndk" path="ndk" revision="9f555971e1481854d5b4dc11b3e6af9fff4f241f"/>
   <!-- Information: platform/prebuilt is tagged with AU_LINUX_ANDROID_ICS_CHOCOLATE.04.00.04.05.324 --><project name="platform/prebuilt" path="prebuilt" revision="447ea790fcc957dde59730ecc2a65ca263bdc733"/>
   <!-- Information: platform/system/bluetooth is tagged with M8960AAAAANLYA100703 --><project name="platform/system/bluetooth" path="system/bluetooth" revision="7772cad4823f1f427ce1d4df84a55982386d6d18"/>
   <!-- Information: platform/system/core is tagged with M76XXUSNEKNLYA2040 --><project name="platform/system/core" path="system/core" revision="bf1970408676ce570b8f4dc3efa038e47552137f"/>
   <!-- Information: platform/system/extras is tagged with AU_LINUX_ANDROID_ICS_CHOCOLATE.04.00.04.05.324 --><project name="platform/system/extras" path="system/extras" revision="01db6c1254e1407740a543f24317fc540fc4c049"/>
   <!-- Information: platform/system/media is tagged with AU_LINUX_ANDROID_ICS_CHOCOLATE.04.00.04.05.324 --><project name="platform/system/media" path="system/media" revision="7f71c7fd362bbd992ff2e0e80f7af5859ad116ad"/>
   <!-- Information: platform/system/netd is tagged with M8960AAAAANLYA1049 --><project name="platform/system/netd" path="system/netd" revision="306e765248e3900041bf2737e9f57b1b5694a4ce"/>
   <!-- Information: platform/system/vold is tagged with M8960AAAAANLYA100715A --><project name="platform/system/vold" path="system/vold" revision="99fff257d53cc045d1460841edca5d901dacfcf5"/>
 
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1037,17 +1037,17 @@ pref("devtools.toolbar.visible", false);
 pref("devtools.gcli.allowSet", false);
 pref("devtools.commands.dir", "");
 
 // Toolbox preferences
 pref("devtools.toolbox.footer.height", 250);
 pref("devtools.toolbox.sidebar.width", 500);
 pref("devtools.toolbox.host", "bottom");
 pref("devtools.toolbox.selectedTool", "webconsole");
-pref("devtools.toolbox.toolbarSpec", '["tilt toggle","scratchpad","resize toggle"]');
+pref("devtools.toolbox.toolbarSpec", '["paintflashing toggle","tilt toggle","scratchpad","resize toggle"]');
 pref("devtools.toolbox.sideEnabled", false);
 
 // Enable the Inspector
 pref("devtools.inspector.enabled", true);
 pref("devtools.inspector.activeSidebar", "ruleview");
 pref("devtools.inspector.markupPreview", false);
 
 // Enable the Layout View
--- a/browser/base/content/browser-social.js
+++ b/browser/base/content/browser-social.js
@@ -1025,17 +1025,21 @@ var SocialToolbar = {
         }, true);
       }
     });
 
     let navBar = document.getElementById("nav-bar");
     let anchor = navBar.getAttribute("mode") == "text" ?
                    document.getAnonymousElementByAttribute(aToolbarButton, "class", "toolbarbutton-text") :
                    document.getAnonymousElementByAttribute(aToolbarButton, "class", "toolbarbutton-icon");
-    panel.openPopup(anchor, "bottomcenter topright", 0, 0, false, false);
+    // Bug 849216 - open the popup in a setTimeout so we avoid the auto-rollup
+    // handling from preventing it being opened in some cases.
+    setTimeout(function() {
+      panel.openPopup(anchor, "bottomcenter topright", 0, 0, false, false);
+    }, 0);
   },
 
   setPanelErrorMessage: function SocialToolbar_setPanelErrorMessage(aNotificationFrame) {
     if (!aNotificationFrame)
       return;
 
     let src = aNotificationFrame.getAttribute("src");
     aNotificationFrame.removeAttribute("src");
--- a/browser/base/content/browser.css
+++ b/browser/base/content/browser.css
@@ -20,18 +20,19 @@ tabbrowser {
 #tabbrowser-tabs:not([overflow="true"]) ~ #alltabs-button,
 #tabbrowser-tabs:not([overflow="true"]) + #new-tab-button,
 #tabbrowser-tabs[overflow="true"] > .tabbrowser-arrowscrollbox > .tabs-newtab-button,
 #TabsToolbar[currentset]:not([currentset*="tabbrowser-tabs,new-tab-button"]) > #tabbrowser-tabs > .tabbrowser-arrowscrollbox > .tabs-newtab-button,
 #TabsToolbar[customizing="true"] > #tabbrowser-tabs > .tabbrowser-arrowscrollbox > .tabs-newtab-button {
   visibility: collapse;
 }
 
-#tabbrowser-tabs:not([overflow="true"])[using-closing-tabs-spacer] ~ #alltabs-button {
-  visibility: hidden; /* temporary space to keep a tab's close button under the cursor */
+#tabbrowser-tabs[overflow=true] > .tabbrowser-arrowscrollbox > .scrollbutton-up[collapsed=true],
+#tabbrowser-tabs[overflow=true] > .tabbrowser-arrowscrollbox > .scrollbutton-down[collapsed=true] {
+  visibility: visible; /* keep a tab's close button under the cursor while it's closing tabs */
 }
 
 .tabbrowser-tab {
   -moz-binding: url("chrome://browser/content/tabbrowser.xml#tabbrowser-tab");
 }
 
 .tabbrowser-tab:not([pinned]) {
   -moz-box-flex: 100;
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -4732,16 +4732,17 @@ var TabsProgressListener = {
     // pages have any privilege themselves.
     // We can't look for this during onLocationChange since at that point the
     // document URI is not yet the about:-uri of the error page.
 
     let doc = aWebProgress.DOMWindow.document;
     if (aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
         Components.isSuccessCode(aStatus) &&
         doc.documentURI.startsWith("about:") &&
+        !doc.documentURI.toLowerCase().startsWith("about:blank") &&
         !doc.documentElement.hasAttribute("hasBrowserHandlers")) {
       // STATE_STOP may be received twice for documents, thus store an
       // attribute to ensure handling it just once.
       doc.documentElement.setAttribute("hasBrowserHandlers", "true");
       aBrowser.addEventListener("click", BrowserOnClick, true);
       aBrowser.addEventListener("pagehide", function onPageHide(event) {
         if (event.target.defaultView.frameElement)
           return;
--- a/browser/base/content/pageinfo/pageInfo.js
+++ b/browser/base/content/pageinfo/pageInfo.js
@@ -809,31 +809,31 @@ function saveMedia()
     }
   } else {
     selectSaveFolder(function(aDirectory) {
       if (aDirectory) {
         var saveAnImage = function(aURIString, aChosenData, aBaseURI) {
           internalSave(aURIString, null, null, null, null, false, "SaveImageTitle",
                        aChosenData, aBaseURI, gDocument);
         };
-      
+
         for (var i = 0; i < rowArray.length; i++) {
           var v = rowArray[i];
           var dir = aDirectory.clone();
           var item = gImageView.data[v][COL_IMAGE_NODE];
           var uriString = gImageView.data[v][COL_IMAGE_ADDRESS];
           var uri = makeURI(uriString);
-  
+
           try {
             uri.QueryInterface(Components.interfaces.nsIURL);
             dir.append(decodeURIComponent(uri.fileName));
           } catch(ex) {
             /* data: uris */
           }
-  
+
           if (i == 0) {
             saveAnImage(uriString, new AutoChosen(dir, uri), makeURI(item.baseURI));
           } else {
             // This delay is a hack which prevents the download manager
             // from opening many times. See bug 377339.
             setTimeout(saveAnImage, 200, uriString, new AutoChosen(dir, uri),
                        makeURI(item.baseURI));
           }
@@ -933,17 +933,17 @@ function makePreview(row)
       var imageRequest = item.getRequest(nsIImageLoadingContent.CURRENT_REQUEST);
       if (imageRequest) {
         mimeType = imageRequest.mimeType;
         var image = imageRequest.image;
         if (image)
           numFrames = image.numFrames;
       }
     }
-    
+
     if (!mimeType)
       mimeType = getContentTypeFromHeaders(cacheEntry);
 
     // if we have a data url, get the MIME type from the url
     if (!mimeType && url.startsWith("data:")) {
       let dataMimeType = /^data:(image\/[^;,]+)/i.exec(url);
       if (dataMimeType)
         mimeType = dataMimeType[1].toLowerCase();
@@ -980,17 +980,17 @@ function makePreview(row)
     var newImage = new Image;
     newImage.id = "thepreviewimage";
     var physWidth = 0, physHeight = 0;
     var width = 0, height = 0;
 
     if ((item instanceof HTMLLinkElement || item instanceof HTMLInputElement ||
          item instanceof HTMLImageElement ||
          item instanceof SVGImageElement ||
-         (item instanceof HTMLObjectElement && mimeType.startsWith("image/")) || isBG) && isProtocolAllowed) {
+         (item instanceof HTMLObjectElement && mimeType && mimeType.startsWith("image/")) || isBG) && isProtocolAllowed) {
       newImage.setAttribute("src", url);
       physWidth = newImage.width || 0;
       physHeight = newImage.height || 0;
 
       // "width" and "height" attributes must be set to newImage,
       // even if there is no "width" or "height attribute in item;
       // otherwise, the preview image cannot be displayed correctly.
       if (!isBG) {
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -2841,27 +2841,23 @@
 
     <handlers>
       <handler event="underflow" phase="capturing"><![CDATA[
         if (event.detail == 0)
           return; // Ignore vertical events
 
         var tabs = document.getBindingParent(this);
 
-        if (tabs.hasAttribute("dontresize") || tabs.hasAttribute("using-closing-tabs-spacer")) {
-          tabs.mTabstrip._scrollButtonUp.style.visibility = "hidden";
-          tabs.mTabstrip._scrollButtonDown.style.visibility = "hidden";
-        } else {
-          tabs.removeAttribute("overflow");
-        }
-
         tabs.tabbrowser._removingTabs.forEach(tabs.tabbrowser.removeTab,
                                               tabs.tabbrowser);
 
-        tabs._positionPinnedTabs();
+        if (!tabs.hasAttribute("dontresize") && !tabs._closingTabsSpacer.style.minWidth) {
+          tabs.removeAttribute("overflow");
+          tabs._positionPinnedTabs();
+        }
       ]]></handler>
       <handler event="overflow"><![CDATA[
         if (event.detail == 0)
           return; // Ignore vertical events
 
         var tabs = document.getBindingParent(this);
         tabs.setAttribute("overflow", "true");
         tabs._positionPinnedTabs();
@@ -3107,79 +3103,77 @@
       <!-- Try to keep the active tab's close button under the mouse cursor -->
       <method name="_lockTabSizing">
         <parameter name="aTab"/>
         <body><![CDATA[
           var tabs = this.tabbrowser.visibleTabs;
           if (!tabs.length)
             return;
 
-          var isEndTab = (aTab._tPos > tabs[tabs.length-1]._tPos);
-          var tabWidth = aTab.getBoundingClientRect().width;
-
-          // Locking is neither in effect nor needed, so let tabs expand normally.
-          if (isEndTab && !this.hasAttribute("dontresize"))
-            return;
-
-          // Let spacer grow to the maximum and lock it, then let tabs expand normally
+          this.tabbrowser.addEventListener("mousemove", this, false);
+          window.addEventListener("mouseout", this, false);
+
+          let isEndTab = (aTab._tPos > tabs[tabs.length-1]._tPos);
           if (isEndTab) {
             let spacer = this._closingTabsSpacer;
+            if (!spacer.style.minWidth)
+              spacer.style.minWidth = 0;
+
+            // Locking is neither in effect nor needed, so let tabs expand normally
+            if (!this.hasAttribute("dontresize"))
+              return;
+
             spacer.style.MozBoxFlex = 1;
             spacer.style.minWidth = getComputedStyle(spacer).width;
             spacer.style.MozBoxFlex = "";
 
             this.setAttribute("dontanimate", "true");
             this.removeAttribute("dontresize");
             this.clientTop;
             this.removeAttribute("dontanimate");
             return;
           }
 
           if (!this.hasAttribute("dontresize")) {
+            let tabWidth = aTab.getBoundingClientRect().width;
             this._delayResizingRule.style.setProperty("max-width", tabWidth + "px", "important");
             this.setAttribute("dontanimate", "true");
             this.setAttribute("dontresize", "true");
             this.clientTop; // flush styles to skip animation; see bug 649247
             this.removeAttribute("dontanimate");
           }
 
           if (!this.mTabstrip._scrollButtonUp.disabled) {
             let spacer = this._closingTabsSpacer;
             let width = parseFloat(spacer.style.minWidth) || 0;
-            width += tabWidth;
+            width += aTab.getBoundingClientRect().width;
 
             if (!this.mTabstrip._scrollButtonDown.disabled) {
               let scrollbox = this.mTabstrip._scrollbox;
               width -= scrollbox.scrollLeftMax - scrollbox.scrollLeft;
             }
 
-            if (width >= 0) {
+            if (width >= 0)
               spacer.style.minWidth = width + "px";
-              this.setAttribute("using-closing-tabs-spacer", "true");
-            }
           }
-
-          this.tabbrowser.addEventListener("mousemove", this, false);
-          window.addEventListener("mouseout", this, false);
         ]]></body>
       </method>
 
       <method name="_unlockTabSizing">
         <body><![CDATA[
           this.tabbrowser.removeEventListener("mousemove", this, false);
           window.removeEventListener("mouseout", this, false);
 
           this._closingTabsSpacer.style.minWidth = "";
-          this.removeAttribute("using-closing-tabs-spacer");
           this.removeAttribute("dontresize");
 
-          if (this.hasAttribute("overflow") && this.mTabstrip._scrollbox.scrollWidth <= this.mTabstrip._scrollbox.clientWidth) {
-            this.mTabstrip._scrollButtonUp.style.visibility = "";
-            this.mTabstrip._scrollButtonDown.style.visibility = "";
+          if (this.hasAttribute("overflow") &&
+              this.mTabstrip._scrollbox.scrollWidth <= this.mTabstrip._scrollbox.clientWidth) {
             this.removeAttribute("overflow");
+            this._positionPinnedTabs();
           }
         ]]></body>
       </method>
 
       <field name="_lastNumPinned">0</field>
       <method name="_positionPinnedTabs">
         <body><![CDATA[
           var numPinned = this.tabbrowser._numPinnedTabs;
--- a/browser/components/Makefile.in
+++ b/browser/components/Makefile.in
@@ -5,23 +5,16 @@
 DEPTH     = @DEPTH@
 topsrcdir = @top_srcdir@
 srcdir    = @srcdir@
 VPATH     = @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 MODULE = browsercomps
-XPIDL_MODULE = browsercompsbase
-
-XPIDLSRCS = \
-  nsIBrowserGlue.idl \
-  nsIBrowserHandler.idl \
-  $(NULL)
-
 EXTRA_COMPONENTS = \
   BrowserComponents.manifest \
   $(NULL)
 
 EXTRA_PP_COMPONENTS = \
   nsBrowserContentHandler.js \
   nsBrowserGlue.js \
   $(NULL)
--- a/browser/components/feeds/public/Makefile.in
+++ b/browser/components/feeds/public/Makefile.in
@@ -7,11 +7,9 @@ DEPTH   = @DEPTH@
 topsrcdir = @top_srcdir@
 srcdir    = @srcdir@
 VPATH   = @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 MODULE = browser-feeds
 
-XPIDLSRCS = nsIFeedResultService.idl nsIWebContentConverterRegistrar.idl nsIFeedWriter.idl
-
 include $(topsrcdir)/config/rules.mk
--- a/browser/components/feeds/public/moz.build
+++ b/browser/components/feeds/public/moz.build
@@ -1,5 +1,11 @@
 # vim: set filetype=python:
 # 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/.
 
+XPIDL_SOURCES += [
+    'nsIFeedResultService.idl',
+    'nsIFeedWriter.idl',
+    'nsIWebContentConverterRegistrar.idl',
+]
+
--- a/browser/components/migration/public/Makefile.in
+++ b/browser/components/migration/public/Makefile.in
@@ -6,12 +6,10 @@ DEPTH		= @DEPTH@
 topsrcdir	= @top_srcdir@
 srcdir		= @srcdir@
 VPATH		= @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 MODULE		= migration
 
-XPIDLSRCS	= nsIBrowserProfileMigrator.idl
-
 include $(topsrcdir)/config/rules.mk
 
--- a/browser/components/migration/public/moz.build
+++ b/browser/components/migration/public/moz.build
@@ -1,5 +1,9 @@
 # vim: set filetype=python:
 # 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/.
 
+XPIDL_SOURCES += [
+    'nsIBrowserProfileMigrator.idl',
+]
+
--- a/browser/components/moz.build
+++ b/browser/components/moz.build
@@ -20,8 +20,16 @@ PARALLEL_DIRS += [
     'migration',
 ]
 
 if CONFIG['MOZ_SAFE_BROWSING']:
     PARALLEL_DIRS += ['safebrowsing']
 
 TEST_DIRS += ['test']
 DIRS += ['build']
+
+XPIDL_SOURCES += [
+    'nsIBrowserGlue.idl',
+    'nsIBrowserHandler.idl',
+]
+
+XPIDL_MODULE = 'browsercompsbase'
+
--- a/browser/components/places/Makefile.in
+++ b/browser/components/places/Makefile.in
@@ -7,9 +7,8 @@ DEPTH		= @DEPTH@
 topsrcdir	= @top_srcdir@
 srcdir		= @srcdir@
 VPATH		= @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 include $(topsrcdir)/config/rules.mk
 
-XPIDL_FLAGS += -I$(topsrcdir)/browser/components/
--- a/browser/components/places/moz.build
+++ b/browser/components/places/moz.build
@@ -1,7 +1,12 @@
 # vim: set filetype=python:
 # 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/.
 
 DIRS += ['src']
 TEST_DIRS += ['tests']
+
+XPIDL_FLAGS += [
+    '-I$(topsrcdir)/browser/components/',
+]
+
--- a/browser/components/places/src/Makefile.in
+++ b/browser/components/places/src/Makefile.in
@@ -16,9 +16,8 @@ EXTRA_COMPONENTS = \
   $(NULL)
 
 EXTRA_JS_MODULES = \
   PlacesUIUtils.jsm \
   $(NULL)
 
 include $(topsrcdir)/config/rules.mk
 
-XPIDL_FLAGS += -I$(topsrcdir)/browser/components
--- a/browser/components/places/src/moz.build
+++ b/browser/components/places/src/moz.build
@@ -1,5 +1,9 @@
 # vim: set filetype=python:
 # 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/.
 
+XPIDL_FLAGS += [
+    '-I$(topsrcdir)/browser/components',
+]
+
--- a/browser/components/search/test/browser_426329.js
+++ b/browser/components/search/test/browser_426329.js
@@ -196,68 +196,61 @@ function test() {
       let exists = textbox._formHistSvc.entryExists(textbox.getAttribute("autocompletesearchparam"), searchEntries[i]);
       ok(exists, "form history entry '" + searchEntries[i] + "' should exist");
     }
     testAutocomplete();
   }
 
   function testAutocomplete() {
     var popup = searchBar.textbox.popup;
-    popup.addEventListener("popupshowing", function() {
+    popup.addEventListener("popupshowing", function testACPopupShowing() {
+      popup.removeEventListener("popupshowing", testACPopupShowing);
       checkMenuEntries(searchEntries);
-      finalize();
-      popup.removeEventListener("popupshowing", this, false);
-    }, false);
+      SimpleTest.executeSoon(finalize);
+    });
     searchBar.textbox.showHistoryPopup();
   }
 
   function finalize() {
     searchBar.value = "";
     while (gBrowser.tabs.length != 1) {
       gBrowser.removeTab(gBrowser.tabs[0]);
     }
     content.location.href = "about:blank";
     var engine = ss.getEngineByName("Bug 426329");
     ss.removeEngine(engine);
   }
 
-  function doOnloadOnce(callback) {
-    gBrowser.addEventListener("DOMContentLoaded", function(event) {
-      gBrowser.removeEventListener("DOMContentLoaded", arguments.callee, true);
-      callback(event);
-    }, true);
-  }
-
   function simulateClick(aEvent, aTarget) {
     var event = document.createEvent("MouseEvent");
     var ctrlKeyArg  = aEvent.ctrlKey  || false;
     var altKeyArg   = aEvent.altKey   || false;
     var shiftKeyArg = aEvent.shiftKey || false;
     var metaKeyArg  = aEvent.metaKey  || false;
     var buttonArg   = aEvent.button   || 0;
     event.initMouseEvent("click", true, true, window,
                           0, 0, 0, 0, 0,
                           ctrlKeyArg, altKeyArg, shiftKeyArg, metaKeyArg,
-                          buttonArg, null); 
+                          buttonArg, null);
     aTarget.dispatchEvent(event);
   }
 
   function expectedURL(aSearchTerms) {
     var textToSubURI = Cc["@mozilla.org/intl/texttosuburi;1"].
                        getService(Ci.nsITextToSubURI);
     var searchArg = textToSubURI.ConvertAndEscape("utf-8", aSearchTerms);
     return ENGINE_HTML_BASE + "?test=" + searchArg;
   }
 
   // modified from toolkit/components/satchel/test/test_form_autocomplete.html
   function checkMenuEntries(expectedValues) {
     var actualValues = getMenuEntries();
     is(actualValues.length, expectedValues.length, "Checking length of expected menu");
     for (var i = 0; i < expectedValues.length; i++)
-      is(actualValues[i], expectedValues[i], "Checking menu entry #"+i);
+      is(actualValues[i], expectedValues[i], "Checking menu entry #" + i);
   }
 
   function getMenuEntries() {
     var entries = [];
     var autocompleteMenu = searchBar.textbox.popup;
     // Could perhaps pull values directly from the controller, but it seems
     // more reliable to test the values that are actually in the tree?
     var column = autocompleteMenu.tree.columns[0];
--- a/browser/components/search/test/browser_contextmenu.js
+++ b/browser/components/search/test/browser_contextmenu.js
@@ -25,74 +25,79 @@ function test() {
         break;
       case "engine-removed":
         Services.obs.removeObserver(observer, "browser-search-engine-modified");
         finish();
         break;
     }
   }
 
+  registerCleanupFunction(finalize);
   Services.obs.addObserver(observer, "browser-search-engine-modified", false);
   ss.addEngine("http://mochi.test:8888/browser/browser/components/search/test/testEngine_mozsearch.xml",
                Ci.nsISearchEngine.DATA_XML, "data:image/x-icon,%00",
                false);
 
   function startTest() {
     contextMenu = document.getElementById("contentAreaContextMenu");
     ok(contextMenu, "Got context menu XUL");
 
     doOnloadOnce(testContextMenu);
     var tab = gBrowser.addTab("data:text/plain;charset=utf8,test%20search");
     gBrowser.selectedTab = tab;
   }
 
   function testContextMenu() {
     function rightClickOnDocument() {
+      info("rightClickOnDocument: " + content.window.location);
       waitForBrowserContextMenu(checkContextMenu);
       var clickTarget = content.document.body;
       var eventDetails = { type: "contextmenu", button: 2 };
       EventUtils.synthesizeMouseAtCenter(clickTarget, eventDetails, content);
     }
 
     // check the search menu item and then perform a search
     function checkContextMenu() {
+      info("checkContextMenu");
       var searchItem = contextMenu.getElementsByAttribute("id", "context-searchselect")[0];
       ok(searchItem, "Got search context menu item");
       is(searchItem.label, 'Search ' + ENGINE_NAME + ' for "test search"', "Check context menu label");
       is(searchItem.disabled, false, "Check that search context menu item is enabled");
+      doOnloadOnce(checkSearchURL);
       searchItem.click();
       contextMenu.hidePopup();
     }
 
-    function checkSearchURL(event){
+    function checkSearchURL(event) {
       is(event.originalTarget.URL,
          "http://mochi.test:8888/browser/browser/components/search/test/?test=test+search&ie=utf-8&client=app&channel=contextsearch",
          "Checking context menu search URL");
       finalize();
     }
 
-    doOnloadOnce(checkSearchURL);
+    var selectionListener = {
+      notifySelectionChanged: function(doc, sel, reason) {
+        if (reason != Ci.nsISelectionListener.SELECTALL_REASON || sel.toString() != "test search")
+          return;
+        info("notifySelectionChanged: Text selected");
+        content.window.getSelection().QueryInterface(Ci.nsISelectionPrivate).
+                                      removeSelectionListener(selectionListener);
+        SimpleTest.executeSoon(rightClickOnDocument);
+      }
+    };
 
+    // add a listener to know when the selection takes effect
+    content.window.getSelection().QueryInterface(Ci.nsISelectionPrivate).
+                                  addSelectionListener(selectionListener);
     // select the text on the page
-    var selectAllItem = contextMenu.getElementsByAttribute("id", "context-selectall")[0];
-    ok(selectAllItem, "Got select all context menu item");
-    selectAllItem.click();
-
-    // wait for the selection to take effect
-    SimpleTest.executeSoon(rightClickOnDocument);
+    goDoCommand('cmd_selectAll');
   }
 
   function finalize() {
     while (gBrowser.tabs.length != 1) {
       gBrowser.removeTab(gBrowser.tabs[0]);
     }
     content.location.href = "about:blank";
     var engine = ss.getEngineByName(ENGINE_NAME);
-    ss.removeEngine(engine);
-  }
-
-  function doOnloadOnce(callback) {
-    gBrowser.addEventListener("DOMContentLoaded", function handleLoad(event) {
-      gBrowser.removeEventListener("DOMContentLoaded", handleLoad, true);
-      callback(event);
-    }, true);
+    if (engine)
+      ss.removeEngine(engine);
   }
 }
--- a/browser/components/search/test/head.js
+++ b/browser/components/search/test/head.js
@@ -15,18 +15,37 @@ function isSubObjectOf(expectedObj, actu
     } else {
       is(actualObj[prop], expectedObj[prop], name + "[" + prop + "]");
     }
   }
 }
 
 function waitForPopupShown(aPopupId, aCallback) {
   let popup = document.getElementById(aPopupId);
+  info("waitForPopupShown: got popup: " + popup.id);
   function onPopupShown() {
-    popup.removeEventListener("popupshown", onPopupShown);
+    info("onPopupShown");
+    removePopupShownListener();
     SimpleTest.executeSoon(aCallback);
   }
+  function removePopupShownListener() {
+    popup.removeEventListener("popupshown", onPopupShown);
+  }
   popup.addEventListener("popupshown", onPopupShown);
+  registerCleanupFunction(removePopupShownListener);
 }
 
 function waitForBrowserContextMenu(aCallback) {
   waitForPopupShown(gBrowser.selectedBrowser.contextMenu, aCallback);
 }
+
+function doOnloadOnce(aCallback) {
+  function doOnloadOnceListener(aEvent) {
+    info("doOnloadOnce: " + aEvent.originalTarget.location);
+    removeDoOnloadOnceListener();
+    aCallback(aEvent);
+  }
+  function removeDoOnloadOnceListener() {
+    gBrowser.removeEventListener("DOMContentLoaded", doOnloadOnceListener);
+  }
+  gBrowser.addEventListener("DOMContentLoaded", doOnloadOnceListener);
+  registerCleanupFunction(removeDoOnloadOnceListener);
+}
--- a/browser/components/search/test/test.html
+++ b/browser/components/search/test/test.html
@@ -1,7 +1,8 @@
 <!DOCTYPE html>
 <html>
 <head>
+  <meta charset="utf-8" />
   <title>Bug 426329</title>
 </head>
 <body></body>
 </html>
--- a/browser/components/sessionstore/Makefile.in
+++ b/browser/components/sessionstore/Makefile.in
@@ -7,14 +7,9 @@ DEPTH		= @DEPTH@
 topsrcdir	= @top_srcdir@
 srcdir		= @srcdir@
 VPATH		= @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 MODULE = sessionstore
 
-XPIDLSRCS = \
-	nsISessionStartup.idl \
-	nsISessionStore.idl \
-  $(NULL)
-
 include $(topsrcdir)/config/rules.mk
--- a/browser/components/sessionstore/moz.build
+++ b/browser/components/sessionstore/moz.build
@@ -1,7 +1,13 @@
 # vim: set filetype=python:
 # 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/.
 
 DIRS += ['src']
 TEST_DIRS += ['test']
+
+XPIDL_SOURCES += [
+    'nsISessionStartup.idl',
+    'nsISessionStore.idl',
+]
+
--- a/browser/components/shell/public/Makefile.in
+++ b/browser/components/shell/public/Makefile.in
@@ -7,19 +7,9 @@ DEPTH   = @DEPTH@
 topsrcdir = @top_srcdir@
 srcdir    = @srcdir@
 VPATH   = @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 MODULE = shellservice
 
-XPIDLSRCS = nsIShellService.idl
-
-ifeq ($(OS_ARCH),WINNT)
-XPIDLSRCS += nsIWindowsShellService.idl
-endif
-
-ifeq (cocoa,$(MOZ_WIDGET_TOOLKIT))
-XPIDLSRCS += nsIMacShellService.idl
-endif
-
 include $(topsrcdir)/config/rules.mk
--- a/browser/components/shell/public/moz.build
+++ b/browser/components/shell/public/moz.build
@@ -1,5 +1,18 @@
 # vim: set filetype=python:
 # 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/.
 
+XPIDL_SOURCES += [
+    'nsIShellService.idl',
+]
+
+if CONFIG['OS_ARCH'] == 'WINNT':
+    XPIDL_SOURCES += [
+        'nsIWindowsShellService.idl',
+    ]
+elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
+    XPIDL_SOURCES += [
+        'nsIMacShellService.idl',
+    ]
+
--- a/browser/components/tabview/test/Makefile.in
+++ b/browser/components/tabview/test/Makefile.in
@@ -41,28 +41,26 @@ include $(topsrcdir)/config/rules.mk
                  browser_tabview_bug597360.js \
                  browser_tabview_bug597399.js \
                  browser_tabview_bug598375.js \
                  browser_tabview_bug598600.js \
                  browser_tabview_bug599048.js \
                  browser_tabview_bug599626.js \
                  browser_tabview_bug600645.js \
                  browser_tabview_bug600812.js \
-                 browser_tabview_bug602432.js \
                  browser_tabview_bug604098.js \
                  browser_tabview_bug606657.js \
                  browser_tabview_bug606905.js \
                  browser_tabview_bug607108.js \
                  browser_tabview_bug608037.js \
                  browser_tabview_bug608153.js \
                  browser_tabview_bug608158.js \
                  browser_tabview_bug608184.js \
                  browser_tabview_bug608405.js \
                  browser_tabview_bug610208.js \
-                 browser_tabview_bug610242.js \
                  browser_tabview_bug612470.js \
                  browser_tabview_bug613541.js \
                  browser_tabview_bug616729.js \
                  browser_tabview_bug616967.js \
                  browser_tabview_bug618816.js \
                  browser_tabview_bug618828.js \
                  browser_tabview_bug619937.js \
                  browser_tabview_bug622835.js \
@@ -98,42 +96,37 @@ include $(topsrcdir)/config/rules.mk
                  browser_tabview_bug634672.js \
                  browser_tabview_bug635696.js \
                  browser_tabview_bug637840.js \
                  browser_tabview_bug640765.js \
                  browser_tabview_bug641802.js \
                  browser_tabview_bug642793.js \
                  browser_tabview_bug643392.js \
                  browser_tabview_bug644097.js \
-                 browser_tabview_bug648882.js \
                  browser_tabview_bug649006.js \
                  browser_tabview_bug649307.js \
                  browser_tabview_bug649319.js \
                  browser_tabview_bug650280_perwindowpb.js \
                  browser_tabview_bug650573.js \
                  browser_tabview_bug651311.js \
                  browser_tabview_bug654295.js \
                  browser_tabview_bug654721.js \
-                 browser_tabview_bug654941.js \
                  browser_tabview_bug655269.js \
                  browser_tabview_bug656778.js \
                  browser_tabview_bug656913.js \
                  browser_tabview_bug659594.js \
                  browser_tabview_bug662266.js \
                  browser_tabview_bug663421.js \
                  browser_tabview_bug665502.js \
                  browser_tabview_bug669694.js \
                  browser_tabview_bug673196.js \
-                 browser_tabview_bug673729.js \
-                 $(filter disabled-temporarily--bug-795265, browser_tabview_bug678374.js) \
                  browser_tabview_bug681599.js \
                  browser_tabview_bug685476.js \
                  browser_tabview_bug685692.js \
                  browser_tabview_bug686654.js \
-                 browser_tabview_bug696602.js \
                  browser_tabview_bug697390.js \
                  browser_tabview_bug705621.js \
                  browser_tabview_bug706430.js \
                  browser_tabview_bug706736.js \
                  browser_tabview_bug707466.js \
                  browser_tabview_bug712203.js \
                  browser_tabview_bug715454.js \
                  browser_tabview_bug716880.js \
@@ -162,11 +155,18 @@ include $(topsrcdir)/config/rules.mk
                  test_bug600645.html \
                  test_bug644097.html \
                  test_bug678374.html \
                  test_bug678374_icon16.png \
                  $(NULL)
 
 
 # browser_tabview_bug597980.js is disabled for leaking, see bug 711907
+# browser_tabview_bug678374.js disabled for intermittent failures (bug 795265)
+# browser_tabview_bug602432.js disabled for intermittent failures (bug 704417)
+# browser_tabview_bug610242.js disabled for intermittent failures (bug 736036)
+# browser_tabview_bug648882.js disabled for intermittent failures (bug 752862)
+# browser_tabview_bug654941.js disabled for intermittent failures (bug 754222)
+# browser_tabview_bug673729.js disabled for intermittent failures (bug 749980)
+# browser_tabview_bug696602.js disabled for intermittent failures (bug 736425)
 
 libs::	$(_BROWSER_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
--- a/browser/devtools/commandline/BuiltinCommands.jsm
+++ b/browser/devtools/commandline/BuiltinCommands.jsm
@@ -10,16 +10,17 @@ const BRAND_SHORT_NAME = Cc["@mozilla.or
                            .GetStringFromName("brandShortName");
 
 this.EXPORTED_SYMBOLS = [ "CmdAddonFlags", "CmdCommands" ];
 
 Cu.import("resource:///modules/devtools/gcli.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/osfile.jsm")
+Cu.import("resource:///modules/devtools/EventEmitter.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "gDevTools",
                                   "resource:///modules/devtools/gDevTools.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "TargetFactory",
                                   "resource:///modules/devtools/Target.jsm");
 
 /* CmdAddon ---------------------------------------------------------------- */
 
@@ -306,190 +307,16 @@ XPCOMUtils.defineLazyModuleGetter(this, 
         return promise;
       }
     });
     module.CmdAddonFlags.addonsLoaded = true;
     Services.obs.notifyObservers(null, "gcli_addon_commands_ready", null);
   });
 }(this));
 
-/* CmdBreak ---------------------------------------------------------------- */
-(function(module) {
-  XPCOMUtils.defineLazyModuleGetter(this, "HUDService",
-                                    "resource:///modules/HUDService.jsm");
-
-  XPCOMUtils.defineLazyModuleGetter(this, "TargetFactory",
-                                    "resource:///modules/devtools/Target.jsm");
-
-  /**
-  * 'break' command
-  */
-  gcli.addCommand({
-    name: "break",
-    description: gcli.lookup("breakDesc"),
-    manual: gcli.lookup("breakManual")
-  });
-
-  /**
-  * 'break list' command
-  */
-  gcli.addCommand({
-    name: "break list",
-    description: gcli.lookup("breaklistDesc"),
-    returnType: "html",
-    exec: function(args, context) {
-      let dbg = getPanel(context, "jsdebugger");
-      if (!dbg) {
-        return gcli.lookup("debuggerStopped");
-      }
-
-      let breakpoints = dbg.getAllBreakpoints();
-
-      if (Object.keys(breakpoints).length === 0) {
-        return gcli.lookup("breaklistNone");
-      }
-
-      let reply = gcli.lookup("breaklistIntro");
-      reply += "<ol>";
-      for each (let breakpoint in breakpoints) {
-        let text = gcli.lookupFormat("breaklistLineEntry",
-                                    [breakpoint.location.url,
-                                      breakpoint.location.line]);
-        reply += "<li>" + text + "</li>";
-      };
-      reply += "</ol>";
-      return reply;
-    }
-  });
-
-  /**
-  * 'break add' command
-  */
-  gcli.addCommand({
-    name: "break add",
-    description: gcli.lookup("breakaddDesc"),
-    manual: gcli.lookup("breakaddManual")
-  });
-
-  /**
-  * 'break add line' command
-  */
-  gcli.addCommand({
-    name: "break add line",
-    description: gcli.lookup("breakaddlineDesc"),
-    params: [
-      {
-        name: "file",
-        type: {
-          name: "selection",
-          data: function(args, context) {
-            let files = [];
-            let dbg = getPanel(context, "jsdebugger");
-            if (dbg) {
-              let sourcesView = dbg.panelWin.DebuggerView.Sources;
-              for (let item in sourcesView) {
-                files.push(item.value);
-              }
-            }
-            return files;
-          }
-        },
-        description: gcli.lookup("breakaddlineFileDesc")
-      },
-      {
-        name: "line",
-        type: { name: "number", min: 1, step: 10 },
-        description: gcli.lookup("breakaddlineLineDesc")
-      }
-    ],
-    returnType: "html",
-    exec: function(args, context) {
-      args.type = "line";
-
-      let dbg = getPanel(context, "jsdebugger");
-      if (!dbg) {
-        return gcli.lookup("debuggerStopped");
-      }
-      var deferred = context.defer();
-      let position = { url: args.file, line: args.line };
-      dbg.addBreakpoint(position, function(aBreakpoint, aError) {
-        if (aError) {
-          deferred.resolve(gcli.lookupFormat("breakaddFailed", [aError]));
-          return;
-        }
-        deferred.resolve(gcli.lookup("breakaddAdded"));
-      });
-      return deferred.promise;
-    }
-  });
-
-
-  /**
-  * 'break del' command
-  */
-  gcli.addCommand({
-    name: "break del",
-    description: gcli.lookup("breakdelDesc"),
-    params: [
-      {
-        name: "breakid",
-        type: {
-          name: "number",
-          min: 0,
-          max: function(args, context) {
-            let dbg = getPanel(context, "jsdebugger");
-            return dbg == null ?
-                null :
-                Object.keys(dbg.getAllBreakpoints()).length - 1;
-          },
-        },
-        description: gcli.lookup("breakdelBreakidDesc")
-      }
-    ],
-    returnType: "html",
-    exec: function(args, context) {
-      let dbg = getPanel(context, "jsdebugger");
-      if (!dbg) {
-        return gcli.lookup("debuggerStopped");
-      }
-
-      let breakpoints = dbg.getAllBreakpoints();
-      let id = Object.keys(breakpoints)[args.breakid];
-      if (!id || !(id in breakpoints)) {
-        return gcli.lookup("breakNotFound");
-      }
-
-      let deferred = context.defer();
-      try {
-        dbg.removeBreakpoint(breakpoints[id], function() {
-          deferred.resolve(gcli.lookup("breakdelRemoved"));
-        });
-      } catch (ex) {
-        // If the debugger has been closed already, don't scare the user.
-        deferred.resolve(gcli.lookup("breakdelRemoved"));
-      }
-      return deferred.promise;
-    }
-  });
-
-  /**
-  * A helper to go from a command context to a debugger panel
-  */
-  function getPanel(context, id) {
-    if (context == null) {
-      return undefined;
-    }
-
-    let gBrowser = context.environment.chromeDocument.defaultView.gBrowser;
-    let target = TargetFactory.forTab(gBrowser.selectedTab);
-    let toolbox = gDevTools.getToolbox(target);
-    return toolbox == null ? undefined : toolbox.getPanel(id);
-  }
-}(this));
-
 /* CmdCalllog -------------------------------------------------------------- */
 
 (function(module) {
   XPCOMUtils.defineLazyGetter(this, "Debugger", function() {
     let JsDebugger = {};
     Components.utils.import("resource://gre/modules/jsdebugger.jsm", JsDebugger);
 
     let global = Components.utils.getGlobalForObject({});
@@ -1133,216 +960,16 @@ XPCOMUtils.defineLazyModuleGetter(this, 
           visible: true,
           typed: command
         });
       });
     }
   }
 }(this));
 
-/* CmdDbg ------------------------------------------------------------------ */
-
-(function(module) {
-  /**
-  * 'dbg' command
-  */
-  gcli.addCommand({
-    name: "dbg",
-    description: gcli.lookup("dbgDesc"),
-    manual: gcli.lookup("dbgManual")
-  });
-
-  /**
-  * 'dbg open' command
-  */
-  gcli.addCommand({
-    name: "dbg open",
-    description: gcli.lookup("dbgOpen"),
-    params: [],
-    exec: function (args, context) {
-      let gBrowser = context.environment.chromeDocument.defaultView.gBrowser;
-      let target = TargetFactory.forTab(gBrowser.selectedTab);
-      return gDevTools.showToolbox(target, "jsdebugger");
-    }
-  });
-
-  /**
-  * 'dbg close' command
-  */
-  gcli.addCommand({
-    name: "dbg close",
-    description: gcli.lookup("dbgClose"),
-    params: [],
-    exec: function (args, context) {
-      let gBrowser = context.environment.chromeDocument.defaultView.gBrowser;
-      let target = TargetFactory.forTab(gBrowser.selectedTab);
-      return gDevTools.closeToolbox(target);
-    }
-  });
-
-  /**
-  * 'dbg interrupt' command
-  */
-  gcli.addCommand({
-    name: "dbg interrupt",
-    description: gcli.lookup("dbgInterrupt"),
-    params: [],
-    exec: function(args, context) {
-      let dbg = getPanel(context, "jsdebugger");
-      if (!dbg) {
-        return gcli.lookup("debuggerStopped");
-      }
-
-      let controller = dbg._controller;
-      let thread = controller.activeThread;
-      if (!thread.paused) {
-        thread.interrupt();
-      }
-    }
-  });
-
-  /**
-  * 'dbg continue' command
-  */
-  gcli.addCommand({
-    name: "dbg continue",
-    description: gcli.lookup("dbgContinue"),
-    params: [],
-    exec: function(args, context) {
-      let dbg = getPanel(context, "jsdebugger");
-      if (!dbg) {
-        return gcli.lookup("debuggerStopped");
-      }
-
-      let controller = dbg._controller;
-      let thread = controller.activeThread;
-      if (thread.paused) {
-        thread.resume();
-      }
-    }
-  });
-
-  /**
-  * 'dbg step' command
-  */
-  gcli.addCommand({
-    name: "dbg step",
-    description: gcli.lookup("dbgStepDesc"),
-    manual: gcli.lookup("dbgStepManual")
-  });
-
-  /**
-  * 'dbg step over' command
-  */
-  gcli.addCommand({
-    name: "dbg step over",
-    description: gcli.lookup("dbgStepOverDesc"),
-    params: [],
-    exec: function(args, context) {
-      let dbg = getPanel(context, "jsdebugger");
-      if (!dbg) {
-        return gcli.lookup("debuggerStopped");
-      }
-
-      let controller = dbg._controller;
-      let thread = controller.activeThread;
-      if (thread.paused) {
-        thread.stepOver();
-      }
-    }
-  });
-
-  /**
-  * 'dbg step in' command
-  */
-  gcli.addCommand({
-    name: 'dbg step in',
-    description: gcli.lookup("dbgStepInDesc"),
-    params: [],
-    exec: function(args, context) {
-      let dbg = getPanel(context, "jsdebugger");
-      if (!dbg) {
-        return gcli.lookup("debuggerStopped");
-      }
-
-      let controller = dbg._controller;
-      let thread = controller.activeThread;
-      if (thread.paused) {
-        thread.stepIn();
-      }
-    }
-  });
-
-  /**
-  * 'dbg step over' command
-  */
-  gcli.addCommand({
-    name: 'dbg step out',
-    description: gcli.lookup("dbgStepOutDesc"),
-    params: [],
-    exec: function(args, context) {
-      let dbg = getPanel(context, "jsdebugger");
-      if (!dbg) {
-        return gcli.lookup("debuggerStopped");
-      }
-
-      let controller = dbg._controller;
-      let thread = controller.activeThread;
-      if (thread.paused) {
-        thread.stepOut();
-      }
-    }
-  });
-
-  /**
-  * 'dbg list' command
-  */
-  gcli.addCommand({
-    name: "dbg list",
-    description: gcli.lookup("dbgListSourcesDesc"),
-    params: [],
-    returnType: "html",
-    exec: function(args, context) {
-      let dbg = getPanel(context, "jsdebugger");
-      let doc = context.environment.chromeDocument;
-      if (!dbg) {
-        return gcli.lookup("debuggerClosed");
-      }
-      let sources = dbg._view.Sources.values;
-      let div = createXHTMLElement(doc, "div");
-      let ol = createXHTMLElement(doc, "ol");
-      sources.forEach(function(src) {
-        let li = createXHTMLElement(doc, "li");
-        li.textContent = src;
-        ol.appendChild(li);
-      });
-      div.appendChild(ol);
-
-      return div;
-    }
-  });
-
-  /**
-  * A helper to create xhtml namespaced elements
-  */
-  function createXHTMLElement(document, tagname) {
-    return document.createElementNS("http://www.w3.org/1999/xhtml", tagname);
-  }
-
-  /**
-  * A helper to go from a command context to a debugger panel
-  */
-  function getPanel(context, id) {
-    let gBrowser = context.environment.chromeDocument.defaultView.gBrowser;
-    let target = TargetFactory.forTab(gBrowser.selectedTab);
-    let toolbox = gDevTools.getToolbox(target);
-    return toolbox == null ? undefined : toolbox.getPanel(id);
-  }
-}(this));
-
 /* CmdEcho ----------------------------------------------------------------- */
 
 (function(module) {
   /**
   * 'echo' command
   */
   gcli.addCommand({
     name: "echo",
@@ -1376,20 +1003,19 @@ XPCOMUtils.defineLazyModuleGetter(this, 
   /**
   * The 'export html' command. This command allows the user to export the page to
   * HTML after they do DOM changes.
   */
   gcli.addCommand({
     name: "export html",
     description: gcli.lookup("exportHtmlDesc"),
     exec: function(args, context) {
-      let document = context.environment.contentDocument;
-      let window = document.defaultView;
-      let page = document.documentElement.outerHTML;
-      window.open('data:text/plain;charset=utf8,' + encodeURIComponent(page));
+      let html = context.environment.document.documentElement.outerHTML;
+      let url = 'data:text/plain;charset=utf8,' + encodeURIComponent(html);
+      context.environment.window.open(url);
     }
   });
 }(this));
 
 /* CmdJsb ------------------------------------------------------------------ */
 
 (function(module) {
   const XMLHttpRequest =
@@ -2056,8 +1682,127 @@ XPCOMUtils.defineLazyModuleGetter(this, 
                         "background-image: url('" + data + "');" +
                         "background-size: 256px " + previewHeight + "px;" +
                         "margin: 4px; display: block");
       div.appendChild(image);
       return div;
     }
   });
 }(this));
+
+/* CmdPaintFlashing ------------------------------------------------------- */
+
+(function(module) {
+  /**
+  * 'paintflashing' command
+  */
+
+  gcli.addCommand({
+    name: 'paintflashing',
+    description: gcli.lookup('paintflashingDesc')
+  });
+
+  gcli.addCommand({
+    name: 'paintflashing on',
+    description: gcli.lookup('paintflashingOnDesc'),
+    manual: gcli.lookup('paintflashingManual'),
+    params: [{
+      group: "options",
+      params: [
+        {
+          type: "boolean",
+          name: "chrome",
+          get hidden() gcli.hiddenByChromePref(),
+          description: gcli.lookup("paintflashingChromeDesc"),
+        }
+      ]
+    }],
+    exec: function(args, context) {
+      var window;
+      if (args.chrome) {
+        window = context.environment.chromeDocument.defaultView;
+      } else {
+        window = context.environment.contentDocument.defaultView;
+      }
+      window.QueryInterface(Ci.nsIInterfaceRequestor).
+             getInterface(Ci.nsIDOMWindowUtils).
+             paintFlashing = true;
+      onPaintFlashingChanged(context);
+    }
+  });
+
+  gcli.addCommand({
+    name: 'paintflashing off',
+    description: gcli.lookup('paintflashingOffDesc'),
+    manual: gcli.lookup('paintflashingManual'),
+    params: [{
+      group: "options",
+      params: [
+        {
+          type: "boolean",
+          name: "chrome",
+          get hidden() gcli.hiddenByChromePref(),
+          description: gcli.lookup("paintflashingChromeDesc"),
+        }
+      ]
+    }],
+    exec: function(args, context) {
+      if (args.chrome) {
+        var window = context.environment.chromeDocument.defaultView;
+      } else {
+        var window = context.environment.contentDocument.defaultView;
+      }
+      window.QueryInterface(Ci.nsIInterfaceRequestor).
+             getInterface(Ci.nsIDOMWindowUtils).
+             paintFlashing = false;
+      onPaintFlashingChanged(context);
+    }
+  });
+
+  gcli.addCommand({
+    name: 'paintflashing toggle',
+    hidden: true,
+    buttonId: "command-button-paintflashing",
+    buttonClass: "command-button",
+    state: {
+      isChecked: function(aTarget) {
+        if (aTarget.isLocalTab) {
+          let window = aTarget.tab.linkedBrowser.contentWindow;
+          let wUtils = window.QueryInterface(Ci.nsIInterfaceRequestor).
+                              getInterface(Ci.nsIDOMWindowUtils);
+          return wUtils.paintFlashing;
+        } else {
+          throw new Error("Unsupported target");
+        }
+      },
+      onChange: function(aTarget, aChangeHandler) {
+        eventEmitter.on("changed", aChangeHandler);
+      },
+      offChange: function(aTarget, aChangeHandler) {
+        eventEmitter.off("changed", aChangeHandler);
+      },
+    },
+    tooltipText: gcli.lookup("paintflashingTooltip"),
+    description: gcli.lookup('paintflashingOnDesc'),
+    manual: gcli.lookup('paintflashingManual'),
+    exec: function(args, context) {
+      var gBrowser = context.environment.chromeDocument.defaultView.gBrowser;
+      var window = gBrowser.contentWindow;
+      var wUtils = window.QueryInterface(Ci.nsIInterfaceRequestor).
+                   getInterface(Ci.nsIDOMWindowUtils);
+      wUtils.paintFlashing = !wUtils.paintFlashing;
+      onPaintFlashingChanged(context);
+    }
+  });
+
+  let eventEmitter = new EventEmitter();
+  function onPaintFlashingChanged(context) {
+    var gBrowser = context.environment.chromeDocument.defaultView.gBrowser;
+    var tab = gBrowser.selectedTab;
+    eventEmitter.emit("changed", tab);
+    function fireChange() {
+      eventEmitter.emit("changed", tab);
+    }
+    var target = TargetFactory.forTab(tab);
+    target.off("navigate", fireChange);
+    target.once("navigate", fireChange);
+  }
+}(this));
--- a/browser/devtools/commandline/Commands.jsm
+++ b/browser/devtools/commandline/Commands.jsm
@@ -3,13 +3,14 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 
 this.EXPORTED_SYMBOLS = [ ];
 
 const Cu = Components.utils;
 
 Cu.import("resource:///modules/devtools/BuiltinCommands.jsm");
+Cu.import("resource:///modules/devtools/CmdDebugger.jsm");
 Cu.import("resource:///modules/devtools/CmdEdit.jsm");
 Cu.import("resource:///modules/devtools/CmdInspect.jsm");
 Cu.import("resource:///modules/devtools/CmdResize.jsm");
 Cu.import("resource:///modules/devtools/CmdTilt.jsm");
 Cu.import("resource:///modules/devtools/CmdScratchpad.jsm");
--- a/browser/devtools/commandline/gcli.jsm
+++ b/browser/devtools/commandline/gcli.jsm
@@ -44,16 +44,19 @@ Components.utils.import("resource:///mod
  * 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.
  */
 
 var mozl10n = {};
 
 (function(aMozl10n) {
+
+  'use strict';
+
   var temp = {};
   Components.utils.import("resource://gre/modules/Services.jsm", temp);
   var stringBundle = temp.Services.strings.createBundle(
           "chrome://browser/locale/devtools/gclicommands.properties");
 
   /**
    * Lookup a string in the GCLI string bundle
    * @param name The name to lookup
@@ -82,16 +85,18 @@ var mozl10n = {};
       throw new Error("Failure in lookupFormat('" + name + "')");
     }
   };
 
 })(mozl10n);
 
 define('gcli/index', ['require', 'exports', 'module' , 'gcli/types/basic', 'gcli/types/command', 'gcli/types/javascript', 'gcli/types/node', 'gcli/types/resource', 'gcli/types/setting', 'gcli/types/selection', 'gcli/settings', 'gcli/ui/intro', 'gcli/ui/focus', 'gcli/ui/fields/basic', 'gcli/ui/fields/javascript', 'gcli/ui/fields/selection', 'gcli/commands/help', 'gcli/commands/pref', 'gcli/canon', 'gcli/ui/ffdisplay'], function(require, exports, module) {
 
+  'use strict';
+
   // Internal startup process. Not exported
   require('gcli/types/basic').startup();
   require('gcli/types/command').startup();
   require('gcli/types/javascript').startup();
   require('gcli/types/node').startup();
   require('gcli/types/resource').startup();
   require('gcli/types/setting').startup();
   require('gcli/types/selection').startup();
@@ -153,20 +158,23 @@ define('gcli/index', ['require', 'export
  *
  * 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/basic', ['require', 'exports', 'module' , 'gcli/l10n', 'gcli/types', 'gcli/types/selection', 'gcli/argument'], function(require, exports, module) {
-
-
-var l10n = require('gcli/l10n');
+define('gcli/types/basic', ['require', 'exports', 'module' , 'util/promise', 'util/util', 'util/l10n', 'gcli/types', 'gcli/types/selection', 'gcli/argument'], function(require, exports, module) {
+
+'use strict';
+
+var Promise = require('util/promise');
+var util = require('util/util');
+var l10n = require('util/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 ArrayConversion = require('gcli/types').ArrayConversion;
 var SelectionType = require('gcli/types/selection').SelectionType;
 
 var BlankArgument = require('gcli/argument').BlankArgument;
@@ -176,26 +184,26 @@ var ArrayArgument = require('gcli/argume
 /**
  * Registration and de-registration.
  */
 exports.startup = function() {
   types.registerType(StringType);
   types.registerType(NumberType);
   types.registerType(BooleanType);
   types.registerType(BlankType);
-  types.registerType(DeferredType);
+  types.registerType(DelegateType);
   types.registerType(ArrayType);
 };
 
 exports.shutdown = function() {
   types.unregisterType(StringType);
   types.unregisterType(NumberType);
   types.unregisterType(BooleanType);
   types.unregisterType(BlankType);
-  types.unregisterType(DeferredType);
+  types.unregisterType(DelegateType);
   types.unregisterType(ArrayType);
 };
 
 
 /**
  * 'string' the most basic string type that doesn't need to convert
  */
 function StringType(typeSpec) {
@@ -207,19 +215,19 @@ StringType.prototype.stringify = functio
   if (value == null) {
     return '';
   }
   return value.toString();
 };
 
 StringType.prototype.parse = function(arg) {
   if (arg.text == null || arg.text === '') {
-    return new Conversion(undefined, arg, Status.INCOMPLETE, '');
-  }
-  return new Conversion(arg.text, arg);
+    return Promise.resolve(new Conversion(undefined, arg, Status.INCOMPLETE, ''));
+  }
+  return Promise.resolve(new Conversion(arg.text, arg));
 };
 
 StringType.prototype.name = 'string';
 
 exports.StringType = StringType;
 
 
 /**
@@ -276,50 +284,50 @@ NumberType.prototype.getMax = function()
       return this._max;
     }
   }
   return undefined;
 };
 
 NumberType.prototype.parse = function(arg) {
   if (arg.text.replace(/^\s*-?/, '').length === 0) {
-    return new Conversion(undefined, arg, Status.INCOMPLETE, '');
+    return Promise.resolve(new Conversion(undefined, arg, Status.INCOMPLETE, ''));
   }
 
   if (!this._allowFloat && (arg.text.indexOf('.') !== -1)) {
-    return new Conversion(undefined, arg, Status.ERROR,
-        l10n.lookupFormat('typesNumberNotInt', [ arg.text ]));
+    var message = l10n.lookupFormat('typesNumberNotInt2', [ arg.text ]);
+    return Promise.resolve(new Conversion(undefined, arg, Status.ERROR, message));
   }
 
   var value;
   if (this._allowFloat) {
     value = parseFloat(arg.text);
   }
   else {
     value = parseInt(arg.text, 10);
   }
 
   if (isNaN(value)) {
-    return new Conversion(undefined, arg, Status.ERROR,
-        l10n.lookupFormat('typesNumberNan', [ arg.text ]));
+    var message = l10n.lookupFormat('typesNumberNan', [ arg.text ]);
+    return Promise.resolve(new Conversion(undefined, arg, Status.ERROR, message));
   }
 
   var max = this.getMax();
   if (max != null && value > max) {
-    return new Conversion(undefined, arg, Status.ERROR,
-        l10n.lookupFormat('typesNumberMax', [ value, max ]));
+    var message = l10n.lookupFormat('typesNumberMax', [ value, max ]);
+    return Promise.resolve(new Conversion(undefined, arg, Status.ERROR, message));
   }
 
   var min = this.getMin();
   if (min != null && value < min) {
-    return new Conversion(undefined, arg, Status.ERROR,
-        l10n.lookupFormat('typesNumberMin', [ value, min ]));
-  }
-
-  return new Conversion(value, arg);
+    var message = l10n.lookupFormat('typesNumberMin', [ value, min ]);
+    return Promise.resolve(new Conversion(undefined, arg, Status.ERROR, message));
+  }
+
+  return Promise.resolve(new Conversion(value, arg));
 };
 
 NumberType.prototype.decrement = function(value) {
   if (typeof value !== 'number' || isNaN(value)) {
     return this.getMax() || 1;
   }
   var newValue = value - this._step;
   // Snap to the nearest incremental of the step
@@ -381,108 +389,113 @@ BooleanType.prototype = Object.create(Se
 
 BooleanType.prototype.lookup = [
   { name: 'false', value: false },
   { name: 'true', value: true }
 ];
 
 BooleanType.prototype.parse = function(arg) {
   if (arg.type === 'TrueNamedArgument') {
-    return new Conversion(true, arg);
+    return Promise.resolve(new Conversion(true, arg));
   }
   if (arg.type === 'FalseNamedArgument') {
-    return new Conversion(false, arg);
+    return Promise.resolve(new Conversion(false, arg));
   }
   return SelectionType.prototype.parse.call(this, arg);
 };
 
 BooleanType.prototype.stringify = function(value) {
   if (value == null) {
     return '';
   }
   return '' + value;
 };
 
 BooleanType.prototype.getBlank = function() {
-  return new Conversion(false, new BlankArgument(), Status.VALID, '', this.lookup);
+  return new Conversion(false, new BlankArgument(), Status.VALID, '',
+                        Promise.resolve(this.lookup));
 };
 
 BooleanType.prototype.name = 'boolean';
 
 exports.BooleanType = BooleanType;
 
 
 /**
  * A type for "we don't know right now, but hope to soon".
  */
-function DeferredType(typeSpec) {
-  if (typeof typeSpec.defer !== 'function') {
-    throw new Error('Instances of DeferredType need typeSpec.defer to be a function that returns a type');
+function DelegateType(typeSpec) {
+  if (typeof typeSpec.delegateType !== 'function') {
+    throw new Error('Instances of DelegateType need typeSpec.delegateType to be a function that returns a type');
   }
   Object.keys(typeSpec).forEach(function(key) {
     this[key] = typeSpec[key];
   }, this);
 }
 
-DeferredType.prototype = Object.create(Type.prototype);
-
-DeferredType.prototype.stringify = function(value) {
-  return this.defer().stringify(value);
-};
-
-DeferredType.prototype.parse = function(arg) {
-  return this.defer().parse(arg);
-};
-
-DeferredType.prototype.decrement = function(value) {
-  var deferred = this.defer();
-  return (deferred.decrement ? deferred.decrement(value) : undefined);
-};
-
-DeferredType.prototype.increment = function(value) {
-  var deferred = this.defer();
-  return (deferred.increment ? deferred.increment(value) : undefined);
-};
-
-DeferredType.prototype.increment = function(value) {
-  var deferred = this.defer();
-  return (deferred.increment ? deferred.increment(value) : undefined);
-};
-
-DeferredType.prototype.getType = function() {
-  return this.defer();
-};
-
-Object.defineProperty(DeferredType.prototype, 'isImportant', {
+/**
+ * Child types should implement this method to return an instance of the type
+ * that should be used. If no type is available, or some sort of temporary
+ * placeholder is required, BlankType can be used.
+ */
+DelegateType.prototype.delegateType = function() {
+  throw new Error('Not implemented');
+};
+
+DelegateType.prototype = Object.create(Type.prototype);
+
+DelegateType.prototype.stringify = function(value) {
+  return this.delegateType().stringify(value);
+};
+
+DelegateType.prototype.parse = function(arg) {
+  return this.delegateType().parse(arg);
+};
+
+DelegateType.prototype.decrement = function(value) {
+  var delegated = this.delegateType();
+  return (delegated.decrement ? delegated.decrement(value) : undefined);
+};
+
+DelegateType.prototype.increment = function(value) {
+  var delegated = this.delegateType();
+  return (delegated.increment ? delegated.increment(value) : undefined);
+};
+
+DelegateType.prototype.getType = function() {
+  return this.delegateType();
+};
+
+Object.defineProperty(DelegateType.prototype, 'isImportant', {
   get: function() {
-    return this.defer().isImportant;
+    return this.delegateType().isImportant;
   },
   enumerable: true
 });
 
-DeferredType.prototype.name = 'deferred';
-
-exports.DeferredType = DeferredType;
-
-
-/**
- * 'blank' is a type for use with DeferredType when we don't know yet.
+DelegateType.prototype.name = 'delegate';
+
+exports.DelegateType = DelegateType;
+
+
+/**
+ * 'blank' is a type for use with DelegateType when we don't know yet.
  * It should not be used anywhere else.
  */
 function BlankType(typeSpec) {
 }
 
 BlankType.prototype = Object.create(Type.prototype);
 
 BlankType.prototype.stringify = function(value) {
   return '';
 };
 
 BlankType.prototype.parse = function(arg) {
-  return new Conversion(undefined, arg);
+  return Promise.resolve(new Conversion(undefined, arg));
 };
 
 BlankType.prototype.name = 'blank';
 
 exports.BlankType = BlankType;
 
 
 /**
@@ -507,31 +520,36 @@ ArrayType.prototype.stringify = function
   if (values == null) {
     return '';
   }
   // BUG 664204: Check for strings with spaces and add quotes
   return values.join(' ');
 };
 
 ArrayType.prototype.parse = function(arg) {
-  if (arg.type === 'ArrayArgument') {
-    var conversions = arg.getArguments().map(function(subArg) {
-      var conversion = this.subtype.parse(subArg);
-      // Hack alert. ArrayConversion needs to be able to answer questions
-      // about the status of individual conversions in addition to the
-      // overall state. This allows us to do that easily.
+  if (arg.type !== 'ArrayArgument') {
+    console.error('non ArrayArgument to ArrayType.parse', arg);
+    throw new Error('non ArrayArgument to ArrayType.parse');
+  }
+
+  // Parse an argument to a conversion
+  // Hack alert. ArrayConversion needs to be able to answer questions about
+  // the status of individual conversions in addition to the overall state.
+  // |subArg.conversion| allows us to do that easily.
+  var subArgParse = function(subArg) {
+    return this.subtype.parse(subArg).then(function(conversion) {
       subArg.conversion = conversion;
       return conversion;
-    }, this);
+    }.bind(this), console.error);
+  }.bind(this);
+
+  var conversionPromises = arg.getArguments().map(subArgParse);
+  return util.all(conversionPromises).then(function(conversions) {
     return new ArrayConversion(conversions, arg);
-  }
-  else {
-    console.error('non ArrayArgument to ArrayType.parse', arg);
-    throw new Error('non ArrayArgument to ArrayType.parse');
-  }
+  });
 };
 
 ArrayType.prototype.getBlank = function(values) {
   return new ArrayConversion([], new ArrayArgument());
 };
 
 ArrayType.prototype.name = 'array';
 
@@ -550,17 +568,914 @@ exports.ArrayType = ArrayType;
  *
  * 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/l10n', ['require', 'exports', 'module' ], function(require, exports, module) {
+define('util/promise', ['require', 'exports', 'module' ], function(require, exports, module) {
+
+  'use strict';
+
+  var imported = {};
+  Components.utils.import("resource://gre/modules/commonjs/sdk/core/promise.js",
+                          imported);
+
+  exports.defer = imported.Promise.defer;
+  exports.resolve = imported.Promise.resolve;
+  exports.reject = imported.Promise.reject;
+
+});
+/*
+ * 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('util/util', ['require', 'exports', 'module' , 'util/promise'], function(require, exports, module) {
+
+'use strict';
+
+/*
+ * A number of DOM manipulation and event handling utilities.
+ */
+
+//------------------------------------------------------------------------------
+
+var eventDebug = false;
+
+/**
+ * Patch up broken console API from node
+ */
+if (eventDebug) {
+  if (console.group == null) {
+    console.group = function() { console.log(arguments); };
+  }
+  if (console.groupEnd == null) {
+    console.groupEnd = function() { console.log(arguments); };
+  }
+}
+
+/**
+ * Useful way to create a name for a handler, used in createEvent()
+ */
+function nameFunction(handler) {
+  var scope = handler.scope ? handler.scope.constructor.name + '.' : '';
+  var name = handler.func.name;
+  if (name) {
+    return scope + name;
+  }
+  for (var prop in handler.scope) {
+    if (handler.scope[prop] === handler.func) {
+      return scope + prop;
+    }
+  }
+  return scope + handler.func;
+}
+
+/**
+ * Create an event.
+ * For use as follows:
+ *
+ *   function Hat() {
+ *     this.putOn = createEvent('Hat.putOn');
+ *     ...
+ *   }
+ *   Hat.prototype.adorn = function(person) {
+ *     this.putOn({ hat: hat, person: person });
+ *     ...
+ *   }
+ *
+ *   var hat = new Hat();
+ *   hat.putOn.add(function(ev) {
+ *     console.log('The hat ', ev.hat, ' has is worn by ', ev.person);
+ *   }, scope);
+ *
+ * @param name Optional name to help with debugging
+ */
+exports.createEvent = function(name) {
+  var handlers = [];
+  var holdFire = false;
+  var heldEvents = [];
+  var eventCombiner = undefined;
+
+  /**
+   * This is how the event is triggered.
+   * @param ev The event object to be passed to the event listeners
+   */
+  var event = function(ev) {
+    if (holdFire) {
+      heldEvents.push(ev);
+      if (eventDebug) {
+        console.log('Held fire: ' + name, ev);
+      }
+      return;
+    }
+
+    if (eventDebug) {
+      console.group('Fire: ' + name + ' to ' + handlers.length + ' listeners', ev);
+    }
+
+    // Use for rather than forEach because it step debugs better, which is
+    // important for debugging events
+    for (var i = 0; i < handlers.length; i++) {
+      var handler = handlers[i];
+      if (eventDebug) {
+        console.log(nameFunction(handler));
+      }
+      handler.func.call(handler.scope, ev);
+    }
+
+    if (eventDebug) {
+      console.groupEnd();
+    }
+  };
+
+  /**
+   * Add a new handler function
+   * @param func The function to call when this event is triggered
+   * @param scope Optional 'this' object for the function call
+   */
+  event.add = function(func, scope) {
+    if (eventDebug) {
+      console.log('Adding listener to ' + name);
+    }
+
+    handlers.push({ func: func, scope: scope });
+  };
+
+  /**
+   * Remove a handler function added through add(). Both func and scope must
+   * be strict equals (===) the values used in the call to add()
+   * @param func The function to call when this event is triggered
+   * @param scope Optional 'this' object for the function call
+   */
+  event.remove = function(func, scope) {
+    if (eventDebug) {
+      console.log('Removing listener from ' + name);
+    }
+
+    var found = false;
+    handlers = handlers.filter(function(test) {
+      var match = (test.func === func && test.scope === scope);
+      if (match) {
+        found = true;
+      }
+      return !match;
+    });
+    if (!found) {
+      console.warn('Handler not found. Attached to ' + name);
+    }
+  };
+
+  /**
+   * Remove all handlers.
+   * Reset the state of this event back to it's post create state
+   */
+  event.removeAll = function() {
+    handlers = [];
+  };
+
+  /**
+   * Temporarily prevent this event from firing.
+   * @see resumeFire(ev)
+   */
+  event.holdFire = function() {
+    if (eventDebug) {
+      console.group('Holding fire: ' + name);
+    }
+
+    holdFire = true;
+  };
+
+  /**
+   * Resume firing events.
+   * If there are heldEvents, then we fire one event to cover them all. If an
+   * event combining function has been provided then we use that to combine the
+   * events. Otherwise the last held event is used.
+   * @see holdFire()
+   */
+  event.resumeFire = function() {
+    if (eventDebug) {
+      console.groupEnd('Resume fire: ' + name);
+    }
+
+    if (holdFire !== true) {
+      throw new Error('Event not held: ' + name);
+    }
+
+    holdFire = false;
+    if (heldEvents.length === 0) {
+      return;
+    }
+
+    if (heldEvents.length === 1) {
+      event(heldEvents[0]);
+    }
+    else {
+      var first = heldEvents[0];
+      var last = heldEvents[heldEvents.length - 1];
+      if (eventCombiner) {
+        event(eventCombiner(first, last, heldEvents));
+      }
+      else {
+        event(last);
+      }
+    }
+
+    heldEvents = [];
+  };
+
+  /**
+   * When resumeFire has a number of events to combine, by default it just
+   * picks the last, however you can provide an eventCombiner which returns a
+   * combined event.
+   * eventCombiners will be passed 3 parameters:
+   * - first The first event to be held
+   * - last The last event to be held
+   * - all An array containing all the held events
+   * The return value from an eventCombiner is expected to be an event object
+   */
+  Object.defineProperty(event, 'eventCombiner', {
+    set: function(newEventCombiner) {
+      if (typeof newEventCombiner !== 'function') {
+        throw new Error('eventCombiner is not a function');
+      }
+      eventCombiner = newEventCombiner;
+    },
+
+    enumerable: true
+  });
+
+  return event;
+};
+
+//------------------------------------------------------------------------------
+
+var Promise = require('util/promise');
+
+/**
+ * Implementation of 'promised', while we wait for bug 790195 to be fixed.
+ * @see Consuming promises in https://addons.mozilla.org/en-US/developers/docs/sdk/latest/modules/sdk/core/promise.html
+ * @see https://bugzilla.mozilla.org/show_bug.cgi?id=790195
+ * @see https://github.com/mozilla/addon-sdk/blob/master/packages/api-utils/lib/promise.js#L179
+ */
+exports.promised = (function() {
+  // Note: Define shortcuts and utility functions here in order to avoid
+  // slower property accesses and unnecessary closure creations on each
+  // call of this popular function.
+
+  var call = Function.call;
+  var concat = Array.prototype.concat;
+
+  // Utility function that does following:
+  // execute([ f, self, args...]) => f.apply(self, args)
+  function execute(args) { return call.apply(call, args); }
+
+  // Utility function that takes promise of `a` array and maybe promise `b`
+  // as arguments and returns promise for `a.concat(b)`.
+  function promisedConcat(promises, unknown) {
+    return promises.then(function(values) {
+      return Promise.resolve(unknown).then(function(value) {
+        return values.concat([ value ]);
+      });
+    });
+  }
+
+  return function promised(f, prototype) {
+    /**
+    Returns a wrapped `f`, which when called returns a promise that resolves to
+    `f(...)` passing all the given arguments to it, which by the way may be
+    promises. Optionally second `prototype` argument may be provided to be used
+    a prototype for a returned promise.
+
+    ## Example
+
+    var promise = promised(Array)(1, promise(2), promise(3))
+    promise.then(console.log) // => [ 1, 2, 3 ]
+    **/
+
+    return function promised() {
+      // create array of [ f, this, args... ]
+      return concat.apply([ f, this ], arguments).
+          // reduce it via `promisedConcat` to get promised array of fulfillments
+          reduce(promisedConcat, Promise.resolve([], prototype)).
+          // finally map that to promise of `f.apply(this, args...)`
+          then(execute);
+    };
+  };
+})();
+
+/**
+ * Convert an array of promises to a single promise, which is resolved (with an
+ * array containing resolved values) only when all the component promises are
+ * resolved.
+ */
+exports.all = exports.promised(Array);
+
+/**
+ * Utility to convert a resolved promise to a concrete value.
+ * Warning: This is something of an experiment. The alternative of mixing
+ * concrete/promise return values could be better.
+ */
+exports.synchronize = function(promise) {
+  if (promise == null || typeof promise.then !== 'function') {
+    return promise;
+  }
+  var failure = undefined;
+  var reply = undefined;
+  var onDone = function(value) {
+    failure = false;
+    reply = value;
+  };
+  var onError = function (value) {
+    failure = true;
+    reply = value;
+  };
+  promise.then(onDone, onError);
+  if (failure === undefined) {
+    throw new Error('non synchronizable promise');
+  }
+  if (failure) {
+    throw reply;
+  }
+  return reply;
+};
+
+/**
+ * promiseMap is roughly like Array.map except that the action is taken to be
+ * something that completes asynchronously, returning a promise.
+ * @param array An array of objects to enumerate
+ * @param action A function to call for each member of the array
+ * @param scope Optional object to use as 'this' for the function calls
+ * @return A promise which is resolved (with an array of resolution values)
+ * when all the array members have been passed to the action function, and
+ * rejected as soon as any of the action function calls failsĀ 
+ */
+exports.promiseEach = function(array, action, scope) {
+  if (array.length === 0) {
+    return Promise.resolve([]);
+  }
+
+  var deferred = Promise.defer();
+
+  var callNext = function(index) {
+    var replies = [];
+    var promiseReply = action.call(scope, array[index]);
+    Promise.resolve(promiseReply).then(function(reply) {
+      replies[index] = reply;
+
+      var nextIndex = index + 1;
+      if (nextIndex >= array.length) {
+        deferred.resolve(replies);
+      }
+      else {
+        callNext(nextIndex);
+      }
+    }).then(null, function(ex) {
+      deferred.reject(ex);
+    });
+  };
+
+  callNext(0);
+  return deferred.promise;
+};
+
+
+//------------------------------------------------------------------------------
+
+/**
+ * XHTML namespace
+ */
+exports.NS_XHTML = 'http://www.w3.org/1999/xhtml';
+
+/**
+ * XUL namespace
+ */
+exports.NS_XUL = 'http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul';
+
+/**
+ * Create an HTML or XHTML element depending on whether the document is HTML
+ * or XML based. Where HTML/XHTML elements are distinguished by whether they
+ * are created using doc.createElementNS('http://www.w3.org/1999/xhtml', tag)
+ * or doc.createElement(tag)
+ * If you want to create a XUL element then you don't have a problem knowing
+ * what namespace you want.
+ * @param doc The document in which to create the element
+ * @param tag The name of the tag to create
+ * @returns The created element
+ */
+exports.createElement = function(doc, tag) {
+  if (exports.isXmlDocument(doc)) {
+    return doc.createElementNS(exports.NS_XHTML, tag);
+  }
+  else {
+    return doc.createElement(tag);
+  }
+};
+
+/**
+ * Remove all the child nodes from this node
+ * @param elem The element that should have it's children removed
+ */
+exports.clearElement = function(elem) {
+  while (elem.hasChildNodes()) {
+    elem.removeChild(elem.firstChild);
+  }
+};
+
+var isAllWhitespace = /^\s*$/;
+
+/**
+ * Iterate over the children of a node looking for TextNodes that have only
+ * whitespace content and remove them.
+ * This utility is helpful when you have a template which contains whitespace
+ * so it looks nice, but where the whitespace interferes with the rendering of
+ * the page
+ * @param elem The element which should have blank whitespace trimmed
+ * @param deep Should this node removal include child elements
+ */
+exports.removeWhitespace = function(elem, deep) {
+  var i = 0;
+  while (i < elem.childNodes.length) {
+    var child = elem.childNodes.item(i);
+    if (child.nodeType === 3 /*Node.TEXT_NODE*/ &&
+        isAllWhitespace.test(child.textContent)) {
+      elem.removeChild(child);
+    }
+    else {
+      if (deep && child.nodeType === 1 /*Node.ELEMENT_NODE*/) {
+        exports.removeWhitespace(child, deep);
+      }
+      i++;
+    }
+  }
+};
+
+/**
+ * Create a style element in the document head, and add the given CSS text to
+ * it.
+ * @param cssText The CSS declarations to append
+ * @param doc The document element to work from
+ * @param id Optional id to assign to the created style tag. If the id already
+ * exists on the document, we do not add the CSS again.
+ */
+exports.importCss = function(cssText, doc, id) {
+  if (!cssText) {
+    return undefined;
+  }
+
+  doc = doc || document;
+
+  if (!id) {
+    id = 'hash-' + hash(cssText);
+  }
+
+  var found = doc.getElementById(id);
+  if (found) {
+    if (found.tagName.toLowerCase() !== 'style') {
+      console.error('Warning: importCss passed id=' + id +
+              ', but that pre-exists (and isn\'t a style tag)');
+    }
+    return found;
+  }
+
+  var style = exports.createElement(doc, 'style');
+  style.id = id;
+  style.appendChild(doc.createTextNode(cssText));
+
+  var head = doc.getElementsByTagName('head')[0] || doc.documentElement;
+  head.appendChild(style);
+
+  return style;
+};
+
+/**
+ * Simple hash function which happens to match Java's |String.hashCode()|
+ * Done like this because I we don't need crypto-security, but do need speed,
+ * and I don't want to spend a long time working on it.
+ * @see http://werxltd.com/wp/2010/05/13/javascript-implementation-of-javas-string-hashcode-method/
+ */
+function hash(str) {
+  var hash = 0;
+  if (str.length == 0) {
+    return hash;
+  }
+  for (var i = 0; i < str.length; i++) {
+    var character = str.charCodeAt(i);
+    hash = ((hash << 5) - hash) + character;
+    hash = hash & hash; // Convert to 32bit integer
+  }
+  return hash;
+}
+
+/**
+ * Shortcut for clearElement/createTextNode/appendChild to make up for the lack
+ * of standards around textContent/innerText
+ */
+exports.setTextContent = function(elem, text) {
+  exports.clearElement(elem);
+  var child = elem.ownerDocument.createTextNode(text);
+  elem.appendChild(child);
+};
+
+/**
+ * There are problems with innerHTML on XML documents, so we need to do a dance
+ * using document.createRange().createContextualFragment() when in XML mode
+ */
+exports.setContents = function(elem, contents) {
+  if (typeof HTMLElement !== 'undefined' && contents instanceof HTMLElement) {
+    exports.clearElement(elem);
+    elem.appendChild(contents);
+    return;
+  }
+
+  if ('innerHTML' in elem) {
+    elem.innerHTML = contents;
+  }
+  else {
+    try {
+      var ns = elem.ownerDocument.documentElement.namespaceURI;
+      if (!ns) {
+        ns = exports.NS_XHTML;
+      }
+      exports.clearElement(elem);
+      contents = '<div xmlns="' + ns + '">' + contents + '</div>';
+      var range = elem.ownerDocument.createRange();
+      var child = range.createContextualFragment(contents).firstChild;
+      while (child.hasChildNodes()) {
+        elem.appendChild(child.firstChild);
+      }
+    }
+    catch (ex) {
+      console.error('Bad XHTML', ex);
+      console.trace();
+      throw ex;
+    }
+  }
+};
+
+/**
+ * Load some HTML into the given document and return a DOM element.
+ * This utility assumes that the html has a single root (other than whitespace)
+ */
+exports.toDom = function(document, html) {
+  var div = exports.createElement(document, 'div');
+  exports.setContents(div, html);
+  return div.children[0];
+};
+
+/**
+ * How to detect if we're in an XML document.
+ * In a Mozilla we check that document.xmlVersion = null, however in Chrome
+ * we use document.contentType = undefined.
+ * @param doc The document element to work from (defaulted to the global
+ * 'document' if missing
+ */
+exports.isXmlDocument = function(doc) {
+  doc = doc || document;
+  // Best test for Firefox
+  if (doc.contentType && doc.contentType != 'text/html') {
+    return true;
+  }
+  // Best test for Chrome
+  if (doc.xmlVersion != null) {
+    return true;
+  }
+  return false;
+};
+
+/**
+ * Find the position of [element] in [nodeList].
+ * @returns an index of the match, or -1 if there is no match
+ */
+function positionInNodeList(element, nodeList) {
+  for (var i = 0; i < nodeList.length; i++) {
+    if (element === nodeList[i]) {
+      return i;
+    }
+  }
+  return -1;
+}
+
+/**
+ * Find a unique CSS selector for a given element
+ * @returns a string such that ele.ownerDocument.querySelector(reply) === ele
+ * and ele.ownerDocument.querySelectorAll(reply).length === 1
+ */
+exports.findCssSelector = function(ele) {
+  var document = ele.ownerDocument;
+  if (ele.id && document.getElementById(ele.id) === ele) {
+    return '#' + ele.id;
+  }
+
+  // Inherently unique by tag name
+  var tagName = ele.tagName.toLowerCase();
+  if (tagName === 'html') {
+    return 'html';
+  }
+  if (tagName === 'head') {
+    return 'head';
+  }
+  if (tagName === 'body') {
+    return 'body';
+  }
+
+  if (ele.parentNode == null) {
+    console.log('danger: ' + tagName);
+  }
+
+  // We might be able to find a unique class name
+  var selector, index, matches;
+  if (ele.classList.length > 0) {
+    for (var i = 0; i < ele.classList.length; i++) {
+      // Is this className unique by itself?
+      selector = '.' + ele.classList.item(i);
+      matches = document.querySelectorAll(selector);
+      if (matches.length === 1) {
+        return selector;
+      }
+      // Maybe it's unique with a tag name?
+      selector = tagName + selector;
+      matches = document.querySelectorAll(selector);
+      if (matches.length === 1) {
+        return selector;
+      }
+      // Maybe it's unique using a tag name and nth-child
+      index = positionInNodeList(ele, ele.parentNode.children) + 1;
+      selector = selector + ':nth-child(' + index + ')';
+      matches = document.querySelectorAll(selector);
+      if (matches.length === 1) {
+        return selector;
+      }
+    }
+  }
+
+  // So we can be unique w.r.t. our parent, and use recursion
+  index = positionInNodeList(ele, ele.parentNode.children) + 1;
+  selector = exports.findCssSelector(ele.parentNode) + ' > ' +
+          tagName + ':nth-child(' + index + ')';
+
+  return selector;
+};
+
+/**
+ * Work out the path for images.
+ */
+exports.createUrlLookup = function(callingModule) {
+  return function imageUrl(path) {
+    try {
+      return require('text!gcli/ui/' + path);
+    }
+    catch (ex) {
+      // Under node/unamd callingModule is provided by node. This code isn't
+      // the right answer but it's enough to pass all the unit tests and get
+      // test coverage information, which is all we actually care about here.
+      if (callingModule.filename) {
+        return callingModule.filename + path;
+      }
+
+      var filename = callingModule.id.split('/').pop() + '.js';
+
+      if (callingModule.uri.substr(-filename.length) !== filename) {
+        console.error('Can\'t work out path from module.uri/module.id');
+        return path;
+      }
+
+      if (callingModule.uri) {
+        var end = callingModule.uri.length - filename.length - 1;
+        return callingModule.uri.substr(0, end) + '/' + path;
+      }
+
+      return filename + '/' + path;
+    }
+  };
+};
+
+/**
+ * Helper to find the 'data-command' attribute and call some action on it.
+ * @see |updateCommand()| and |executeCommand()|
+ */
+function withCommand(element, action) {
+  var command = element.getAttribute('data-command');
+  if (!command) {
+    command = element.querySelector('*[data-command]')
+            .getAttribute('data-command');
+  }
+
+  if (command) {
+    action(command);
+  }
+  else {
+    console.warn('Missing data-command for ' + util.findCssSelector(element));
+  }
+}
+
+/**
+ * Update the requisition to contain the text of the clicked element
+ * @param element The clicked element, containing either a data-command
+ * attribute directly or in a nested element, from which we get the command
+ * to be executed.
+ * @param context Either a Requisition or an ExecutionContext or another object
+ * that contains an |update()| function that follows a similar contract.
+ */
+exports.updateCommand = function(element, context) {
+  withCommand(element, function(command) {
+    context.update(command);
+  });
+};
+
+/**
+ * Execute the text contained in the element that was clicked
+ * @param element The clicked element, containing either a data-command
+ * attribute directly or in a nested element, from which we get the command
+ * to be executed.
+ * @param context Either a Requisition or an ExecutionContext or another object
+ * that contains an |update()| function that follows a similar contract.
+ */
+exports.executeCommand = function(element, context) {
+  withCommand(element, function(command) {
+    context.exec({
+      visible: true,
+      typed: command
+    });
+  });
+};
+
+
+//------------------------------------------------------------------------------
+
+/**
+ * Keyboard handling is a mess. http://unixpapa.com/js/key.html
+ * It would be good to use DOM L3 Keyboard events,
+ * http://www.w3.org/TR/2010/WD-DOM-Level-3-Events-20100907/#events-keyboardevents
+ * however only Webkit supports them, and there isn't a shim on Monernizr:
+ * https://github.com/Modernizr/Modernizr/wiki/HTML5-Cross-browser-Polyfills
+ * and when the code that uses this KeyEvent was written, nothing was clear,
+ * so instead, we're using this unmodern shim:
+ * http://stackoverflow.com/questions/5681146/chrome-10-keyevent-or-something-similar-to-firefoxs-keyevent
+ * See BUG 664991: GCLI's keyboard handling should be updated to use DOM-L3
+ * https://bugzilla.mozilla.org/show_bug.cgi?id=664991
+ */
+if (typeof 'KeyEvent' === 'undefined') {
+  exports.KeyEvent = this.KeyEvent;
+}
+else {
+  exports.KeyEvent = {
+    DOM_VK_CANCEL: 3,
+    DOM_VK_HELP: 6,
+    DOM_VK_BACK_SPACE: 8,
+    DOM_VK_TAB: 9,
+    DOM_VK_CLEAR: 12,
+    DOM_VK_RETURN: 13,
+    DOM_VK_ENTER: 14,
+    DOM_VK_SHIFT: 16,
+    DOM_VK_CONTROL: 17,
+    DOM_VK_ALT: 18,
+    DOM_VK_PAUSE: 19,
+    DOM_VK_CAPS_LOCK: 20,
+    DOM_VK_ESCAPE: 27,
+    DOM_VK_SPACE: 32,
+    DOM_VK_PAGE_UP: 33,
+    DOM_VK_PAGE_DOWN: 34,
+    DOM_VK_END: 35,
+    DOM_VK_HOME: 36,
+    DOM_VK_LEFT: 37,
+    DOM_VK_UP: 38,
+    DOM_VK_RIGHT: 39,
+    DOM_VK_DOWN: 40,
+    DOM_VK_PRINTSCREEN: 44,
+    DOM_VK_INSERT: 45,
+    DOM_VK_DELETE: 46,
+    DOM_VK_0: 48,
+    DOM_VK_1: 49,
+    DOM_VK_2: 50,
+    DOM_VK_3: 51,
+    DOM_VK_4: 52,
+    DOM_VK_5: 53,
+    DOM_VK_6: 54,
+    DOM_VK_7: 55,
+    DOM_VK_8: 56,
+    DOM_VK_9: 57,
+    DOM_VK_SEMICOLON: 59,
+    DOM_VK_EQUALS: 61,
+    DOM_VK_A: 65,
+    DOM_VK_B: 66,
+    DOM_VK_C: 67,
+    DOM_VK_D: 68,
+    DOM_VK_E: 69,
+    DOM_VK_F: 70,
+    DOM_VK_G: 71,
+    DOM_VK_H: 72,
+    DOM_VK_I: 73,
+    DOM_VK_J: 74,
+    DOM_VK_K: 75,
+    DOM_VK_L: 76,
+    DOM_VK_M: 77,
+    DOM_VK_N: 78,
+    DOM_VK_O: 79,
+    DOM_VK_P: 80,
+    DOM_VK_Q: 81,
+    DOM_VK_R: 82,
+    DOM_VK_S: 83,
+    DOM_VK_T: 84,
+    DOM_VK_U: 85,
+    DOM_VK_V: 86,
+    DOM_VK_W: 87,
+    DOM_VK_X: 88,
+    DOM_VK_Y: 89,
+    DOM_VK_Z: 90,
+    DOM_VK_CONTEXT_MENU: 93,
+    DOM_VK_NUMPAD0: 96,
+    DOM_VK_NUMPAD1: 97,
+    DOM_VK_NUMPAD2: 98,
+    DOM_VK_NUMPAD3: 99,
+    DOM_VK_NUMPAD4: 100,
+    DOM_VK_NUMPAD5: 101,
+    DOM_VK_NUMPAD6: 102,
+    DOM_VK_NUMPAD7: 103,
+    DOM_VK_NUMPAD8: 104,
+    DOM_VK_NUMPAD9: 105,
+    DOM_VK_MULTIPLY: 106,
+    DOM_VK_ADD: 107,
+    DOM_VK_SEPARATOR: 108,
+    DOM_VK_SUBTRACT: 109,
+    DOM_VK_DECIMAL: 110,
+    DOM_VK_DIVIDE: 111,
+    DOM_VK_F1: 112,
+    DOM_VK_F2: 113,
+    DOM_VK_F3: 114,
+    DOM_VK_F4: 115,
+    DOM_VK_F5: 116,
+    DOM_VK_F6: 117,
+    DOM_VK_F7: 118,
+    DOM_VK_F8: 119,
+    DOM_VK_F9: 120,
+    DOM_VK_F10: 121,
+    DOM_VK_F11: 122,
+    DOM_VK_F12: 123,
+    DOM_VK_F13: 124,
+    DOM_VK_F14: 125,
+    DOM_VK_F15: 126,
+    DOM_VK_F16: 127,
+    DOM_VK_F17: 128,
+    DOM_VK_F18: 129,
+    DOM_VK_F19: 130,
+    DOM_VK_F20: 131,
+    DOM_VK_F21: 132,
+    DOM_VK_F22: 133,
+    DOM_VK_F23: 134,
+    DOM_VK_F24: 135,
+    DOM_VK_NUM_LOCK: 144,
+    DOM_VK_SCROLL_LOCK: 145,
+    DOM_VK_COMMA: 188,
+    DOM_VK_PERIOD: 190,
+    DOM_VK_SLASH: 191,
+    DOM_VK_BACK_QUOTE: 192,
+    DOM_VK_OPEN_BRACKET: 219,
+    DOM_VK_BACK_SLASH: 220,
+    DOM_VK_CLOSE_BRACKET: 221,
+    DOM_VK_QUOTE: 222,
+    DOM_VK_META: 224
+  };
+}
+
+
+});
+/*
+ * 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('util/l10n', ['require', 'exports', 'module' ], function(require, exports, module) {
+
+'use strict';
 
 Components.utils.import('resource://gre/modules/XPCOMUtils.jsm');
 Components.utils.import('resource://gre/modules/Services.jsm');
 
 var imports = {};
 XPCOMUtils.defineLazyGetter(imports, 'stringBundle', function () {
   return Services.strings.createBundle('chrome://browser/locale/devtools/gcli.properties');
 });
@@ -584,17 +1499,17 @@ exports.lookupSwap = function(key, swaps
 exports.lookupPlural = function(key, ord, swaps) {
   throw new Error('lookupPlural is not available in mozilla');
 };
 
 exports.getPreferredLocales = function() {
   return [ 'root' ];
 };
 
-/** @see lookup() in lib/gcli/l10n.js */
+/** @see lookup() in lib/util/l10n.js */
 exports.lookup = function(key) {
   try {
     // Our memory leak hunter walks reachable objects trying to work out what
     // type of thing they are using object.constructor.name. If that causes
     // problems then we can avoid the unknown-key-exception with the following:
     /*
     if (key === 'constructor') {
       return { name: 'l10n-mem-leak-defeat' };
@@ -604,24 +1519,24 @@ exports.lookup = function(key) {
     return imports.stringBundle.GetStringFromName(key);
   }
   catch (ex) {
     console.error('Failed to lookup ', key, ex);
     return key;
   }
 };
 
-/** @see propertyLookup in lib/gcli/l10n.js */
+/** @see propertyLookup in lib/util/l10n.js */
 exports.propertyLookup = Proxy.create({
   get: function(rcvr, name) {
     return exports.lookup(name);
   }
 });
 
-/** @see lookupFormat in lib/gcli/l10n.js */
+/** @see lookupFormat in lib/util/l10n.js */
 exports.lookupFormat = function(key, swaps) {
   try {
     return imports.stringBundle.formatStringFromName(key, swaps, swaps.length);
   }
   catch (ex) {
     console.error('Failed to format ', key, ex);
     return key;
   }
@@ -640,19 +1555,21 @@ exports.lookupFormat = function(key, swa
  *
  * 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', ['require', 'exports', 'module' , 'gcli/argument'], function(require, exports, module) {
-
-
+define('gcli/types', ['require', 'exports', 'module' , 'util/promise', 'gcli/argument'], function(require, exports, module) {
+
+'use strict';
+
+var Promise = require('util/promise');
 var Argument = require('gcli/argument').Argument;
 var BlankArgument = require('gcli/argument').BlankArgument;
 
 
 /**
  * Some types can detect validity, that is to say they can distinguish between
  * valid and invalid values.
  * We might want to change these constants to be numbers for better performance
@@ -749,16 +1666,28 @@ function Conversion(value, arg, status, 
   this.value = value;
 
   // Allow us to trace where this Conversion came from
   this.arg = arg;
   if (arg == null) {
     throw new Error('Missing arg');
   }
 
+  if (predictions != null) {
+    var toCheck = typeof predictions === 'function' ? predictions() : predictions;
+    if (typeof toCheck.then !== 'function') {
+      throw new Error('predictions is not a promise');
+    }
+    toCheck.then(function(value) {
+      if (!Array.isArray(value)) {
+        throw new Error('prediction resolves to non array');
+      }
+    }, console.error);
+  }
+
   this._status = status || Status.VALID;
   this.message = message;
   this.predictions = predictions;
 }
 
 /**
  * Ensure that all arguments that are part of this conversion know what they
  * are assigned to.
@@ -821,53 +1750,54 @@ Conversion.prototype.getStatus = functio
  */
 Conversion.prototype.toString = function() {
   return this.arg.toString();
 };
 
 /**
  * If status === INCOMPLETE, then we may be able to provide predictions as to
  * how the argument can be completed.
- * @return An array of items, where each item is an object with the following
- * properties:
+ * @return An array of items, or a promise of an array of items, where each
+ * item is an object with the following properties:
  * - name (mandatory): Displayed to the user, and typed in. No whitespace
  * - description (optional): Short string for display in a tool-tip
  * - manual (optional): Longer description which details usage
  * - incomplete (optional): Indicates that the prediction if used should not
  *   be considered necessarily sufficient, which typically will mean that the
  *   UI should not append a space to the completion
  * - value (optional): If a value property is present, this will be used as the
  *   value of the conversion, otherwise the item itself will be used.
  */
 Conversion.prototype.getPredictions = function() {
   if (typeof this.predictions === 'function') {
     return this.predictions();
   }
-  return this.predictions || [];
-};
-
-/**
- * Return an index constrained by the available predictions. Basically
- * (index % predicitons.length)
+  return Promise.resolve(this.predictions || []);
+};
+
+/**
+ * Return a promise of an index constrained by the available predictions.
+ * i.e. (index % predicitons.length)
  */
 Conversion.prototype.constrainPredictionIndex = function(index) {
   if (index == null) {
-    return undefined;
-  }
-
-  var predictions = this.getPredictions();
-  if (predictions.length === 0) {
-    return undefined;
-  }
-
-  index = index % predictions.length;
-  if (index < 0) {
-    index = predictions.length + index;
-  }
-  return index;
+    return Promise.resolve();
+  }
+
+  return this.getPredictions().then(function(value) {
+    if (value.length === 0) {
+      return undefined;
+    }
+
+    index = index % value.length;
+    if (index < 0) {
+      index = value.length + index;
+    }
+    return index;
+  }.bind(this));
 };
 
 /**
  * Constant to allow everyone to agree on the maximum number of predictions
  * that should be provided. We actually display 1 less than this number.
  */
 Conversion.maxPredictions = 11;
 
@@ -948,17 +1878,17 @@ ArrayConversion.prototype.toString = fun
   }, this).join(', ') + ' ]';
 };
 
 exports.ArrayConversion = ArrayConversion;
 
 
 /**
  * Most of our types are 'static' e.g. there is only one type of 'string',
- * however some types like 'selection' and 'deferred' are customizable.
+ * however some types like 'selection' and 'delegate' are customizable.
  * The basic Type type isn't useful, but does provide documentation about what
  * types do.
  */
 function Type() {
 }
 
 /**
  * Convert the given <tt>value</tt> to a string representation.
@@ -982,22 +1912,22 @@ Type.prototype.parse = function(arg) {
 
 /**
  * A convenience method for times when you don't have an argument to parse
  * but instead have a string.
  * @see #parse(arg)
  */
 Type.prototype.parseString = function(str) {
   return this.parse(new Argument(str));
-},
+};
 
 /**
  * The plug-in system, and other things need to know what this type is
  * called. The name alone is not enough to fully specify a type. Types like
- * 'selection' and 'deferred' need extra data, however this function returns
+ * 'selection' and 'delegate' need extra data, however this function returns
  * only the name, not the extra data.
  */
 Type.prototype.name = undefined;
 
 /**
  * If there is some concept of a higher value, return it,
  * otherwise return undefined.
  */
@@ -1015,23 +1945,23 @@ Type.prototype.decrement = function(valu
 
 /**
  * The 'blank value' of most types is 'undefined', but there are exceptions;
  * This allows types to specify a better conversion from empty string than
  * 'undefined'.
  * 2 known examples of this are boolean -> false and array -> []
  */
 Type.prototype.getBlank = function() {
-  return this.parse(new BlankArgument());
-};
-
-/**
- * This is something of a hack for the benefit of DeferredType which needs to
+  return new Conversion(undefined, new BlankArgument(), Status.INCOMPLETE, '');
+};
+
+/**
+ * This is something of a hack for the benefit of DelegateType which needs to
  * be able to lie about it's type for fields to accept it as one of their own.
- * Sub-types can ignore this unless they're DeferredType.
+ * Sub-types can ignore this unless they're DelegateType.
  */
 Type.prototype.getType = function() {
   return this;
 };
 
 exports.Type = Type;
 
 /**
@@ -1134,16 +2064,17 @@ exports.getType = function(typeSpec) {
  * 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/argument', ['require', 'exports', 'module' ], function(require, exports, module) {
 
+'use strict';
 
 /**
  * Thinking out loud here:
  * Arguments are an area where we could probably refactor things a bit better.
  * The split process in Requisition creates a set of Arguments, which are then
  * assigned. The assign process sometimes converts them into subtypes of
  * Argument. We might consider that what gets assigned is _always_ one of the
  * subtypes (or actually a different type hierarchy entirely) and that we
@@ -1729,25 +2660,29 @@ exports.ArrayArgument = ArrayArgument;
  *
  * 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/selection', ['require', 'exports', 'module' , 'gcli/l10n', 'gcli/types', 'gcli/types/spell'], function(require, exports, module) {
-
-
-var l10n = require('gcli/l10n');
+define('gcli/types/selection', ['require', 'exports', 'module' , 'util/promise', 'util/util', 'util/l10n', 'gcli/types', 'gcli/types/spell', 'gcli/argument'], function(require, exports, module) {
+
+'use strict';
+
+var Promise = require('util/promise');
+var util = require('util/util');
+var l10n = require('util/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 spell = require('gcli/types/spell');
+var BlankArgument = require('gcli/argument').BlankArgument;
 
 
 /**
  * Registration and de-registration.
  */
 exports.startup = function() {
   types.registerType(SelectionType);
 };
@@ -1771,16 +2706,19 @@ exports.shutdown = function() {
  * - stringifyProperty: Conversion from value to string is generally a process
  *   of looking through all the valid options for a matching value, and using
  *   the associated name. However the name maybe available directly from the
  *   value using a property lookup. Setting 'stringifyProperty' allows
  *   SelectionType to take this shortcut.
  * - cacheable: If lookup is a function, then we normally assume that
  *   the values fetched can change. Setting 'cacheable:true' enables internal
  *   caching.
+ * - neverForceAsync: It's useful for testing purposes to be able to force all
+ *   selection types to be asynchronous. This flag prevents that happening for
+ *   types that are fundamentally synchronous.
  */
 function SelectionType(typeSpec) {
   if (typeSpec) {
     Object.keys(typeSpec).forEach(function(key) {
       this[key] = typeSpec[key];
     }, this);
   }
 }
@@ -1789,222 +2727,279 @@ SelectionType.prototype = Object.create(
 
 SelectionType.prototype.stringify = function(value) {
   if (value == null) {
     return '';
   }
   if (this.stringifyProperty != null) {
     return value[this.stringifyProperty];
   }
-  var name = null;
-  var lookup = this.getLookup();
-  lookup.some(function(item) {
-    if (item.value === value) {
-      name = item.name;
-      return true;
-    }
-    return false;
-  }, this);
-  return name;
+
+  try {
+    var name = null;
+    var lookup = util.synchronize(this.getLookup());
+    lookup.some(function(item) {
+      if (item.value === value) {
+        name = item.name;
+        return true;
+      }
+      return false;
+    }, this);
+    return name;
+  }
+  catch (ex) {
+    // Types really need to ensure stringify can happen synchronously
+    // which means using stringifyProperty if getLookup is asynchronous, but
+    // if this fails we need a bailout ...
+    return value.toString();
+  }
 };
 
 /**
  * If typeSpec contained cacheable:true then calls to parse() work on cached
  * data. clearCache() enables the cache to be cleared.
  */
 SelectionType.prototype.clearCache = function() {
   delete this._cachedLookup;
 };
 
 /**
  * There are several ways to get selection data. This unifies them into one
  * single function.
  * @return An array of objects with name and value properties.
  */
 SelectionType.prototype.getLookup = function() {
-  if (this._cachedLookup) {
+  if (this._cachedLookup != null) {
     return this._cachedLookup;
   }
 
-  if (this.lookup) {
-    if (typeof this.lookup === 'function') {
-      if (this.cacheable) {
-        this._cachedLookup = this.lookup();
-        return this._cachedLookup;
-      }
-      return this.lookup();
-    }
-    return this.lookup;
-  }
-
-  if (Array.isArray(this.data)) {
-    this.lookup = this._dataToLookup(this.data);
-    return this.lookup;
-  }
-
-  if (typeof(this.data) === 'function') {
-    return this._dataToLookup(this.data());
-  }
-
-  throw new Error('SelectionType has no data');
-};
+  var reply;
+  if (this.lookup == null) {
+    reply = resolve(this.data, this.neverForceAsync).then(dataToLookup);
+  }
+  else {
+    var lookup = (typeof this.lookup === 'function') ?
+            this.lookup.bind(this) :
+            this.lookup;
+
+    reply = resolve(lookup, this.neverForceAsync);
+  }
+
+  if (this.cacheable && !forceAsync) {
+    this._cachedLookup = reply;
+  }
+
+  return reply;
+};
+
+var forceAsync = false;
+
+/**
+ * Both 'lookup' and 'data' properties (see docs on SelectionType constructor)
+ * in addition to being real data can be a function or a promise, or even a
+ * function which returns a promise of real data, etc. This takes a thing and
+ * returns a promise of actual values.
+ */
+function resolve(thing, neverForceAsync) {
+  if (forceAsync && !neverForceAsync) {
+    var deferred = Promise.defer();
+    setTimeout(function() {
+      Promise.resolve(thing).then(function(resolved) {
+        if (typeof resolved === 'function') {
+          resolved = resolve(resolved(), neverForceAsync);
+        }
+
+        deferred.resolve(resolved);
+      });
+    }, 500);
+    return deferred.promise;
+  }
+
+  return Promise.resolve(thing).then(function(resolved) {
+    if (typeof resolved === 'function') {
+      return resolve(resolved(), neverForceAsync);
+    }
+    return resolved;
+  });
+}
 
 /**
  * Selection can be provided with either a lookup object (in the 'lookup'
  * property) or an array of strings (in the 'data' property). Internally we
  * always use lookup, so we need a way to convert a 'data' array to a lookup.
  */
-SelectionType.prototype._dataToLookup = function(data) {
+function dataToLookup(data) {
+  if (!Array.isArray(data)) {
+    throw new Error('SelectionType has no lookup or data');
+  }
+
   return data.map(function(option) {
     return { name: option, value: option };
   }, this);
 };
 
 /**
  * Return a list of possible completions for the given arg.
  * @param arg The initial input to match
  * @return A trimmed array of string:value pairs
  */
 SelectionType.prototype._findPredictions = function(arg) {
-  var predictions = [];
-  var lookup = this.getLookup();
-  var i, option;
-  var maxPredictions = Conversion.maxPredictions;
-  var match = arg.text.toLowerCase();
-
-  // If the arg has a suffix then we're kind of 'done'. Only an exact match
-  // will do.
-  if (arg.suffix.length > 0) {
+  return Promise.resolve(this.getLookup()).then(function(lookup) {
+    var predictions = [];
+    var i, option;
+    var maxPredictions = Conversion.maxPredictions;
+    var match = arg.text.toLowerCase();
+
+    // If the arg has a suffix then we're kind of 'done'. Only an exact match
+    // will do.
+    if (arg.suffix.length > 0) {
+      for (i = 0; i < lookup.length && predictions.length < maxPredictions; i++) {
+        option = lookup[i];
+        if (option.name === arg.text) {
+          this._addToPredictions(predictions, option, arg);
+        }
+      }
+
+      return predictions;
+    }
+
+    // Cache lower case versions of all the option names
+    for (i = 0; i < lookup.length; i++) {
+      option = lookup[i];
+      if (option._gcliLowerName == null) {
+        option._gcliLowerName = option.name.toLowerCase();
+      }
+    }
+
+    // Exact hidden matches. If 'hidden: true' then we only allow exact matches
+    // All the tests after here check that !option.value.hidden
     for (i = 0; i < lookup.length && predictions.length < maxPredictions; i++) {
       option = lookup[i];
       if (option.name === arg.text) {
         this._addToPredictions(predictions, option, arg);
       }
     }
 
+    // Start with prefix matching
+    for (i = 0; i < lookup.length && predictions.length < maxPredictions; i++) {
+      option = lookup[i];
+      if (option._gcliLowerName.indexOf(match) === 0 && !option.value.hidden) {
+        if (predictions.indexOf(option) === -1) {
+          this._addToPredictions(predictions, option, arg);
+        }
+      }
+    }
+
+    // Try infix matching if we get less half max matched
+    if (predictions.length < (maxPredictions / 2)) {
+      for (i = 0; i < lookup.length && predictions.length < maxPredictions; i++) {
+        option = lookup[i];
+        if (option._gcliLowerName.indexOf(match) !== -1 && !option.value.hidden) {
+          if (predictions.indexOf(option) === -1) {
+            this._addToPredictions(predictions, option, arg);
+          }
+        }
+      }
+    }
+
+    // Try fuzzy matching if we don't get a prefix match
+    if (predictions.length === 0) {
+      var names = [];
+      lookup.forEach(function(opt) {
+        if (!opt.value.hidden) {
+          names.push(opt.name);
+        }
+      });
+      var corrected = spell.correct(match, names);
+      if (corrected) {
+        lookup.forEach(function(opt) {
+          if (opt.name === corrected) {
+            predictions.push(opt);
+          }
+        }, this);
+      }
+    }
+
     return predictions;
-  }
-
-  // Cache lower case versions of all the option names
-  for (i = 0; i < lookup.length; i++) {
-    option = lookup[i];
-    if (option._gcliLowerName == null) {
-      option._gcliLowerName = option.name.toLowerCase();
-    }
-  }
-
-  // Exact hidden matches. If 'hidden: true' then we only allow exact matches
-  // All the tests after here check that !option.value.hidden
-  for (i = 0; i < lookup.length && predictions.length < maxPredictions; i++) {
-    option = lookup[i];
-    if (option.name === arg.text) {
-      this._addToPredictions(predictions, option, arg);
-    }
-  }
-
-  // Start with prefix matching
-  for (i = 0; i < lookup.length && predictions.length < maxPredictions; i++) {
-    option = lookup[i];
-    if (option._gcliLowerName.indexOf(match) === 0 && !option.value.hidden) {
-      if (predictions.indexOf(option) === -1) {
-        this._addToPredictions(predictions, option, arg);
-      }
-    }
-  }
-
-  // Try infix matching if we get less half max matched
-  if (predictions.length < (maxPredictions / 2)) {
-    for (i = 0; i < lookup.length && predictions.length < maxPredictions; i++) {
-      option = lookup[i];
-      if (option._gcliLowerName.indexOf(match) !== -1 && !option.value.hidden) {
-        if (predictions.indexOf(option) === -1) {
-          this._addToPredictions(predictions, option, arg);
-        }
-      }
-    }
-  }
-
-  // Try fuzzy matching if we don't get a prefix match
-  if (predictions.length === 0) {
-    var names = [];
-    lookup.forEach(function(opt) {
-      if (!opt.value.hidden) {
-        names.push(opt.name);
-      }
-    });
-    var corrected = spell.correct(match, names);
-    if (corrected) {
-      lookup.forEach(function(opt) {
-        if (opt.name === corrected) {
-          predictions.push(opt);
-        }
-      }, this);
-    }
-  }
-
-  return predictions;
+  }.bind(this));
 };
 
 /**
  * Add an option to our list of predicted options.
  * We abstract out this portion of _findPredictions() because CommandType needs
  * to make an extra check before actually adding which SelectionType does not
  * need to make.
  */
 SelectionType.prototype._addToPredictions = function(predictions, option, arg) {
   predictions.push(option);
 };
 
 SelectionType.prototype.parse = function(arg) {
-  var predictions = this._findPredictions(arg);
-
-  if (predictions.length === 0) {
-    var msg = l10n.lookupFormat('typesSelectionNomatch', [ arg.text ]);
-    return new Conversion(undefined, arg, Status.ERROR, msg, predictions);
-  }
-
-  // This is something of a hack it basically allows us to tell the
-  // setting type to forget its last setting hack.
-  if (this.noMatch) {
-    this.noMatch();
-  }
-
-  if (predictions[0].name === arg.text) {
-    var value = predictions[0].value;
-    return new Conversion(value, arg, Status.VALID, '', predictions);
-  }
-
-  return new Conversion(undefined, arg, Status.INCOMPLETE, '', predictions);
+  return this._findPredictions(arg).then(function(predictions) {
+    if (predictions.length === 0) {
+      var msg = l10n.lookupFormat('typesSelectionNomatch', [ arg.text ]);
+      return new Conversion(undefined, arg, Status.ERROR, msg,
+                            Promise.resolve(predictions));
+    }
+
+    // This is something of a hack it basically allows us to tell the
+    // setting type to forget its last setting hack.
+    if (this.noMatch) {
+      this.noMatch();
+    }
+
+    if (predictions[0].name === arg.text) {
+      var value = predictions[0].value;
+      return new Conversion(value, arg, Status.VALID, '',
+                            Promise.resolve(predictions));
+    }
+
+    return new Conversion(undefined, arg, Status.INCOMPLETE, '',
+                          Promise.resolve(predictions));
+  }.bind(this));
+};
+
+SelectionType.prototype.getBlank = function() {
+  var predictFunc = function() {
+    return Promise.resolve(this.getLookup()).then(function(lookup) {
+      return lookup.filter(function(option) {
+        return !option.value.hidden;
+      }).slice(0, Conversion.maxPredictions - 1);
+    }, console.error);
+  }.bind(this);
+
+  return new Conversion(undefined, new BlankArgument(), Status.INCOMPLETE, '',
+                        predictFunc);
 };
 
 /**
  * For selections, up is down and black is white. It's like this, given a list
  * [ a, b, c, d ], it's natural to think that it starts at the top and that
  * going up the list, moves towards 'a'. However 'a' has the lowest index, so
  * for SelectionType, up is down and down is up.
  * Sorry.
  */
 SelectionType.prototype.decrement = function(value) {
-  var lookup = this.getLookup();
+  var lookup = util.synchronize(this.getLookup());
   var index = this._findValue(lookup, value);
   if (index === -1) {
     index = 0;
   }
   index++;
   if (index >= lookup.length) {
     index = 0;
   }
   return lookup[index].value;
 };
 
 /**
  * See note on SelectionType.decrement()
  */
 SelectionType.prototype.increment = function(value) {
-  var lookup = this.getLookup();
+  var lookup = util.synchronize(this.getLookup());
   var index = this._findValue(lookup, value);
   if (index === -1) {
     // For an increment operation when there is nothing to start from, we
     // want to start from the top, i.e. index 0, so the value before we
     // 'increment' (see note above) must be 1.
     index = 1;
   }
   index--;
@@ -2053,59 +3048,60 @@ exports.SelectionType = SelectionType;
  * 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) {
 
+'use strict';
+
 /*
  * 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;
+  var wordiLen = wordi.length;
+  var wordjLen = 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 row0 = new Array(wordiLen+1);
+  var row1 = new Array(wordiLen+1);
+  var row2 = new Array(wordiLen+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++) {
+  for (i = 0; i <= wordiLen; 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++)
+  for (j = 1; j <= wordjLen; 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++)
-    {
+    for (i = 1; i <= wordiLen; 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
@@ -2116,43 +3112,49 @@ function damerauLevenshteinDistance(word
     }
 
     tmp = row2;
     row2 = row1;
     row1 = row0;
     row0 = tmp;
   }
 
-  return row1[N];
-};
+  return row1[wordiLen];
+}
 
 /**
  * A function that returns the correction for the specified word.
  */
 exports.correct = function(word, names) {
+  if (names.length === 0) {
+    return undefined;
+  }
+
   var distance = {};
-  var sorted_candidates;
+  var sortedCandidates;
 
   names.forEach(function(candidate) {
     distance[candidate] = damerauLevenshteinDistance(word, candidate);
   });
 
-  sorted_candidates = names.sort(function(worda, wordb) {
+  sortedCandidates = names.sort(function(worda, wordb) {
     if (distance[worda] !== distance[wordb]) {
       return distance[worda] - distance[wordb];
-    } else {
+    }
+    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 {
+  if (distance[sortedCandidates[0]] <= MAX_EDIT_DISTANCE) {
+    return sortedCandidates[0];
+  }
+  else {
     return undefined;
   }
 };
 
 
 });
 /*
  * Copyright 2012, Mozilla Foundation and contributors
@@ -2165,21 +3167,23 @@ exports.correct = function(word, names) 
  *
  * 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/command', ['require', 'exports', 'module' , 'gcli/canon', 'gcli/l10n', 'gcli/types', 'gcli/types/selection'], function(require, exports, module) {
-
-
+define('gcli/types/command', ['require', 'exports', 'module' , 'util/promise', 'util/l10n', 'gcli/canon', 'gcli/types', 'gcli/types/selection'], function(require, exports, module) {
+
+'use strict';
+
+var Promise = require('util/promise');
+var l10n = require('util/l10n');
 var canon = require('gcli/canon');
-var l10n = require('gcli/l10n');
 var types = require('gcli/types');
 var SelectionType = require('gcli/types/selection').SelectionType;
 var Status = require('gcli/types').Status;
 var Conversion = require('gcli/types').Conversion;
 
 
 /**
  * Registration and de-registration.
@@ -2201,50 +3205,58 @@ exports.shutdown = function() {
  * SelectionType to make it handle Commands correctly was to high, so we
  * simplified.
  * If you are making changes to this code, you should check there too.
  */
 function ParamType(typeSpec) {
   this.requisition = typeSpec.requisition;
   this.isIncompleteName = typeSpec.isIncompleteName;
   this.stringifyProperty = 'name';
+  this.neverForceAsync = true;
 }
 
 ParamType.prototype = Object.create(SelectionType.prototype);
 
 ParamType.prototype.name = 'param';
 
 ParamType.prototype.lookup = function() {
   var displayedParams = [];
   var command = this.requisition.commandAssignment.value;
-  command.params.forEach(function(param) {
-    var arg = this.requisition.getAssignment(param.name).arg;
-    if (!param.isPositionalAllowed && arg.type === "BlankArgument") {
-      displayedParams.push({ name: '--' + param.name, value: param });
-    }
-  }, this);
+  if (command != null) {
+    command.params.forEach(function(param) {
+      var arg = this.requisition.getAssignment(param.name).arg;
+      if (!param.isPositionalAllowed && arg.type === "BlankArgument") {
+        displayedParams.push({ name: '--' + param.name, value: param });
+      }
+    }, this);
+  }
   return displayedParams;
 };
 
 ParamType.prototype.parse = function(arg) {
-  return this.isIncompleteName ?
-      SelectionType.prototype.parse.call(this, arg) :
-      new Conversion(undefined, arg, Status.ERROR, l10n.lookup('cliUnusedArg'));
+  if (this.isIncompleteName) {
+    return SelectionType.prototype.parse.call(this, arg);
+  }
+  else {
+    var message = l10n.lookup('cliUnusedArg');
+    return Promise.resolve(new Conversion(undefined, arg, Status.ERROR, message));
+  }
 };
 
 
 /**
  * Select from the available commands.
  * This is very similar to a SelectionType, however the level of hackery in
  * SelectionType to make it handle Commands correctly was to high, so we
  * simplified.
  * If you are making changes to this code, you should check there too.
  */
 function CommandType() {
   this.stringifyProperty = 'name';
+  this.neverForceAsync = true;
 }
 
 CommandType.prototype = Object.create(SelectionType.prototype);
 
 CommandType.prototype.name = 'command';
 
 CommandType.prototype.lookup = function() {
   var commands = canon.getCommands();
@@ -2271,40 +3283,41 @@ CommandType.prototype._addToPredictions 
 
 CommandType.prototype.parse = function(arg) {
   // Especially at startup, predictions live over the time that things change
   // so we provide a completion function rather than completion values
   var predictFunc = function() {
     return this._findPredictions(arg);
   }.bind(this);
 
-  var predictions = this._findPredictions(arg);
-
-  if (predictions.length === 0) {
-    var msg = l10n.lookupFormat('typesSelectionNomatch', [ arg.text ]);
-    return new Conversion(undefined, arg, Status.ERROR, msg, predictFunc);
-  }
-
-  var command = predictions[0].value;
-
-  if (predictions.length === 1) {
-    // Is it an exact match of an executable command,
-    // or just the only possibility?
-    if (command.name === arg.text && typeof command.exec === 'function') {
-      return new Conversion(command, arg, Status.VALID, '');
-    }
+  return this._findPredictions(arg).then(function(predictions) {
+    if (predictions.length === 0) {
+      var msg = l10n.lookupFormat('typesSelectionNomatch', [ arg.text ]);
+      return new Conversion(undefined, arg, Status.ERROR, msg, predictFunc);
+    }
+
+    var command = predictions[0].value;
+
+    if (predictions.length === 1) {
+      // Is it an exact match of an executable command,
+      // or just the only possibility?
+      if (command.name === arg.text && typeof command.exec === 'function') {
+        return new Conversion(command, arg, Status.VALID, '');
+      }
+
+      return new Conversion(undefined, arg, Status.INCOMPLETE, '', predictFunc);
+    }
+
+    // It's valid if the text matches, even if there are several options
+    if (predictions[0].name === arg.text) {
+      return new Conversion(command, arg, Status.VALID, '', predictFunc);
+    }
+
     return new Conversion(undefined, arg, Status.INCOMPLETE, '', predictFunc);
-  }
-
-  // It's valid if the text matches, even if there are several options
-  if (predictions[0].name === arg.text) {
-    return new Conversion(command, arg, Status.VALID, '', predictFunc);
-  }
-
-  return new Conversion(undefined, arg, Status.INCOMPLETE, '', predictFunc);
+  }.bind(this));
 };
 
 
 });
 /*
  * Copyright 2012, Mozilla Foundation and contributors
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
@@ -2315,22 +3328,24 @@ CommandType.prototype.parse = function(a
  *
  * 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/canon', ['require', 'exports', 'module' , 'gcli/util', 'gcli/l10n', 'gcli/types', 'gcli/types/basic', 'gcli/types/selection'], function(require, exports, module) {
+define('gcli/canon', ['require', 'exports', 'module' , 'util/promise', 'util/util', 'util/l10n', 'gcli/types', 'gcli/types/basic', 'gcli/types/selection'], function(require, exports, module) {
+
+'use strict';
 var canon = exports;
 
-
-var util = require('gcli/util');
-var l10n = require('gcli/l10n');
+var Promise = require('util/promise');
+var util = require('util/util');
+var l10n = require('util/l10n');
 
 var types = require('gcli/types');
 var Status = require('gcli/types').Status;
 var BooleanType = require('gcli/types/basic').BooleanType;
 var SelectionType = require('gcli/types/selection').SelectionType;
 
 /**
  * Implement the localization algorithm for any documentation objects (i.e.
@@ -2491,26 +3506,26 @@ function Parameter(paramSpec, command, g
 
   // Check the defaultValue for validity.
   // Both undefined and null get a pass on this test. undefined is used when
   // there is no defaultValue, and null is used when the parameter is
   // optional, neither are required to parse and stringify.
   if (this._defaultValue != null) {
     try {
       var defaultText = this.type.stringify(this.paramSpec.defaultValue);
-      var defaultConversion = this.type.parseString(defaultText);
-      if (defaultConversion.getStatus() !== Status.VALID) {
-        throw new Error('In ' + this.command.name + '/' + this.name +
+      this.type.parseString(defaultText).then(function(defaultConversion) {
+        if (defaultConversion.getStatus() !== Status.VALID) {
+          console.error('In ' + this.command.name + '/' + this.name +
                         ': Error round tripping defaultValue. status = ' +
                         defaultConversion.getStatus());
-      }
+        }
+      }.bind(this), console.error);
     }
     catch (ex) {
-      throw new Error('In ' + this.command.name + '/' + this.name +
-                      ': ' + ex);
+      throw new Error('In ' + this.command.name + '/' + this.name + ': ' + ex);
     }
   }
 
   // All parameters that can only be set via a named parameter must have a
   // non-undefined default value
   if (!this.isPositionalAllowed && this.paramSpec.defaultValue === undefined &&
       this.type.getBlank == null && !(this.type instanceof BooleanType)) {
     throw new Error('In ' + this.command.name + '/' + this.name +
@@ -2547,21 +3562,17 @@ Parameter.prototype.isKnownAs = function
 };
 
 /**
  * Read the default value for this parameter either from the parameter itself
  * (if this function has been over-ridden) or from the type, or from calling
  * parseString on an empty string
  */
 Parameter.prototype.getBlank = function() {
-  if (this.type.getBlank) {
-    return this.type.getBlank();
-  }
-
-  return this.type.parseString('');
+  return this.type.getBlank();
 };
 
 /**
  * Resolve the manual for this parameter, by looking in the paramSpec
  * and doing a l10n lookup
  */
 Object.defineProperty(Parameter.prototype, 'manual', {
   get: function() {
@@ -2751,754 +3762,22 @@ canon.CommandOutputManager = CommandOutp
  *
  * 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/util', ['require', 'exports', 'module' ], function(require, exports, module) {
-
-/*
- * A number of DOM manipulation and event handling utilities.
- */
-
-
-//------------------------------------------------------------------------------
-
-var eventDebug = false;
-
-/**
- * Patch up broken console API from node
- */
-if (eventDebug) {
-  if (console.group == null) {
-    console.group = function() { console.log(arguments); };
-  }
-  if (console.groupEnd == null) {
-    console.groupEnd = function() { console.log(arguments); };
-  }
-}
-
-/**
- * Useful way to create a name for a handler, used in createEvent()
- */
-function nameFunction(handler) {
-  var scope = handler.scope ? handler.scope.constructor.name + '.' : '';
-  var name = handler.func.name;
-  if (name) {
-    return scope + name;
-  }
-  for (var prop in handler.scope) {
-    if (handler.scope[prop] === handler.func) {
-      return scope + prop;
-    }
-  }
-  return scope + handler.func;
-}
-
-/**
- * Create an event.
- * For use as follows:
- *
- *   function Hat() {
- *     this.putOn = createEvent('Hat.putOn');
- *     ...
- *   }
- *   Hat.prototype.adorn = function(person) {
- *     this.putOn({ hat: hat, person: person });
- *     ...
- *   }
- *
- *   var hat = new Hat();
- *   hat.putOn.add(function(ev) {
- *     console.log('The hat ', ev.hat, ' has is worn by ', ev.person);
- *   }, scope);
- *
- * @param name Optional name to help with debugging
- */
-exports.createEvent = function(name) {
-  var handlers = [];
-  var holdFire = false;
-  var heldEvents = [];
-  var eventCombiner = undefined;
-
-  /**
-   * This is how the event is triggered.
-   * @param ev The event object to be passed to the event listeners
-   */
-  var event = function(ev) {
-    if (holdFire) {
-      heldEvents.push(ev);
-      if (eventDebug) {
-        console.log('Held fire: ' + name, ev);
-      }
-      return;
-    }
-
-    if (eventDebug) {
-      console.group('Fire: ' + name + ' to ' + handlers.length + ' listeners', ev);
-    }
-
-    // Use for rather than forEach because it step debugs better, which is
-    // important for debugging events
-    for (var i = 0; i < handlers.length; i++) {
-      var handler = handlers[i];
-      if (eventDebug) {
-        console.log(nameFunction(handler));
-      }
-      handler.func.call(handler.scope, ev);
-    }
-
-    if (eventDebug) {
-      console.groupEnd();
-    }
-  };
-
-  /**
-   * Add a new handler function
-   * @param func The function to call when this event is triggered
-   * @param scope Optional 'this' object for the function call
-   */
-  event.add = function(func, scope) {
-    if (eventDebug) {
-      console.log('Adding listener to ' + name);
-    }
-
-    handlers.push({ func: func, scope: scope });
-  };
-
-  /**
-   * Remove a handler function added through add(). Both func and scope must
-   * be strict equals (===) the values used in the call to add()
-   * @param func The function to call when this event is triggered
-   * @param scope Optional 'this' object for the function call
-   */
-  event.remove = function(func, scope) {
-    if (eventDebug) {
-      console.log('Removing listener from ' + name);
-    }
-
-    var found = false;
-    handlers = handlers.filter(function(test) {
-      var match = (test.func === func && test.scope === scope);
-      if (match) {
-        found = true;
-      }
-      return !match;
-    });
-    if (!found) {
-      console.warn('Handler not found. Attached to ' + name);
-    }
-  };
-
-  /**
-   * Remove all handlers.
-   * Reset the state of this event back to it's post create state
-   */
-  event.removeAll = function() {
-    handlers = [];
-  };
-
-  /**
-   * Temporarily prevent this event from firing.
-   * @see resumeFire(ev)
-   */
-  event.holdFire = function() {
-    if (eventDebug) {
-      console.group('Holding fire: ' + name);
-    }
-
-    holdFire = true;
-  };
-
-  /**
-   * Resume firing events.
-   * If there are heldEvents, then we fire one event to cover them all. If an
-   * event combining function has been provided then we use that to combine the
-   * events. Otherwise the last held event is used.
-   * @see holdFire()
-   */
-  event.resumeFire = function() {
-    if (eventDebug) {
-      console.groupEnd('Resume fire: ' + name);
-    }
-
-    if (holdFire !== true) {
-      throw new Error('Event not held: ' + name);
-    }
-
-    holdFire = false;
-    if (heldEvents.length === 0) {
-      return;
-    }
-
-    if (heldEvents.length === 1) {
-      event(heldEvents[0]);
-    }
-    else {
-      var first = heldEvents[0];
-      var last = heldEvents[heldEvents.length - 1];
-      if (eventCombiner) {
-        event(eventCombiner(first, last, heldEvents));
-      }
-      else {
-        event(last);
-      }
-    }
-
-    heldEvents = [];
-  };
-
-  /**
-   * When resumeFire has a number of events to combine, by default it just
-   * picks the last, however you can provide an eventCombiner which returns a
-   * combined event.
-   * eventCombiners will be passed 3 parameters:
-   * - first The first event to be held
-   * - last The last event to be held
-   * - all An array containing all the held events
-   * The return value from an eventCombiner is expected to be an event object
-   */
-  Object.defineProperty(event, 'eventCombiner', {
-    set: function(newEventCombiner) {
-      if (typeof newEventCombiner !== 'function') {
-        throw new Error('eventCombiner is not a function');
-      }
-      eventCombiner = newEventCombiner;
-    },
-
-    enumerable: true
-  });
-
-  return event;
-};
-
-
-//------------------------------------------------------------------------------
-
-/**
- * XHTML namespace
- */
-exports.NS_XHTML = 'http://www.w3.org/1999/xhtml';
-
-/**
- * XUL namespace
- */
-exports.NS_XUL = 'http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul';
-
-/**
- * Create an HTML or XHTML element depending on whether the document is HTML
- * or XML based. Where HTML/XHTML elements are distinguished by whether they
- * are created using doc.createElementNS('http://www.w3.org/1999/xhtml', tag)
- * or doc.createElement(tag)
- * If you want to create a XUL element then you don't have a problem knowing
- * what namespace you want.
- * @param doc The document in which to create the element
- * @param tag The name of the tag to create
- * @returns The created element
- */
-exports.createElement = function(doc, tag) {
-  if (exports.isXmlDocument(doc)) {
-    return doc.createElementNS(exports.NS_XHTML, tag);
-  }
-  else {
-    return doc.createElement(tag);
-  }
-};
-
-/**
- * Remove all the child nodes from this node
- * @param elem The element that should have it's children removed
- */
-exports.clearElement = function(elem) {
-  while (elem.hasChildNodes()) {
-    elem.removeChild(elem.firstChild);
-  }
-};
-
-var isAllWhitespace = /^\s*$/;
-
-/**
- * Iterate over the children of a node looking for TextNodes that have only
- * whitespace content and remove them.
- * This utility is helpful when you have a template which contains whitespace
- * so it looks nice, but where the whitespace interferes with the rendering of
- * the page
- * @param elem The element which should have blank whitespace trimmed
- * @param deep Should this node removal include child elements
- */
-exports.removeWhitespace = function(elem, deep) {
-  var i = 0;
-  while (i < elem.childNodes.length) {
-    var child = elem.childNodes.item(i);
-    if (child.nodeType === 3 /*Node.TEXT_NODE*/ &&
-        isAllWhitespace.test(child.textContent)) {
-      elem.removeChild(child);
-    }
-    else {
-      if (deep && child.nodeType === 1 /*Node.ELEMENT_NODE*/) {
-        exports.removeWhitespace(child, deep);
-      }
-      i++;
-    }
-  }
-};
-
-/**
- * Create a style element in the document head, and add the given CSS text to
- * it.
- * @param cssText The CSS declarations to append
- * @param doc The document element to work from
- * @param id Optional id to assign to the created style tag. If the id already
- * exists on the document, we do not add the CSS again.
- */
-exports.importCss = function(cssText, doc, id) {
-  if (!cssText) {
-    return undefined;
-  }
-
-  doc = doc || document;
-
-  if (!id) {
-    id = 'hash-' + hash(cssText);
-  }
-
-  var found = doc.getElementById(id);
-  if (found) {
-    if (found.tagName.toLowerCase() !== 'style') {
-      console.error('Warning: importCss passed id=' + id +
-              ', but that pre-exists (and isn\'t a style tag)');
-    }
-    return found;
-  }
-
-  var style = exports.createElement(doc, 'style');
-  style.id = id;
-  style.appendChild(doc.createTextNode(cssText));
-
-  var head = doc.getElementsByTagName('head')[0] || doc.documentElement;
-  head.appendChild(style);
-
-  return style;
-};
-
-/**
- * Simple hash function which happens to match Java's |String.hashCode()|
- * Done like this because I we don't need crypto-security, but do need speed,
- * and I don't want to spend a long time working on it.
- * @see http://werxltd.com/wp/2010/05/13/javascript-implementation-of-javas-string-hashcode-method/
- */
-function hash(str) {
-  var hash = 0;
-  if (str.length == 0) {
-    return hash;
-  }
-  for (var i = 0; i < str.length; i++) {
-    var character = str.charCodeAt(i);
-    hash = ((hash << 5) - hash) + character;
-    hash = hash & hash; // Convert to 32bit integer
-  }
-  return hash;
-}
-
-/**
- * Shortcut for clearElement/createTextNode/appendChild to make up for the lack
- * of standards around textContent/innerText
- */
-exports.setTextContent = function(elem, text) {
-  exports.clearElement(elem);
-  var child = elem.ownerDocument.createTextNode(text);
-  elem.appendChild(child);
-};
-
-/**
- * There are problems with innerHTML on XML documents, so we need to do a dance
- * using document.createRange().createContextualFragment() when in XML mode
- */
-exports.setContents = function(elem, contents) {
-  if (typeof HTMLElement !== 'undefined' && contents instanceof HTMLElement) {
-    exports.clearElement(elem);
-    elem.appendChild(contents);
-    return;
-  }
-
-  if ('innerHTML' in elem) {
-    elem.innerHTML = contents;
-  }
-  else {
-    try {
-      var ns = elem.ownerDocument.documentElement.namespaceURI;
-      if (!ns) {
-        ns = exports.NS_XHTML;
-      }
-      exports.clearElement(elem);
-      contents = '<div xmlns="' + ns + '">' + contents + '</div>';
-      var range = elem.ownerDocument.createRange();
-      var child = range.createContextualFragment(contents).firstChild;
-      while (child.hasChildNodes()) {
-        elem.appendChild(child.firstChild);
-      }
-    }
-    catch (ex) {
-      console.error('Bad XHTML', ex);
-      console.trace();
-      throw ex;
-    }
-  }
-};
-
-/**
- * Load some HTML into the given document and return a DOM element.
- * This utility assumes that the html has a single root (other than whitespace)
- */
-exports.toDom = function(document, html) {
-  var div = exports.createElement(document, 'div');
-  exports.setContents(div, html);
-  return div.children[0];
-};
-
-/**
- * How to detect if we're in an XML document.
- * In a Mozilla we check that document.xmlVersion = null, however in Chrome
- * we use document.contentType = undefined.
- * @param doc The document element to work from (defaulted to the global
- * 'document' if missing
- */
-exports.isXmlDocument = function(doc) {
-  doc = doc || document;
-  // Best test for Firefox
-  if (doc.contentType && doc.contentType != 'text/html') {
-    return true;
-  }
-  // Best test for Chrome
-  if (doc.xmlVersion != null) {
-    return true;
-  }
-  return false;
-};
-
-/**
- * Find the position of [element] in [nodeList].
- * @returns an index of the match, or -1 if there is no match
- */
-function positionInNodeList(element, nodeList) {
-  for (var i = 0; i < nodeList.length; i++) {
-    if (element === nodeList[i]) {
-      return i;
-    }
-  }
-  return -1;
-}
-
-/**
- * Find a unique CSS selector for a given element
- * @returns a string such that ele.ownerDocument.querySelector(reply) === ele
- * and ele.ownerDocument.querySelectorAll(reply).length === 1
- */
-exports.findCssSelector = function(ele) {
-  var document = ele.ownerDocument;
-  if (ele.id && document.getElementById(ele.id) === ele) {
-    return '#' + ele.id;
-  }
-
-  // Inherently unique by tag name
-  var tagName = ele.tagName.toLowerCase();
-  if (tagName === 'html') {
-    return 'html';
-  }
-  if (tagName === 'head') {
-    return 'head';
-  }
-  if (tagName === 'body') {
-    return 'body';
-  }
-
-  if (ele.parentNode == null) {
-    console.log('danger: ' + tagName);
-  }
-
-  // We might be able to find a unique class name
-  var selector, index, matches;
-  if (ele.classList.length > 0) {
-    for (var i = 0; i < ele.classList.length; i++) {
-      // Is this className unique by itself?
-      selector = '.' + ele.classList.item(i);
-      matches = document.querySelectorAll(selector);
-      if (matches.length === 1) {
-        return selector;
-      }
-      // Maybe it's unique with a tag name?
-      selector = tagName + selector;
-      matches = document.querySelectorAll(selector);
-      if (matches.length === 1) {
-        return selector;
-      }
-      // Maybe it's unique using a tag name and nth-child
-      index = positionInNodeList(ele, ele.parentNode.children) + 1;
-      selector = selector + ':nth-child(' + index + ')';
-      matches = document.querySelectorAll(selector);
-      if (matches.length === 1) {
-        return selector;
-      }
-    }
-  }
-
-  // So we can be unique w.r.t. our parent, and use recursion
-  index = positionInNodeList(ele, ele.parentNode.children) + 1;
-  selector = exports.findCssSelector(ele.parentNode) + ' > ' +
-          tagName + ':nth-child(' + index + ')';
-
-  return selector;
-};
-
-/**
- * Work out the path for images.
- */
-exports.createUrlLookup = function(callingModule) {
-  return function imageUrl(path) {
-    try {
-      return require('text!gcli/ui/' + path);
-    }
-    catch (ex) {
-      // Under node/unamd callingModule is provided by node. This code isn't
-      // the right answer but it's enough to pass all the unit tests and get
-      // test coverage information, which is all we actually care about here.
-      if (callingModule.filename) {
-        return callingModule.filename + path;
-      }
-
-      var filename = callingModule.id.split('/').pop() + '.js';
-
-      if (callingModule.uri.substr(-filename.length) !== filename) {
-        console.error('Can\'t work out path from module.uri/module.id');
-        return path;
-      }
-
-      if (callingModule.uri) {
-        var end = callingModule.uri.length - filename.length - 1;
-        return callingModule.uri.substr(0, end) + '/' + path;
-      }
-
-      return filename + '/' + path;
-    }
-  };
-};
-
-/**
- * Helper to find the 'data-command' attribute and call some action on it.
- * @see |updateCommand()| and |executeCommand()|
- */
-function withCommand(element, action) {
-  var command = element.getAttribute('data-command');
-  if (!command) {
-    command = element.querySelector('*[data-command]')
-            .getAttribute('data-command');
-  }
-
-  if (command) {
-    action(command);
-  }
-  else {
-    console.warn('Missing data-command for ' + util.findCssSelector(element));
-  }
-}
-
-/**
- * Update the requisition to contain the text of the clicked element
- * @param element The clicked element, containing either a data-command
- * attribute directly or in a nested element, from which we get the command
- * to be executed.
- * @param context Either a Requisition or an ExecutionContext or another object
- * that contains an |update()| function that follows a similar contract.
- */
-exports.updateCommand = function(element, context) {
-  withCommand(element, function(command) {
-    context.update(command);
-  });
-};
-
-/**
- * Execute the text contained in the element that was clicked
- * @param element The clicked element, containing either a data-command
- * attribute directly or in a nested element, from which we get the command
- * to be executed.
- * @param context Either a Requisition or an ExecutionContext or another object
- * that contains an |update()| function that follows a similar contract.
- */
-exports.executeCommand = function(element, context) {
-  withCommand(element, function(command) {
-    context.exec({
-      visible: true,
-      typed: command
-    });
-  });
-};
-
-
-//------------------------------------------------------------------------------
-
-/**
- * Keyboard handling is a mess. http://unixpapa.com/js/key.html
- * It would be good to use DOM L3 Keyboard events,
- * http://www.w3.org/TR/2010/WD-DOM-Level-3-Events-20100907/#events-keyboardevents
- * however only Webkit supports them, and there isn't a shim on Monernizr:
- * https://github.com/Modernizr/Modernizr/wiki/HTML5-Cross-browser-Polyfills
- * and when the code that uses this KeyEvent was written, nothing was clear,
- * so instead, we're using this unmodern shim:
- * http://stackoverflow.com/questions/5681146/chrome-10-keyevent-or-something-similar-to-firefoxs-keyevent
- * See BUG 664991: GCLI's keyboard handling should be updated to use DOM-L3
- * https://bugzilla.mozilla.org/show_bug.cgi?id=664991
- */
-if (typeof 'KeyEvent' === 'undefined') {
-  exports.KeyEvent = this.KeyEvent;
-}
-else {
-  exports.KeyEvent = {
-    DOM_VK_CANCEL: 3,
-    DOM_VK_HELP: 6,
-    DOM_VK_BACK_SPACE: 8,
-    DOM_VK_TAB: 9,
-    DOM_VK_CLEAR: 12,
-    DOM_VK_RETURN: 13,
-    DOM_VK_ENTER: 14,
-    DOM_VK_SHIFT: 16,
-    DOM_VK_CONTROL: 17,
-    DOM_VK_ALT: 18,
-    DOM_VK_PAUSE: 19,
-    DOM_VK_CAPS_LOCK: 20,
-    DOM_VK_ESCAPE: 27,
-    DOM_VK_SPACE: 32,
-    DOM_VK_PAGE_UP: 33,
-    DOM_VK_PAGE_DOWN: 34,
-    DOM_VK_END: 35,
-    DOM_VK_HOME: 36,
-    DOM_VK_LEFT: 37,
-    DOM_VK_UP: 38,
-    DOM_VK_RIGHT: 39,
-    DOM_VK_DOWN: 40,
-    DOM_VK_PRINTSCREEN: 44,
-    DOM_VK_INSERT: 45,
-    DOM_VK_DELETE: 46,
-    DOM_VK_0: 48,
-    DOM_VK_1: 49,
-    DOM_VK_2: 50,
-    DOM_VK_3: 51,
-    DOM_VK_4: 52,
-    DOM_VK_5: 53,
-    DOM_VK_6: 54,
-    DOM_VK_7: 55,
-    DOM_VK_8: 56,
-    DOM_VK_9: 57,
-    DOM_VK_SEMICOLON: 59,
-    DOM_VK_EQUALS: 61,
-    DOM_VK_A: 65,
-    DOM_VK_B: 66,
-    DOM_VK_C: 67,
-    DOM_VK_D: 68,
-    DOM_VK_E: 69,
-    DOM_VK_F: 70,
-    DOM_VK_G: 71,
-    DOM_VK_H: 72,
-    DOM_VK_I: 73,
-    DOM_VK_J: 74,
-    DOM_VK_K: 75,
-    DOM_VK_L: 76,
-    DOM_VK_M: 77,
-    DOM_VK_N: 78,
-    DOM_VK_O: 79,
-    DOM_VK_P: 80,
-    DOM_VK_Q: 81,
-    DOM_VK_R: 82,
-    DOM_VK_S: 83,
-    DOM_VK_T: 84,
-    DOM_VK_U: 85,
-    DOM_VK_V: 86,
-    DOM_VK_W: 87,
-    DOM_VK_X: 88,
-    DOM_VK_Y: 89,
-    DOM_VK_Z: 90,
-    DOM_VK_CONTEXT_MENU: 93,
-    DOM_VK_NUMPAD0: 96,
-    DOM_VK_NUMPAD1: 97,
-    DOM_VK_NUMPAD2: 98,
-    DOM_VK_NUMPAD3: 99,
-    DOM_VK_NUMPAD4: 100,
-    DOM_VK_NUMPAD5: 101,
-    DOM_VK_NUMPAD6: 102,
-    DOM_VK_NUMPAD7: 103,
-    DOM_VK_NUMPAD8: 104,
-    DOM_VK_NUMPAD9: 105,
-    DOM_VK_MULTIPLY: 106,
-    DOM_VK_ADD: 107,
-    DOM_VK_SEPARATOR: 108,
-    DOM_VK_SUBTRACT: 109,
-    DOM_VK_DECIMAL: 110,
-    DOM_VK_DIVIDE: 111,
-    DOM_VK_F1: 112,
-    DOM_VK_F2: 113,
-    DOM_VK_F3: 114,
-    DOM_VK_F4: 115,
-    DOM_VK_F5: 116,
-    DOM_VK_F6: 117,
-    DOM_VK_F7: 118,
-    DOM_VK_F8: 119,
-    DOM_VK_F9: 120,
-    DOM_VK_F10: 121,
-    DOM_VK_F11: 122,
-    DOM_VK_F12: 123,
-    DOM_VK_F13: 124,
-    DOM_VK_F14: 125,
-    DOM_VK_F15: 126,
-    DOM_VK_F16: 127,
-    DOM_VK_F17: 128,
-    DOM_VK_F18: 129,
-    DOM_VK_F19: 130,
-    DOM_VK_F20: 131,
-    DOM_VK_F21: 132,
-    DOM_VK_F22: 133,
-    DOM_VK_F23: 134,
-    DOM_VK_F24: 135,
-    DOM_VK_NUM_LOCK: 144,
-    DOM_VK_SCROLL_LOCK: 145,
-    DOM_VK_COMMA: 188,
-    DOM_VK_PERIOD: 190,
-    DOM_VK_SLASH: 191,
-    DOM_VK_BACK_QUOTE: 192,
-    DOM_VK_OPEN_BRACKET: 219,
-    DOM_VK_BACK_SLASH: 220,
-    DOM_VK_CLOSE_BRACKET: 221,
-    DOM_VK_QUOTE: 222,
-    DOM_VK_META: 224
-  };
-}
-
-
-});
-/*
- * 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/javascript', ['require', 'exports', 'module' , 'gcli/l10n', 'gcli/types'], function(require, exports, module) {
-
-
-var l10n = require('gcli/l10n');
+define('gcli/types/javascript', ['require', 'exports', 'module' , 'util/promise', 'util/l10n', 'gcli/types'], function(require, exports, module) {
+
+'use strict';
+
+var Promise = require('util/promise');
+var l10n = require('util/l10n');
 var types = require('gcli/types');
 
 var Conversion = types.Conversion;
 var Type = types.Type;
 var Status = types.Status;
 
 
 /**
@@ -3511,17 +3790,17 @@ exports.startup = function() {
 exports.shutdown = function() {
   types.unregisterType(JavascriptType);
 };
 
 /**
  * The object against which we complete, which is usually 'window' if it exists
  * but could be something else in non-web-content environments.
  */
-var globalObject;
+var globalObject = undefined;
 if (typeof window !== 'undefined') {
   globalObject = window;
 }
 
 /**
  * Setter for the object against which JavaScript completions happen
  */
 exports.setGlobalObject = function(obj) {
@@ -3566,110 +3845,110 @@ JavascriptType.prototype.stringify = fun
 JavascriptType.MAX_COMPLETION_MATCHES = 10;
 
 JavascriptType.prototype.parse = function(arg) {
   var typed = arg.text;
   var scope = globalObject;
 
   // No input is undefined
   if (typed === '') {
-    return new Conversion(undefined, arg, Status.INCOMPLETE);
+    return Promise.resolve(new Conversion(undefined, arg, Status.INCOMPLETE));
   }
   // Just accept numbers
   if (!isNaN(parseFloat(typed)) && isFinite(typed)) {
-    return new Conversion(typed, arg);
+    return Promise.resolve(new Conversion(typed, arg));
   }
   // Just accept constants like true/false/null/etc
   if (typed.trim().match(/(null|undefined|NaN|Infinity|true|false)/)) {
-    return new Conversion(typed, arg);
+    return Promise.resolve(new Conversion(typed, arg));
   }
 
   // Analyze the input text and find the beginning of the last part that
   // should be completed.
   var beginning = this._findCompletionBeginning(typed);
 
   // There was an error analyzing the string.
   if (beginning.err) {
-    return new Conversion(typed, arg, Status.ERROR, beginning.err);
+    return Promise.resolve(new Conversion(typed, arg, Status.ERROR, beginning.err));
   }
 
   // If the current state is ParseState.COMPLEX, then we can't do completion.
   // so bail out now
   if (beginning.state === ParseState.COMPLEX) {
-    return new Conversion(typed, arg);
+    return Promise.resolve(new Conversion(typed, arg));
   }
 
   // If the current state is not ParseState.NORMAL, then we are inside of a
   // string which means that no completion is possible.
   if (beginning.state !== ParseState.NORMAL) {
-    return new Conversion(typed, arg, Status.INCOMPLETE, '');
+    return Promise.resolve(new Conversion(typed, arg, Status.INCOMPLETE, ''));
   }
 
   var completionPart = typed.substring(beginning.startPos);
   var properties = completionPart.split('.');
   var matchProp;
-  var prop;
+  var prop = undefined;
 
   if (properties.length > 1) {
     matchProp = properties.pop().trimLeft();
     for (var i = 0; i < properties.length; i++) {
       prop = properties[i].trim();
 
       // We can't complete on null.foo, so bail out
       if (scope == null) {
-        return new Conversion(typed, arg, Status.ERROR,
-                l10n.lookup('jstypeParseScope'));
+        return Promise.resolve(new Conversion(typed, arg, Status.ERROR,
+                                        l10n.lookup('jstypeParseScope')));
       }
 
       if (prop === '') {
-        return new Conversion(typed, arg, Status.INCOMPLETE, '');
+        return Promise.resolve(new Conversion(typed, arg, Status.INCOMPLETE, ''));
       }
 
       // Check if prop is a getter function on 'scope'. Functions can change
       // other stuff so we can't execute them to get the next object. Stop here.
       if (this._isSafeProperty(scope, prop)) {
-        return new Conversion(typed, arg);
+        return Promise.resolve(new Conversion(typed, arg));
       }
 
       try {
         scope = scope[prop];
       }
       catch (ex) {
         // It would be nice to be able to report this error in some way but
         // as it can happen just when someone types '{sessionStorage.', it
         // almost doesn't really count as an error, so we ignore it
-        return new Conversion(typed, arg, Status.VALID, '');
+        return Promise.resolve(new Conversion(typed, arg, Status.VALID, ''));
       }
     }
   }
   else {
     matchProp = properties[0].trimLeft();
   }
 
   // If the reason we just stopped adjusting the scope was a non-simple string,
   // then we're not sure if the input is valid or invalid, so accept it
   if (prop && !prop.match(/^[0-9A-Za-z]*$/)) {
-    return new Conversion(typed, arg);
+    return Promise.resolve(new Conversion(typed, arg));
   }
 
   // However if the prop was a simple string, it is an error
   if (scope == null) {
-    return new Conversion(typed, arg, Status.ERROR,
-        l10n.lookupFormat('jstypeParseMissing', [ prop ]));
+    var message = l10n.lookupFormat('jstypeParseMissing', [ prop ]);
+    return Promise.resolve(new Conversion(typed, arg, Status.ERROR, message));
   }
 
   // If the thing we're looking for isn't a simple string, then we're not going
   // to find it, but we're not sure if it's valid or invalid, so accept it
   if (!matchProp.match(/^[0-9A-Za-z]*$/)) {
-    return new Conversion(typed, arg);
+    return Promise.resolve(new Conversion(typed, arg));
   }
 
   // Skip Iterators and Generators.
   if (this._isIteratorOrGenerator(scope)) {
-    return null;
+    return Promise.resolve(new Conversion(typed, arg));
   }
 
   var matchLen = matchProp.length;
   var prefix = matchLen === 0 ? typed : typed.slice(0, -matchLen);
   var status = Status.INCOMPLETE;
   var message = '';
 
   // We really want an array of matches (for sorting) but it's easier to
@@ -3698,17 +3977,17 @@ JavascriptType.prototype.parse = functio
         }
       });
 
       distUpPrototypeChain++;
       root = Object.getPrototypeOf(root);
     }
   }
   catch (ex) {
-    return new Conversion(typed, arg, Status.INCOMPLETE, '');
+    return Promise.resolve(new Conversion(typed, arg, Status.INCOMPLETE, ''));
   }
 
   // Convert to an array for sorting, and while we're at it, note if we got
   // an exact match so we know that this input is valid
   matches = Object.keys(matches).map(function(property) {
     if (property === matchProp) {
       status = Status.VALID;
     }
@@ -3789,20 +4068,21 @@ JavascriptType.prototype.parse = functio
 
   if (predictions.length === 0) {
     status = Status.ERROR;
     message = l10n.lookupFormat('jstypeParseMissing', [ matchProp ]);
   }
 
   // If the match is the only one possible, and its VALID, predict nothing
   if (predictions.length === 1 && status === Status.VALID) {
-    predictions = undefined;
-  }
-
-  return new Conversion(typed, arg, status, message, predictions);
+    predictions = [];
+  }
+
+  return Promise.resolve(new Conversion(typed, arg, status, message,
+                                  Promise.resolve(predictions)));
 };
 
 /**
  * Does the given property have a prefix that indicates that it is vendor
  * specific?
  */
 function isVendorPrefixed(name) {
   return name.indexOf('moz') === 0 ||
@@ -3992,17 +4272,17 @@ JavascriptType.prototype._isIteratorOrGe
  */
 JavascriptType.prototype._isSafeProperty = function(scope, prop) {
   if (typeof scope !== 'object') {
     return false;
   }
 
   // Walk up the prototype chain of 'scope' looking for a property descriptor
   // for 'prop'
-  var propDesc;
+  var propDesc = undefined;
   while (scope) {
     try {
       propDesc = Object.getOwnPropertyDescriptor(scope, prop);
       if (propDesc) {
         break;
       }
     }
     catch (ex) {
@@ -4046,21 +4326,23 @@ exports.JavascriptType = JavascriptType;
  *
  * 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/node', ['require', 'exports', 'module' , 'gcli/host', 'gcli/l10n', 'gcli/types', 'gcli/argument'], function(require, exports, module) {
-
-
-var host = require('gcli/host');
-var l10n = require('gcli/l10n');
+define('gcli/types/node', ['require', 'exports', 'module' , 'util/promise', 'util/host', 'util/l10n', 'gcli/types', 'gcli/argument'], function(require, exports, module) {
+
+'use strict';
+
+var Promise = require('util/promise');
+var host = require('util/host');
+var l10n = require('util/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 BlankArgument = require('gcli/argument').BlankArgument;
 
 
 /**
@@ -4075,17 +4357,17 @@ exports.shutdown = function() {
   types.unregisterType(NodeType);
   types.unregisterType(NodeListType);
 };
 
 /**
  * The object against which we complete, which is usually 'window' if it exists
  * but could be something else in non-web-content environments.
  */
-var doc;
+var doc = undefined;
 if (typeof document !== 'undefined') {
   doc = document;
 }
 
 /**
  * For testing only.
  * The fake empty NodeList used when there are no matches, we replace this with
  * something that looks better as soon as we have a document, so not only
@@ -4132,46 +4414,46 @@ NodeType.prototype.stringify = function(
   if (value == null) {
     return '';
   }
   return value.__gcliQuery || 'Error';
 };
 
 NodeType.prototype.parse = function(arg) {
   if (arg.text === '') {
-    return new Conversion(undefined, arg, Status.INCOMPLETE);
+    return Promise.resolve(new Conversion(undefined, arg, Status.INCOMPLETE));
   }
 
   var nodes;
   try {
     nodes = doc.querySelectorAll(arg.text);
   }
   catch (ex) {
-    return new Conversion(undefined, arg, Status.ERROR,
-            l10n.lookup('nodeParseSyntax'));
+    return Promise.resolve(new Conversion(undefined, arg, Status.ERROR,
+                                          l10n.lookup('nodeParseSyntax')));
   }
 
   if (nodes.length === 0) {
-    return new Conversion(undefined, arg, Status.INCOMPLETE,
-        l10n.lookup('nodeParseNone'));
+    return Promise.resolve(new Conversion(undefined, arg, Status.INCOMPLETE,
+                                          l10n.lookup('nodeParseNone')));
   }
 
   if (nodes.length === 1) {
     var node = nodes.item(0);
     node.__gcliQuery = arg.text;
 
     host.flashNodes(node, true);
 
-    return new Conversion(node, arg, Status.VALID, '');
+    return Promise.resolve(new Conversion(node, arg, Status.VALID, ''));
   }
 
   host.flashNodes(nodes, false);
 
-  return new Conversion(undefined, arg, Status.ERROR,
-          l10n.lookupFormat('nodeParseMultiple', [ nodes.length ]));
+  var message = l10n.lookupFormat('nodeParseMultiple', [ nodes.length ]);
+  return Promise.resolve(new Conversion(undefined, arg, Status.ERROR, message));
 };
 
 NodeType.prototype.name = 'node';
 
 
 
 /**
  * A CSS expression that refers to a node list.
@@ -4203,35 +4485,35 @@ NodeListType.prototype.stringify = funct
   if (value == null) {
     return '';
   }
   return value.__gcliQuery || 'Error';
 };
 
 NodeListType.prototype.parse = function(arg) {
   if (arg.text === '') {
-    return new Conversion(undefined, arg, Status.INCOMPLETE);
+    return Promise.resolve(new Conversion(undefined, arg, Status.INCOMPLETE));
   }
 
   var nodes;
   try {
     nodes = doc.querySelectorAll(arg.text);
   }
   catch (ex) {
-    return new Conversion(undefined, arg, Status.ERROR,
-            l10n.lookup('nodeParseSyntax'));
+    return Promise.resolve(new Conversion(undefined, arg, Status.ERROR,
+                                    l10n.lookup('nodeParseSyntax')));
   }
 
   if (nodes.length === 0 && !this.allowEmpty) {
-    return new Conversion(undefined, arg, Status.INCOMPLETE,
-        l10n.lookup('nodeParseNone'));
+    return Promise.resolve(new Conversion(undefined, arg, Status.INCOMPLETE,
+                                    l10n.lookup('nodeParseNone')));
   }
 
   host.flashNodes(nodes, false);
-  return new Conversion(nodes, arg, Status.VALID, '');
+  return Promise.resolve(new Conversion(nodes, arg, Status.VALID, ''));
 };
 
 NodeListType.prototype.name = 'nodelist';
 
 
 });
 /*
  * Copyright 2012, Mozilla Foundation and contributors
@@ -4244,18 +4526,19 @@ NodeListType.prototype.name = 'nodelist'
  *
  * 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/host', ['require', 'exports', 'module' ], function(require, exports, module) {
-
+define('util/host', ['require', 'exports', 'module' ], function(require, exports, module) {
+
+  'use strict';
 
   /**
    * The chromeWindow as as required by Highlighter, so it knows where to
    * create temporary highlight nodes.
    */
   exports.chromeWindow = undefined;
 
   /**
@@ -4290,19 +4573,21 @@ define('gcli/host', ['require', 'exports
  *
  * 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/resource', ['require', 'exports', 'module' , 'gcli/types', 'gcli/types/selection'], function(require, exports, module) {
-
-
+define('gcli/types/resource', ['require', 'exports', 'module' , 'util/promise', 'gcli/types', 'gcli/types/selection'], function(require, exports, module) {
+
+'use strict';
+
+var Promise = require('util/promise');
 var types = require('gcli/types');
 var SelectionType = require('gcli/types/selection').SelectionType;
 
 
 /**
  * Registration and de-registration.
  */
 exports.startup = function() {
@@ -4317,17 +4602,17 @@ exports.shutdown = function() {
 exports.clearResourceCache = function() {
   ResourceCache.clear();
 };
 
 /**
  * The object against which we complete, which is usually 'window' if it exists
  * but could be something else in non-web-content environments.
  */
-var doc;
+var doc = undefined;
 if (typeof document !== 'undefined') {
   doc = document;
 }
 
 /**
  * Setter for the document that contains the nodes we're matching
  */
 exports.setDocument = function(document) {
@@ -4543,19 +4828,19 @@ ResourceType.prototype.getLookup = funct
   var resources = [];
   if (this.include !== Resource.TYPE_SCRIPT) {
     Array.prototype.push.apply(resources, CssResource._getAllStyles());
   }
   if (this.include !== Resource.TYPE_CSS) {
     Array.prototype.push.apply(resources, ScriptResource._getAllScripts());
   }
 
-  return resources.map(function(resource) {
+  return Promise.resolve(resources.map(function(resource) {
     return { name: resource.name, value: resource };
-  });
+  }));
 };
 
 ResourceType.prototype.name = 'resource';
 
 
 /**
  * A quick cache of resources against nodes
  * TODO: Potential memory leak when the target document has css or script
@@ -4607,21 +4892,22 @@ var ResourceCache = {
  * 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/setting', ['require', 'exports', 'module' , 'gcli/settings', 'gcli/types', 'gcli/types/selection', 'gcli/types/basic'], function(require, exports, module) {
 
+'use strict';
 
 var settings = require('gcli/settings');
 var types = require('gcli/types');
 var SelectionType = require('gcli/types/selection').SelectionType;
-var DeferredType = require('gcli/types/basic').DeferredType;
+var DelegateType = require('gcli/types/basic').DelegateType;
 
 
 /**
  * Registration and de-registration.
  */
 exports.startup = function() {
   types.registerType(SettingType);
   types.registerType(SettingValueType);
@@ -4629,19 +4915,19 @@ exports.startup = function() {
 
 exports.shutdown = function() {
   types.unregisterType(SettingType);
   types.unregisterType(SettingValueType);
 };
 
 /**
  * This is a whole new level of nasty. 'setting' and 'settingValue' are a pair
- * for obvious reasons. settingValue is a deferred type - it defers to the type
- * of the setting, but how do we implement the defer function - how does it
- * work out its paired setting?
+ * for obvious reasons. settingValue is a delegate type - it delegates to the
+ * type of the setting, but how do we implement the defer function - how does
+ * it work out its paired setting?
  * In another parallel universe we pass the requisition to all the parse
  * methods so we can extract the args in SettingValueType.parse, however that
  * seems like a lot of churn for a simple way to connect 2 things. So we're
  * hacking. SettingType tries to keep 'lastSetting' up to date.
  */
 var lastSetting = null;
 
 /**
@@ -4666,33 +4952,35 @@ SettingType.prototype.noMatch = function
 };
 
 SettingType.prototype.stringify = function(option) {
   lastSetting = option;
   return SelectionType.prototype.stringify.call(this, option);
 };
 
 SettingType.prototype.parse = function(arg) {
-  var conversion = SelectionType.prototype.parse.call(this, arg);
-  lastSetting = conversion.value;
-  return conversion;
+  var promise = SelectionType.prototype.parse.call(this, arg);
+  promise.then(function(conversion) {
+    lastSetting = conversion.value;
+  });
+  return promise;
 };
 
 SettingType.prototype.name = 'setting';
 
 
 /**
  * A type for entering the value of a known setting
  */
 function SettingValueType(typeSpec) {
 }
 
-SettingValueType.prototype = Object.create(DeferredType.prototype);
-
-SettingValueType.prototype.defer = function() {
+SettingValueType.prototype = Object.create(DelegateType.prototype);
+
+SettingValueType.prototype.delegateType = function() {
   if (lastSetting != null) {
     return lastSetting.type;
   }
   else {
     return types.getType('blank');
   }
 };
 
@@ -4711,17 +4999,19 @@ SettingValueType.prototype.name = 'setti
  *
  * 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/settings', ['require', 'exports', 'module' , 'gcli/util', 'gcli/types'], function(require, exports, module) {
+define('gcli/settings', ['require', 'exports', 'module' , 'util/util', 'gcli/types'], function(require, exports, module) {
+
+'use strict';
 
 var imports = {};
 
 Components.utils.import('resource://gre/modules/XPCOMUtils.jsm', imports);
 
 imports.XPCOMUtils.defineLazyGetter(imports, 'prefBranch', function() {
   var prefService = Components.classes['@mozilla.org/preferences-service;1']
           .getService(Components.interfaces.nsIPrefService);
@@ -4730,17 +5020,17 @@ imports.XPCOMUtils.defineLazyGetter(impo
 });
 
 imports.XPCOMUtils.defineLazyGetter(imports, 'supportsString', function() {
   return Components.classes["@mozilla.org/supports-string;1"]
           .createInstance(Components.interfaces.nsISupportsString);
 });
 
 
-var util = require('gcli/util');
+var util = require('util/util');
 var types = require('gcli/types');
 
 /**
  * All local settings have this prefix when used in Firefox
  */
 var DEVTOOLS_PREFIX = 'devtools.gcli.';
 
 /**
@@ -5002,21 +5292,23 @@ exports.removeSetting = function() { };
  *
  * 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/ui/intro', ['require', 'exports', 'module' , 'gcli/settings', 'gcli/l10n', 'gcli/util', 'gcli/ui/view', 'gcli/cli', 'text!gcli/ui/intro.html'], function(require, exports, module) {
-
+define('gcli/ui/intro', ['require', 'exports', 'module' , 'util/util', 'util/l10n', 'gcli/settings', 'gcli/ui/view', 'gcli/cli', 'text!gcli/ui/intro.html'], function(require, exports, module) {
+
+  'use strict';
+
+  var util = require('util/util');
+  var l10n = require('util/l10n');
   var settings = require('gcli/settings');
-  var l10n = require('gcli/l10n');
-  var util = require('gcli/util');
   var view = require('gcli/ui/view');
   var Output = require('gcli/cli').Output;
 
   /**
    * Record if the user has clicked on 'Got It!'
    */
   var hideIntroSettingSpec = {
     name: 'hideIntro',
@@ -5089,21 +5381,22 @@ define('gcli/ui/intro', ['require', 'exp
  *
  * 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/ui/view', ['require', 'exports', 'module' , 'gcli/util', 'gcli/ui/domtemplate'], function(require, exports, module) {
-
-
-var util = require('gcli/util');
-var domtemplate = require('gcli/ui/domtemplate');
+define('gcli/ui/view', ['require', 'exports', 'module' , 'util/util', 'util/domtemplate'], function(require, exports, module) {
+
+'use strict';
+
+var util = require('util/util');
+var domtemplate = require('util/domtemplate');
 
 
 /**
  * We want to avoid commands having to create DOM structures because that's
  * messy and because we're going to need to have command output displayed in
  * different documents. A View is a way to wrap an HTML template (for
  * domtemplate) in with the data and options to render the template, so anyone
  * can later run the template in the context of any document.
@@ -5179,17 +5472,19 @@ exports.createView = 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('gcli/ui/domtemplate', ['require', 'exports', 'module' ], function(require, exports, module) {
+define('util/domtemplate', ['require', 'exports', 'module' ], function(require, exports, module) {
+
+  'use strict';
 
   var obj = {};
   Components.utils.import('resource:///modules/devtools/Templater.jsm', obj);
   exports.template = obj.template;
 
 });
 /*
  * Copyright 2012, Mozilla Foundation and contributors
@@ -5202,25 +5497,26 @@ define('gcli/ui/domtemplate', ['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('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');
+define('gcli/cli', ['require', 'exports', 'module' , 'util/promise', 'util/util', 'util/l10n', 'gcli/ui/view', 'gcli/canon', 'gcli/types', 'gcli/types/basic', 'gcli/argument'], function(require, exports, module) {
+
+'use strict';
+
+var Promise = require('util/promise');
+var util = require('util/util');
+var l10n = require('util/l10n');
+
 var view = require('gcli/ui/view');
-var l10n = require('gcli/l10n');
-
 var canon = require('gcli/canon');
-var Q = require('gcli/promise');
 var CommandOutputManager = require('gcli/canon').CommandOutputManager;
 
 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;
@@ -5330,29 +5626,30 @@ Assignment.prototype.getPredictions = fu
  * @param index The index of the prediction to choose
  */
 Assignment.prototype.getPredictionAt = function(index) {
   if (index == null) {
     index = 0;
   }
 
   if (this.isInName()) {
-    return undefined;
-  }
-
-  var predictions = this.getPredictions();
-  if (predictions.length === 0) {
-    return undefined;
-  }
-
-  index = index % predictions.length;
-  if (index < 0) {
-    index = predictions.length + index;
-  }
-  return predictions[index];
+    return Promise.resolve(undefined);
+  }
+
+  return this.getPredictions().then(function(predictions) {
+    if (predictions.length === 0) {
+      return undefined;
+    }
+
+    index = index % predictions.length;
+    if (index < 0) {
+      index = predictions.length + index;
+    }
+    return predictions[index];
+  }.bind(this), console.error);
 };
 
 /**
  * Some places want to take special action if we are in the name part of a
  * named argument (i.e. the '--foo' bit).
  * Currently this does not take actual cursor position into account, it just
  * assumes that the cursor is at the end. In the future we will probably want
  * to take this into account.
@@ -5375,17 +5672,18 @@ Assignment.prototype.ensureVisibleArgume
   if (this.conversion.arg.type !== 'BlankArgument') {
     return false;
   }
 
   var arg = this.conversion.arg.beget({
     text: '',
     prefixSpace: this.param instanceof CommandAssignment
   });
-  this.conversion = this.param.type.parse(arg);
+  // For trivial input like { test: '' }, parse() should be synchronous ...
+  this.conversion = util.synchronize(this.param.type.parse(arg));
   this.conversion.assign(this);
 
   return true;
 };
 
 /**
  * Work out what the status of the current conversion is which involves looking
  * not only at the conversion, but also checking if data has been provided
@@ -5417,24 +5715,28 @@ Assignment.prototype.toString = function
 
 /**
  * For test/debug use only. The output from this function is subject to wanton
  * random change without notice, and should not be relied upon to even exist
  * at some later date.
  */
 Object.defineProperty(Assignment.prototype, '_summaryJson', {
   get: function() {
+    var predictionCount = '<async>';
+    this.getPredictions().then(function(predictions) {
+      predictionCount = predictions.length;
+    }, console.log);
     return {
       param: this.param.name + '/' + this.param.type.name,
       defaultValue: this.param.defaultValue,
       arg: this.conversion.arg._summaryJson,
       value: this.value,
       message: this.getMessage(),
       status: this.getStatus().toString(),
-      predictionCount: this.getPredictions().length
+      predictionCount: predictionCount
     };
   },
   enumerable: true
 });
 
 exports.Assignment = Assignment;
 
 
@@ -5534,17 +5836,18 @@ function UnassignedAssignment(requisitio
       name: 'param',
       requisition: requisition,
       isIncompleteName: (arg.text.charAt(0) === '-')
     }
   });
   this.paramIndex = -1;
   this.onAssignmentChange = util.createEvent('UnassignedAssignment.onAssignmentChange');
 
-  this.conversion = this.param.type.parse(arg);
+  // synchronize is ok because we can be sure that param type is synchronous
+  this.conversion = util.synchronize(this.param.type.parse(arg));
   this.conversion.assign(this);
 }
 
 UnassignedAssignment.prototype = Object.create(Assignment.prototype);
 
 UnassignedAssignment.prototype.getStatus = function(arg) {
   return this.conversion.getStatus();
 };
@@ -5590,17 +5893,19 @@ function Requisition(environment, doc, c
       // Ignore
     }
   }
   this.commandOutputManager = commandOutputManager || new CommandOutputManager();
 
   // The command that we are about to execute.
   // @see setCommandConversion()
   this.commandAssignment = new CommandAssignment();
-  this.setAssignment(this.commandAssignment, null);
+  var promise = this.setAssignment(this.commandAssignment, null,
+                                   { skipArgUpdate: true });
+  util.synchronize(promise);
 
   // 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 = {};
@@ -5675,17 +5980,19 @@ 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);
+      var promise = this.setAssignment(assignment, null,
+                                       { skipArgUpdate: true });
+      util.synchronize(promise);
       assignment.onAssignmentChange.add(this._assignmentChanged, this);
       this._assignments[param.name] = assignment;
     }
   }
   this.assignmentCount = Object.keys(this._assignments).length;
 };
 
 /**
@@ -5804,26 +6111,27 @@ Requisition.prototype.getAssignments = f
 
 /**
  * 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 options There are a number of ways to customize how the assignment
  * is made, including:
- * - argUpdate: (default:false) Adjusts the args in this requisition to keep
+ * - 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.
- * - matchPadding: (default:false) If argUpdate=true, and matchPadding=true
- *   then further take the step of altering the whitespace on the prefix and
- *   suffix of the new argument to match that of the old argument.
+ * - matchPadding: (default:false) Altering the whitespace on the prefix and
+ *   suffix of the new argument to match that of the old argument. This only
+ *   makes sense with skipArgUpdate=false
+ *   then further take the step of
  */
 Requisition.prototype.setAssignment = function(assignment, arg, options) {
   options = options || {};
-  if (options.argUpdate) {
+  if (options.skipArgUpdate !== true) {
     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
@@ -5854,147 +6162,188 @@ Requisition.prototype.setAssignment = fu
             replacementArgs[i].suffix = this._args[index].suffix;
           }
         }
         this._args[index] = replacementArgs[i];
       }
     }
   }
 
-  var conversion;
+  function setAssignmentInternal(conversion) {
+    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
+    });
+  }
+
   if (arg == null) {
-    conversion = assignment.param.type.getBlank();
+    setAssignmentInternal(assignment.param.type.getBlank());
   }
   else if (typeof arg.getStatus === 'function') {
-    conversion = arg;
+    setAssignmentInternal(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
-  });
+    return assignment.param.type.parse(arg).then(function(conversion) {
+      setAssignmentInternal(conversion);
+    }.bind(this), console.error);
+  }
+
+  return Promise.resolve(undefined);
 };
 
 /**
  * Reset all the assignments to their default values
  */
 Requisition.prototype.setBlankArguments = function() {
   this.getAssignments().forEach(function(assignment) {
-    this.setAssignment(assignment, null);
+    var promise = this.setAssignment(assignment, null, { skipArgUpdate: true });
+    util.synchronize(promise);
   }, this);
 };
 
 /**
  * Complete the argument at <tt>cursor</tt>.
  * Basically the same as:
  *   assignment = getAssignmentAt(cursor);
  *   assignment.value = assignment.conversion.predictions[0];
  * Except it's done safely, and with particular care to where we place the
  * space, which is complex, and annoying if we get it wrong.
+ *
+ * WARNING: complete() can happen asynchronously.
+ *
  * @param cursor The cursor configuration. Should have start and end properties
  * which should be set to start and end of the selection.
  * @param predictionChoice The index of the prediction that we should choose.
  * This number is not bounded by the size of the prediction array, we take the
  * modulus to get it within bounds
+ * @return A promise which completes (with undefined) when any outstanding
+ * completion tasks are done.
  */
 Requisition.prototype.complete = function(cursor, predictionChoice) {
   var assignment = this.getAssignmentAt(cursor.start);
 
-  this.onTextChange.holdFire();
-
-  var prediction = assignment.getPredictionAt(predictionChoice);
-  if (prediction == null) {
-    // No predictions generally means we shouldn't change anything on TAB, but
-    // TAB has the connotation of 'next thing' and when we're at the end of
-    // a thing that implies that we should add a space. i.e.
-    // 'help<TAB>' -> 'help '
-    // But we should only do this if the thing that we're 'completing' is valid
-    // and doesn't already end in a space.
-    if (assignment.arg.suffix.slice(-1) !== ' ' &&
-            assignment.getStatus() === Status.VALID) {
-      this._addSpace(assignment);
-    }
-
-    // Also add a space if we are in the name part of an assignment, however
-    // this time we don't want the 'push the space to the next assignment'
-    // logic, so we don't use addSpace
-    if (assignment.isInName()) {
-      var newArg = assignment.conversion.arg.beget({ prefixPostSpace: true });
-      this.setAssignment(assignment, newArg, { argUpdate: true });
-    }
-  }
-  else {
-    // Mutate this argument to hold the completion
-    var arg = assignment.arg.beget({
-      text: prediction.name,
-      dontQuote: (assignment === this.commandAssignment)
-    });
-    this.setAssignment(assignment, arg, { argUpdate: true });
-
-    if (!prediction.incomplete) {
-      // The prediction is complete, add a space to let the user move-on
-      this._addSpace(assignment);
-
-      // Bug 779443 - Remove or explain the reparse
-      if (assignment instanceof UnassignedAssignment) {
-        this.update(this.toString());
-      }
-    }
-  }
-
-  this.onTextChange();
-  this.onTextChange.resumeFire();
+  var predictionPromise = assignment.getPredictionAt(predictionChoice);
+  return predictionPromise.then(function(prediction) {
+    var outstanding = [];
+    this.onTextChange.holdFire();
+
+    // Note: Since complete is asynchronous we should perhaps have a system to
+    // bail out of making changes if the command line has changed since TAB
+    // was pressed. It's not yet clear if this will be a problem.
+
+    if (prediction == null) {
+      // No predictions generally means we shouldn't change anything on TAB,
+      // but TAB has the connotation of 'next thing' and when we're at the end
+      // of a thing that implies that we should add a space. i.e.
+      // 'help<TAB>' -> 'help '
+      // But we should only do this if the thing that we're 'completing' is
+      // valid and doesn't already end in a space.
+      if (assignment.arg.suffix.slice(-1) !== ' ' &&
+              assignment.getStatus() === Status.VALID) {
+        outstanding.push(this._addSpace(assignment));
+      }
+
+      // Also add a space if we are in the name part of an assignment, however
+      // this time we don't want the 'push the space to the next assignment'
+      // logic, so we don't use addSpace
+      if (assignment.isInName()) {
+        var newArg = assignment.conversion.arg.beget({ prefixPostSpace: true });
+        var p = this.setAssignment(assignment, newArg);
+        outstanding.push(p);
+      }
+    }
+    else {
+      // Mutate this argument to hold the completion
+      var arg = assignment.arg.beget({
+        text: prediction.name,
+        dontQuote: (assignment === this.commandAssignment)
+      });
+      var promise = this.setAssignment(assignment, arg);
+
+      if (!prediction.incomplete) {
+        promise = promise.then(function() {
+          // The prediction is complete, add a space to let the user move-on
+          return this._addSpace(assignment).then(function() {
+            // Bug 779443 - Remove or explain the re-parse
+            if (assignment instanceof UnassignedAssignment) {
+              return this.update(this.toString());
+            }
+          }.bind(this));
+        }.bind(this));
+      }
+
+      outstanding.push(promise);
+    }
+
+    return util.all(outstanding).then(function() {
+      this.onTextChange();
+      this.onTextChange.resumeFire();
+    }.bind(this));
+  }.bind(this));
+};
+
+/**
+ * A test method to check that all args are assigned in some way
+ */
+Requisition.prototype._assertArgsAssigned = function() {
+  this._args.forEach(function(arg) {
+    if (arg.assignment == null) {
+      console.log('No assignment for ' + arg);
+    }
+  }, this);
 };
 
 /**
  * Pressing TAB sometimes requires that we add a space to denote that we're on
  * to the 'next thing'.
  * @param assignment The assignment to which to append the space
  */
 Requisition.prototype._addSpace = function(assignment) {
   var arg = assignment.conversion.arg.beget({ suffixSpace: true });
   if (arg !== assignment.conversion.arg) {
-    this.setAssignment(assignment, arg, { argUpdate: true });
+    return this.setAssignment(assignment, arg);
+  }
+  else {
+    return Promise.resolve(undefined);
   }
 };
 
 /**
  * Replace the current value with the lower value if such a concept exists.
  */
 Requisition.prototype.decrement = function(assignment) {
   var replacement = assignment.param.type.decrement(assignment.conversion.value);
   if (replacement != null) {
     var str = assignment.param.type.stringify(replacement);
     var arg = assignment.conversion.arg.beget({ text: str });
-    this.setAssignment(assignment, arg, { argUpdate: true });
+    var promise = this.setAssignment(assignment, arg);
+    util.synchronize(promise);
   }
 };
 
 /**
  * Replace the current value with the higher value if such a concept exists.
  */
 Requisition.prototype.increment = function(assignment) {
   var replacement = assignment.param.type.increment(assignment.conversion.value);
   if (replacement != null) {
     var str = assignment.param.type.stringify(replacement);
     var arg = assignment.conversion.arg.beget({ text: str });
-    this.setAssignment(assignment, arg, { argUpdate: true });
+    var promise = this.setAssignment(assignment, arg);
+    util.synchronize(promise);
   }
 };
 
 /**
  * Extract a canonical version of the input
  */
 Requisition.prototype.toCanonicalString = function() {
   var line = [];
@@ -6232,54 +6581,51 @@ Requisition.prototype.getAssignmentAt = 
         ' cursor=' + cursor + ' text=' + this.toString());
   }
 
   return reply;
 };
 
 /**
  * Entry point for keyboard accelerators or anything else that wants to execute
- * a command. There are 3 ways to call <tt>exec()</tt>:
- * 1. Without any parameters. This assumes that the command to be executed has
- *    already been parsed by the requisition using <tt>update()</tt>.
- * 2. With a string parameter, or an object with a 'typed' property. This is
- *    effectively a shortcut for calling <tt>update(typed); exec();</tt>
- * 3. With input having a 'command' property which is either a command object
- *    (i.e. from canon.getCommand) or a string which can be passed to
- *    canon.getCommand() plus and optional 'args' property which contains the
- *    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) {
+ * a command.
+ * @param options Object describing how the execution should be handled.
+ * (optional). Contains some of the following properties:
+ * - hidden (boolean, default=false) Should the output be hidden from the
+ *   commandOutputManager for this requisition
+ * - command/args A fast shortcut to executing a known command with a known
+ *   set of parsed arguments.
+ * - typed (string, deprecated) Don't use this. Also don't set the options
+ *   object itself to be a string.
+ */
+Requisition.prototype.exec = function(options) {
   var command = null;
   var args = null;
   var hidden = false;
-  if (input && input.hidden) {
+  if (options && options.hidden) {
     hidden = true;
   }
 
-  if (input) {
+  if (options) {
     if (typeof input === 'string') {
-      this.update(input);
-    }
-    else if (typeof input.typed === 'string') {
-      this.update(input.typed);
-    }
-    else if (input.command != null) {
+      // Deprecated - does not handle async properly
+      this.update(options);
+    }
+    else if (typeof options.typed === 'string') {
+      // Deprecated - does not handle async properly
+      this.update(options.typed);
+    }
+    else if (options.command != null) {
       // Fast track by looking up the command directly since passed args
       // means there is no command line to parse.
-      command = canon.getCommand(input.command);
+      command = canon.getCommand(options.command);
       if (!command) {
-        console.error('Command not found: ' + input.command);
-      }
-      args = input.args;
+        console.error('Command not found: ' + options.command);
+      }
+      args = options.args;
     }
   }
 
   if (!command) {
     command = this.commandAssignment.value;
     args = this.getArgsObject();
   }
 
@@ -6300,57 +6646,81 @@ 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);
-  };
+  var onDone = function(data) { output.complete(data, false); };
+  var onError = function(error) { output.complete(error, true); };
 
   try {
     var context = exports.createExecutionContext(this);
     var reply = command.exec(args, context);
 
     this._then(reply, onDone, onError);
   }
   catch (ex) {
+    console.error(ex);
     onError(ex);
   }
 
-  this.update('');
+  this.clear();
   return output;
 };
 
 /**
+ * A shortcut for calling update, resolving the promise and then exec.
+ * @param input The string to execute
+ * @param options Passed to exec
+ * @return A promise of an output object
+ */
+Requisition.prototype.updateExec = function(input, options) {
+  return this.update(input).then(function() {
+    return this.exec(options);
+  }.bind(this));
+};
+
+/**
+ * Similar to update('') except that it's guaranteed to execute synchronously
+ */
+Requisition.prototype.clear = function() {
+  this._structuralChangeInProgress = true;
+
+  var arg = new Argument('', '', '');
+  this._args = [ arg ];
+
+  var commandType = this.commandAssignment.param.type;
+  var conversion = util.synchronize(commandType.parse(arg));
+  this.setAssignment(this.commandAssignment, conversion,
+                     { skipArgUpdate: true });
+
+  this._structuralChangeInProgress = false;
+  this.onTextChange();
+};
+
+/**
  * 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
+    // 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
+    // Deprecated: When we're passed a deferred rather than a promise
     then = thing.promise.then;
   }
 
   if (then != null) {
     then(onDone, onError);
   }
   else {
     onDone(thing);
@@ -6361,21 +6731,23 @@ Requisition.prototype._then = function(t
  * 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
-  this._split(args);
-  this._assign(args);
-
-  this._structuralChangeInProgress = false;
-  this.onTextChange();
+
+  return this._split(args).then(function() {
+    return this._assign(args).then(function() {
+      this._structuralChangeInProgress = false;
+      this.onTextChange();
+    }.bind(this));
+  }.bind(this));
 };
 
 /**
  * For test/debug use only. The output from this function is subject to wanton
  * random change without notice, and should not be relied upon to even exist
  * at some later date.
  */
 Object.defineProperty(Requisition.prototype, '_summaryJson', {
@@ -6634,100 +7006,110 @@ function isSimple(typed) {
    return true;
 }
 
 /**
  * Looks in the canon for a command extension that matches what has been
  * typed at the command line.
  */
 Requisition.prototype._split = function(args) {
+  // We're processing args, so we don't want the assignments that we make to
+  // try to adjust other args assuming this is an external update
+  var noArgUp = { skipArgUpdate: true };
+
   // 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;
+  var conversion = undefined;
   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.setAssignment(this.commandAssignment, conversion);
-    return;
+    return this.setAssignment(this.commandAssignment, conversion, noArgUp);
   }
 
   var argsUsed = 1;
 
+  var commandType = this.commandAssignment.param.type;
   while (argsUsed <= args.length) {
     var arg = (argsUsed === 1) ?
               args[0] :
               new MergedArgument(args, 0, argsUsed);
-    conversion = this.commandAssignment.param.type.parse(arg);
+    // Making this promise synchronous is OK because we know that commandType
+    // is a synchronous type.
+    conversion = util.synchronize(commandType.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;
     }
 
     // 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.setAssignment(this.commandAssignment, conversion);
-
   for (var i = 0; i < argsUsed; i++) {
     args.shift();
   }
 
+  return this.setAssignment(this.commandAssignment, conversion, noArgUp);
+
   // This could probably be re-written to consume args as we go
 };
 
 /**
  * Add all the passed args to the list of unassigned assignments.
  */
 Requisition.prototype._addUnassignedArgs = function(args) {
   args.forEach(function(arg) {
     this._unassigned.push(new UnassignedAssignment(this, arg));
   }.bind(this));
 };
 
 /**
  * Work out which arguments are applicable to which parameters.
  */
 Requisition.prototype._assign = function(args) {
+  // See comment in _split. Avoid multiple updates
+  var noArgUp = { skipArgUpdate: true };
+
   this._unassigned = [];
+  var outstanding = [];
 
   if (!this.commandAssignment.value) {
     this._addUnassignedArgs(args);
-    return;
+    return util.all(outstanding);
   }
 
   if (args.length === 0) {
     this.setBlankArguments();
-    return;
+    return util.all(outstanding);
   }
 
   // Create an error if the command does not take parameters, but we have
   // been given them ...
   if (this.assignmentCount === 0) {
     this._addUnassignedArgs(args);
-    return;
+    return util.all(outstanding);
   }
 
   // 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);
-      this.setAssignment(assignment, arg);
-      return;
+      outstanding.push(this.setAssignment(assignment, arg, noArgUp));
+      return util.all(outstanding);
     }
   }
 
   // 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();
 
   // We collect the arguments used in arrays here before assigning
@@ -6761,78 +7143,80 @@ Requisition.prototype._assign = function
           var arrayArg = arrayArgs[assignment.param.name];
           if (!arrayArg) {
             arrayArg = new ArrayArgument();
             arrayArgs[assignment.param.name] = arrayArg;
           }
           arrayArg.addArgument(arg);
         }
         else {
-          this.setAssignment(assignment, arg);
+          outstanding.push(this.setAssignment(assignment, arg, noArgUp));
         }
       }
       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) {
-      this.setAssignment(assignment, null);
+      outstanding.push(this.setAssignment(assignment, null, noArgUp));
       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) {
-        this.setAssignment(assignment, null);
+        outstanding.push(this.setAssignment(assignment, null, noArgUp));
       }
       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 {
-          this.setAssignment(assignment, arg);
+          outstanding.push(this.setAssignment(assignment, arg, noArgUp));
         }
       }
     }
   }, this);
 
   // Now we need to assign the array argument (if any)
   Object.keys(arrayArgs).forEach(function(name) {
     var assignment = this.getAssignment(name);
-    this.setAssignment(assignment, arrayArgs[name]);
+    outstanding.push(this.setAssignment(assignment, arrayArgs[name], noArgUp));
   }, this);
 
   // What's left is can't be assigned, but we need to extract
   this._addUnassignedArgs(args);
+
+  return util.all(outstanding);
 };
 
 exports.Requisition = Requisition;
 
 /**
  * A simple object to hold information about the output of a command
  */
 function Output(options) {
@@ -6843,16 +7227,19 @@ function Output(options) {
   this.canonical = options.canonical || '';
   this.hidden = options.hidden === true ? true : false;
 
   this.data = undefined;
   this.completed = false;
   this.error = false;
   this.start = new Date();
 
+  this.deferred = Promise.defer();
+  this.then = this.deferred.promise.then;
+
   this.onClose = util.createEvent('Output.onClose');
   this.onChange = util.createEvent('Output.onChange');
 }
 
 /**
  * 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
@@ -6867,22 +7254,30 @@ Output.prototype.changed = function(data
   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) {
+Output.prototype.complete = function(data, error, ev) {
   this.end = new Date();
   this.duration = this.end.getTime() - this.start.getTime();
   this.completed = true;
+  this.error = error;
 
   this.changed(data, ev);
+
+  if (error) {
+    this.deferred.reject();
+  }
+  else {
+    this.deferred.resolve();
+  }
 };
 
 /**
  * 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) {
@@ -6940,86 +7335,54 @@ Output.prototype.toDom = function(elemen
   element.appendChild(node);
 };
 
 /**
  * Convert this object to a string so GCLI can be used in traditional character
  * based terminals.
  */
 Output.prototype.toString = function(document) {
-  var output = this.data;
-  if (output == null) {
-    return '';
-  }
-
-  if (typeof HTMLElement !== 'undefined' && output instanceof HTMLElement) {
-    return output.textContent;
-  }
-
-  if (output.isView) {
-    return output.toDom(document).textContent;
-  }
-
-  return output.toString();
+  if (this.data.isView) {
+    return this.data.toDom(document).textContent;
+  }
+
+  if (typeof HTMLElement !== 'undefined' && this.data instanceof HTMLElement) {
+    return this.data.textContent;
+  }
+  return this.data == null ? '' : this.data.toString();
 };
 
 exports.Output = Output;
 
 /**
  * Functions and data related to the execution of a command
  */
 exports.createExecutionContext = function(requisition) {
   return {
     exec: requisition.exec.bind(requisition),
     update: requisition.update.bind(requisition),
+    updateExec: requisition.updateExec.bind(requisition),
     document: requisition.document,
     environment: requisition.environment,
     createView: view.createView,
     defer: function() {
-      return Q.defer();
+      return Promise.defer();
     },
     /**
      * @deprecated Use defer() instead, which does the same thing, but is not
      * confusingly named
      */
     createPromise: function() {
-      return Q.defer();
+      return Promise.defer();
     }
   };
 };
 
 
 });
-/*
- * 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/promise', ['require', 'exports', 'module' ], function(require, exports, module) {
-
-  var imported = {};
-  Components.utils.import("resource://gre/modules/commonjs/sdk/core/promise.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.introTextOpening2}</p>\n" +
   "\n" +
   "  <p>\n" +
   "    ${l10n.introTextCommands}\n" +
   "    <span class=\"gcli-out-shortcut\" onclick=\"${onclick}\"\n" +
   "        ondblclick=\"${ondblclick}\" data-command=\"help\">help</span>${l10n.introTextKeys2}\n" +
@@ -7041,22 +7404,23 @@ define("text!gcli/ui/intro.html", [], "\
  *
  * 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/ui/focus', ['require', 'exports', 'module' , 'gcli/util', 'gcli/settings', 'gcli/l10n', 'gcli/canon'], function(require, exports, module) {
-
-
-var util = require('gcli/util');
+define('gcli/ui/focus', ['require', 'exports', 'module' , 'util/util', 'util/l10n', 'gcli/settings', 'gcli/canon'], function(require, exports, module) {
+
+'use strict';
+
+var util = require('util/util');
+var l10n = require('util/l10n');
 var settings = require('gcli/settings');
-var l10n = require('gcli/l10n');
 var canon = require('gcli/canon');
 
 /**
  * Record how much help the user wants from the tooltip
  */
 var Eagerness = {
   NEVER: 1,
   SOMETIMES: 2,
@@ -7461,55 +7825,55 @@ exports.FocusManager = FocusManager;
  *
  * 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/ui/fields/basic', ['require', 'exports', 'module' , 'gcli/util', 'gcli/l10n', 'gcli/argument', 'gcli/types', 'gcli/types/basic', 'gcli/ui/fields'], function(require, exports, module) {
-
-
-var util = require('gcli/util');
-var l10n = require('gcli/l10n');
+define('gcli/ui/fields/basic', ['require', 'exports', 'module' , 'util/util', 'util/l10n', 'gcli/argument', 'gcli/types', 'gcli/types/basic', 'gcli/ui/fields'], function(require, exports, module) {
+
+'use strict';
+
+var util = require('util/util');
+var l10n = require('util/l10n');
 
 var Argument = require('gcli/argument').Argument;
 var TrueNamedArgument = require('gcli/argument').TrueNamedArgument;
 var FalseNamedArgument = require('gcli/argument').FalseNamedArgument;
 var ArrayArgument = require('gcli/argument').ArrayArgument;
-
 var ArrayConversion = require('gcli/types').ArrayConversion;
 
 var StringType = require('gcli/types/basic').StringType;
 var NumberType = require('gcli/types/basic').NumberType;
 var BooleanType = require('gcli/types/basic').BooleanType;
-var DeferredType = require('gcli/types/basic').DeferredType;
+var DelegateType = require('gcli/types/basic').DelegateType;
 var ArrayType = require('gcli/types/basic').ArrayType;
 
 var Field = require('gcli/ui/fields').Field;
 var fields = require('gcli/ui/fields');
 
 
 /**
  * Registration and de-registration.
  */
 exports.startup = function() {
   fields.addField(StringField);
   fields.addField(NumberField);
   fields.addField(BooleanField);
-  fields.addField(DeferredField);
+  fields.addField(DelegateField);
   fields.addField(ArrayField);
 };
 
 exports.shutdown = function() {
   fields.removeField(StringField);
   fields.removeField(NumberField);
   fields.removeField(BooleanField);
-  fields.removeField(DeferredField);
+  fields.removeField(DelegateField);
   fields.removeField(ArrayField);
 };
 
 
 /**
  * A field that allows editing of strings
  */
 function StringField(type, options) {
@@ -7652,34 +8016,34 @@ BooleanField.prototype.getConversion = f
   else {
     arg = new Argument(' ' + this.element.checked);
   }
   return this.type.parse(arg);
 };
 
 
 /**
- * A field that works with deferred types by delaying resolution until that
+ * A field that works with delegate types by delaying resolution until that
  * last possible time
  */
-function DeferredField(type, options) {
+function DelegateField(type, options) {
   Field.call(this, type, options);
   this.options = options;
   this.requisition.onAssignmentChange.add(this.update, this);
 
   this.element = util.createElement(this.document, 'div');
   this.update();
 
-  this.onFieldChange = util.createEvent('DeferredField.onFieldChange');
+  this.onFieldChange = util.createEvent('DelegateField.onFieldChange');
 }
 
-DeferredField.prototype = Object.create(Field.prototype);
-
-DeferredField.prototype.update = function() {
-  var subtype = this.type.defer();
+DelegateField.prototype = Object.create(Field.prototype);
+
+DelegateField.prototype.update = function() {
+  var subtype = this.type.delegateType();
   if (subtype === this.subtype) {
     return;
   }
 
   if (this.field) {
     this.field.onFieldChange.remove(this.fieldChanged, this);
     this.field.destroy();
   }
@@ -7687,37 +8051,37 @@ DeferredField.prototype.update = functio
   this.subtype = subtype;
   this.field = fields.getField(subtype, this.options);
   this.field.onFieldChange.add(this.fieldChanged, this);
 
   util.clearElement(this.element);
   this.element.appendChild(this.field.element);
 };
 
-DeferredField.claim = function(type) {
-  return type instanceof DeferredType ? Field.MATCH : Field.NO_MATCH;
-};
-
-DeferredField.prototype.destroy = function() {
+DelegateField.claim = function(type) {
+  return type instanceof DelegateType ? Field.MATCH : Field.NO_MATCH;
+};
+
+DelegateField.prototype.destroy = function() {
   Field.prototype.destroy.call(this);
   this.requisition.onAssignmentChange.remove(this.update, this);
   delete this.element;
   delete this.document;
   delete this.onInputChange;
 };
 
-DeferredField.prototype.setConversion = function(conversion) {
+DelegateField.prototype.setConversion = function(conversion) {
   this.field.setConversion(conversion);
 };
 
-DeferredField.prototype.getConversion = function() {
+DelegateField.prototype.getConversion = function() {
   return this.field.getConversion();
 };
 
-Object.defineProperty(DeferredField.prototype, 'isImportant', {
+Object.defineProperty(DelegateField.prototype, 'isImportant', {
   get: function() {
     return this.field.isImportant;
   },
   enumerable: true
 });
 
 
 /**
@@ -7772,35 +8136,37 @@ ArrayField.prototype.setConversion = fun
     this._onAdd(null, subConversion);
   }, this);
 };
 
 ArrayField.prototype.getConversion = function() {
   var conversions = [];
   var arrayArg = new ArrayArgument();
   for (var i = 0; i < this.members.length; i++) {
-    var conversion = this.members[i].field.getConversion();
-    conversions.push(conversion);
-    arrayArg.addArgument(conversion.arg);
+    Promise.resolve(this.members[i].field.getConversion()).then(function(conversion) {
+      conversions.push(conversion);
+      arrayArg.addArgument(conversion.arg);
+    }.bind(this), console.error);
   }
   return new ArrayConversion(conversions, arrayArg);
 };
 
 ArrayField.prototype._onAdd = function(ev, subConversion) {
   // <div class=gcliArrayMbr save="${element}">
   var element = util.createElement(this.document, 'div');
   element.classList.add('gcli-array-member');
   this.container.appendChild(element);
 
   // ${field.element}
   var field = fields.getField(this.type.subtype, this.options);
   field.onFieldChange.add(function() {
-    var conversion = this.getConversion();
-    this.onFieldChange({ conversion: conversion });
-    this.setMessage(conversion.message);
+    Promise.resolve(this.getConversion()).then(function(conversion) {
+      this.onFieldChange({ conversion: conversion });
+      this.setMessage(conversion.message);
+    }.bind(this), console.error);
   }, this);
 
   if (subConversion) {
     field.setConversion(subConversion);
   }
   element.appendChild(field.element);
 
   // <div class=gcliArrayMbrDel onclick="${_onDel}">
@@ -7840,21 +8206,23 @@ ArrayField.prototype._onAdd = function(e
  *
  * 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/ui/fields', ['require', 'exports', 'module' , 'gcli/util', 'gcli/types/basic'], function(require, exports, module) {
-
-
-var util = require('gcli/util');
-var KeyEvent = require('gcli/util').KeyEvent;
+define('gcli/ui/fields', ['require', 'exports', 'module' , 'util/promise', 'util/util', 'gcli/types/basic'], function(require, exports, module) {
+
+'use strict';
+
+var Promise = require('util/promise');
+var util = require('util/util');
+var KeyEvent = require('util/util').KeyEvent;
 
 var BlankType = require('gcli/types/basic').BlankType;
 
 /**
  * A Field is a way to get input for a single parameter.
  * This class is designed to be inherited from. It's important that all
  * subclasses have a similar constructor signature because they are created
  * via getField(...)
@@ -7925,23 +8293,24 @@ Field.prototype.setMessage = function(me
   }
 };
 
 /**
  * Method to be called by subclasses when their input changes, which allows us
  * to properly pass on the onFieldChange event.
  */
 Field.prototype.onInputChange = function(ev) {
-  var conversion = this.getConversion();
-  this.onFieldChange({ conversion: conversion });
-  this.setMessage(conversion.message);
-
-  if (ev.keyCode === KeyEvent.DOM_VK_RETURN) {
-    this.requisition.exec();
-  }
+  Promise.resolve(this.getConversion()).then(function(conversion) {
+    this.onFieldChange({ conversion: conversion });
+    this.setMessage(conversion.message);
+
+    if (ev.keyCode === KeyEvent.DOM_VK_RETURN) {
+      this.requisition.exec();
+    }
+  }.bind(this), console.error);
 };
 
 /**
  * Some fields contain information that is more important to the user, for
  * example error messages and completion menus.
  */
 Field.prototype.isImportant = false;
 
@@ -8043,17 +8412,17 @@ exports.getField = function(type, option
     return new BlankField(type, options);
   }
 
   return new ctor(type, options);
 };
 
 
 /**
- * For use with deferred types that do not yet have anything to resolve to.
+ * For use with delegate types that do not yet have anything to resolve to.
  * BlankFields are not for general use.
  */
 function BlankField(type, options) {
   Field.call(this, type, options);
 
   this.element = util.createElement(this.document, 'div');
 
   this.onFieldChange = util.createEvent('BlankField.onFieldChange');
@@ -8088,21 +8457,25 @@ exports.addField(BlankField);
  *
  * 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/ui/fields/javascript', ['require', 'exports', 'module' , 'gcli/util', 'gcli/argument', 'gcli/types/javascript', 'gcli/ui/fields/menu', 'gcli/ui/fields'], function(require, exports, module) {
-
-
-var util = require('gcli/util');
-
+define('gcli/ui/fields/javascript', ['require', 'exports', 'module' , 'util/util', 'util/promise', 'gcli/types', 'gcli/argument', 'gcli/types/javascript', 'gcli/ui/fields/menu', 'gcli/ui/fields'], function(require, exports, module) {
+
+'use strict';
+
+var util = require('util/util');
+var Promise = require('util/promise');
+
+var Status = require('gcli/types').Status;
+var Conversion = require('gcli/types').Conversion;
 var ScriptArgument = require('gcli/argument').ScriptArgument;
 var JavascriptType = require('gcli/types/javascript').JavascriptType;
 
 var Menu = require('gcli/ui/fields/menu').Menu;
 var Field = require('gcli/ui/fields').Field;
 var fields = require('gcli/ui/fields');
 
 
@@ -8138,17 +8511,19 @@ function JavascriptField(type, options) 
 
   this.menu = new Menu({
     document: this.document,
     field: true,
     type: type
   });
   this.element.appendChild(this.menu.element);
 
-  this.setConversion(this.type.parse(new ScriptArgument('')));
+  var initial = new Conversion(undefined, new ScriptArgument(''),
+                               Status.INCOMPLETE, '');
+  this.setConversion(initial);
 
   this.onFieldChange = util.createEvent('JavascriptField.onFieldChange');
 
   // i.e. Register this.onItemClick as the default action for a menu click
   this.menu.onItemClick.add(this.itemClicked, this);
 }
 
 JavascriptField.prototype = Object.create(Field.prototype);
@@ -8177,45 +8552,47 @@ JavascriptField.prototype.setConversion 
   if (this.type instanceof JavascriptType) {
     var typed = conversion.arg.text;
     var lastDot = typed.lastIndexOf('.');
     if (lastDot !== -1) {
       prefixLen = lastDot;
     }
   }
 
-  var items = [];
-  var predictions = conversion.getPredictions();
-  predictions.forEach(function(item) {
-    // Commands can be hidden
-    if (!item.hidden) {
-      items.push({
-        name: item.name.substring(prefixLen),
-        complete: item.name,
-        description: item.description || ''
-      });
-    }
-  }, this);
-
-  this.menu.show(items);
   this.setMessage(conversion.message);
+
+  conversion.getPredictions().then(function(predictions) {
+    var items = [];
+    predictions.forEach(function(item) {
+      // Commands can be hidden
+      if (!item.hidden) {
+        items.push({
+          name: item.name.substring(prefixLen),
+          complete: item.name,
+          description: item.description || ''
+        });
+      }
+    }, this);
+    this.menu.show(items);
+  }.bind(this), console.error);
 };
 
 JavascriptField.prototype.itemClicked = function(ev) {
-  var conversion = this.type.parse(ev.arg);
-
-  this.onFieldChange({ conversion: conversion });
-  this.setMessage(conversion.message);
+  Promise.resolve(this.type.parse(ev.arg)).then(function(conversion) {
+    this.onFieldChange({ conversion: conversion });
+    this.setMessage(conversion.message);
+  }.bind(this), console.error);
 };
 
 JavascriptField.prototype.onInputChange = function(ev) {
   this.item = ev.currentTarget.item;
-  var conversion = this.getConversion();
-  this.onFieldChange({ conversion: conversion });
-  this.setMessage(conversion.message);
+  Promise.resolve(this.getConversion()).then(function(conversion) {
+    this.onFieldChange({ conversion: conversion });
+    this.setMessage(conversion.message);
+  }.bind(this), console.error);
 };
 
 JavascriptField.prototype.getConversion = function() {
   // This tweaks the prefix/suffix of the argument to fit
   this.arg = new ScriptArgument(this.input.value, '{ ', ' }');
   return this.type.parse(this.arg);
 };
 
@@ -8234,28 +8611,28 @@ JavascriptField.DEFAULT_VALUE = '__Javas
  *
  * 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/ui/fields/menu', ['require', 'exports', 'module' , 'gcli/util', 'gcli/l10n', 'gcli/argument', 'gcli/types', 'gcli/canon', 'gcli/ui/domtemplate', 'text!gcli/ui/fields/menu.css', 'text!gcli/ui/fields/menu.html'], function(require, exports, module) {
-
-
-var util = require('gcli/util');
-var l10n = require('gcli/l10n');
+define('gcli/ui/fields/menu', ['require', 'exports', 'module' , 'util/util', 'util/l10n', 'util/domtemplate', 'gcli/argument', 'gcli/types', 'gcli/canon', 'text!gcli/ui/fields/menu.css', 'text!gcli/ui/fields/menu.html'], function(require, exports, module) {
+
+'use strict';
+
+var util = require('util/util');
+var l10n = require('util/l10n');
+var domtemplate = require('util/domtemplate');
 
 var Argument = require('gcli/argument').Argument;
 var Conversion = require('gcli/types').Conversion;
 var canon = require('gcli/canon');
 
-var domtemplate = require('gcli/ui/domtemplate');
-
 var menuCss = require('text!gcli/ui/fields/menu.css');
 var menuHtml = require('text!gcli/ui/fields/menu.html');
 
 
 /**
  * Menu is a display of the commands that are possible given the state of a
  * requisition.
  * @param options A way to customize the menu display. Valid options are:
@@ -8477,21 +8854,23 @@ define("text!gcli/ui/fields/menu.html", 
  *
  * 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/ui/fields/selection', ['require', 'exports', 'module' , 'gcli/util', 'gcli/l10n', 'gcli/argument', 'gcli/types', 'gcli/types/basic', 'gcli/types/selection', 'gcli/ui/fields/menu', 'gcli/ui/fields'], function(require, exports, module) {
-
-
-var util = require('gcli/util');
-var l10n = require('gcli/l10n');
+define('gcli/ui/fields/selection', ['require', 'exports', 'module' , 'util/promise', 'util/util', 'util/l10n', 'gcli/argument', 'gcli/types', 'gcli/types/basic', 'gcli/types/selection', 'gcli/ui/fields/menu', 'gcli/ui/fields'], function(require, exports, module) {
+
+'use strict';
+
+var Promise = require('util/promise');
+var util = require('util/util');
+var l10n = require('util/l10n');
 
 var Argument = require('gcli/argument').Argument;
 var Status = require('gcli/types').Status;
 var Conversion = require('gcli/types').Conversion;
 var BooleanType = require('gcli/types/basic').BooleanType;
 var SelectionType = require('gcli/types/selection').SelectionType;
 
 var Menu = require('gcli/ui/fields/menu').Menu;
@@ -8529,18 +8908,20 @@ function SelectionField(type, options) {
 
   this.items = [];
 
   this.element = util.createElement(this.document, 'select');
   this.element.classList.add('gcli-field');
   this._addOption({
     name: l10n.lookupFormat('fieldSelectionSelect', [ options.name ])
   });
-  var lookup = this.type.getLookup();
-  lookup.forEach(this._addOption, this);
+
+  Promise.resolve(this.type.getLookup()).then(function(lookup) {
+    lookup.forEach(this._addOption, this);
+  }.bind(this), console.error);
 
   this.onInputChange = this.onInputChange.bind(this);
   this.element.addEventListener('change', this.onInputChange, false);
 
   this.onFieldChange = util.createEvent('SelectionField.onFieldChange');
 }
 
 SelectionField.prototype = Object.create(Field.prototype);
@@ -8603,52 +8984,58 @@ function SelectionTooltipField(type, opt
 
   // i.e. Register this.onItemClick as the default action for a menu click
   this.menu.onItemClick.add(this.itemClicked, this);
 }
 
 SelectionTooltipField.prototype = Object.create(Field.prototype);
 
 SelectionTooltipField.claim = function(type) {
-  return type.getType() instanceof SelectionType ? Field.TOOLTIP_MATCH : Field.NO_MATCH;
+  return type.getType() instanceof SelectionType ?
+      Field.TOOLTIP_MATCH :
+      Field.NO_MATCH;
 };
 
 SelectionTooltipField.prototype.destroy = function() {
   Field.prototype.destroy.call(this);
   this.menu.onItemClick.remove(this.itemClicked, this);
   this.menu.destroy();
   delete this.element;
   delete this.document;
   delete this.onInputChange;
 };
 
 SelectionTooltipField.prototype.setConversion = function(conversion) {
   this.arg = conversion.arg;
-  var items = conversion.getPredictions().map(function(prediction) {
-    // If the prediction value is an 'item' (that is an object with a name and
-    // description) then use that, otherwise use the prediction itself, because
-    // at least that has a name.
-    return prediction.value.description ? prediction.value : prediction;
-  }, this);
-  this.menu.show(items, conversion.arg.text);
   this.setMessage(conversion.message);
+
+  conversion.getPredictions().then(function(predictions) {
+    var items = predictions.map(function(prediction) {
+      // If the prediction value is an 'item' (that is an object with a name and
+      // description) then use that, otherwise use the prediction itself, because
+      // at least that has a name.
+      return prediction.value.description ? prediction.value : prediction;
+    }, this);
+    this.menu.show(items, conversion.arg.text);
+  }.bind(this), console.error);
 };
 
 SelectionTooltipField.prototype.itemClicked = function(ev) {
-  var conversion = this.type.parse(ev.arg);
-
-  this.onFieldChange({ conversion: conversion });
-  this.setMessage(conversion.message);
+  Promise.resolve(this.type.parse(ev.arg)).then(function(conversion) {
+    this.onFieldChange({ conversion: conversion });
+    this.setMessage(conversion.message);
+  }.bind(this), console.error);
 };
 
 SelectionTooltipField.prototype.onInputChange = function(ev) {
   this.item = ev.currentTarget.item;
-  var conversion = this.getConversion();
-  this.onFieldChange({ conversion: conversion });
-  this.setMessage(conversion.message);
+  Promise.resolve(this.getConversion()).then(function(conversion) {
+    this.onFieldChange({ conversion: conversion });
+    this.setMessage(conversion.message);
+  }.bind(this), console.error);
 };
 
 SelectionTooltipField.prototype.getConversion = function() {
   // This tweaks the prefix/suffix of the argument to fit
   this.arg = this.arg.beget({ text: this.input.value });
   return this.type.parse(this.arg);
 };
 
@@ -8690,23 +9077,23 @@ SelectionTooltipField.DEFAULT_VALUE = '_
  *
  * 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/commands/help', ['require', 'exports', 'module' , 'gcli/canon', 'gcli/l10n', 'gcli/util', 'gcli/ui/view', 'text!gcli/commands/help_man.html', 'text!gcli/commands/help_list.html', 'text!gcli/commands/help.css'], function(require, exports, module) {
-var help = exports;
-
-
+define('gcli/commands/help', ['require', 'exports', 'module' , 'util/util', 'util/l10n', 'gcli/canon', 'gcli/ui/view', 'text!gcli/commands/help_man.html', 'text!gcli/commands/help_list.html', 'text!gcli/commands/help.css'], function(require, exports, module) {
+
+'use strict';
+
+var util = require('util/util');
+var l10n = require('util/l10n');
 var canon = require('gcli/canon');
-var l10n = require('gcli/l10n');
-var util = require('gcli/util');
 var view = require('gcli/ui/view');
 
 // Storing the HTML on exports allows other builds to alter the help template
 // but still allowing dryice to do it's dependency thing properly
 exports.helpManHtml = require('text!gcli/commands/help_man.html');
 exports.helpListHtml = require('text!gcli/commands/help_list.html');
 exports.helpCss = require('text!gcli/commands/help.css');
 
@@ -8748,21 +9135,21 @@ var helpCommandSpec = {
       cssId: 'gcli-help'
     });
   }
 };
 
 /**
  * Registration and de-registration.
  */
-help.startup = function() {
+exports.startup = function() {
   canon.addCommand(helpCommandSpec);
 };
 
-help.shutdown = function() {
+exports.shutdown = function() {
   canon.removeCommand(helpCommandSpec);
 };
 
 /**
  * Create a block of data suitable to be passed to the help_list.html template
  */
 function getListTemplateData(args, context) {
   var matchingCommands = canon.getCommands().filter(function(command) {
@@ -8945,21 +9332,22 @@ define("text!gcli/commands/help.css", []
  *
  * 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/commands/pref', ['require', 'exports', 'module' , 'gcli/canon', 'gcli/l10n', 'gcli/settings', 'text!gcli/commands/pref_set_check.html'], function(require, exports, module) {
-
-
+define('gcli/commands/pref', ['require', 'exports', 'module' , 'util/l10n', 'gcli/canon', 'gcli/settings', 'text!gcli/commands/pref_set_check.html'], function(require, exports, module) {
+
+'use strict';
+
+var l10n = require('util/l10n');
 var canon = require('gcli/canon');
-var l10n = require('gcli/l10n');
 var settings = require('gcli/settings');
 
 /**
  * Record if the user has clicked on 'Got It!'
  */
 var allowSetSettingSpec = {
   name: 'allowSet',
   type: 'boolean',
@@ -9100,42 +9488,42 @@ define("text!gcli/commands/pref_set_chec
  *
  * 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/ui/ffdisplay', ['require', 'exports', 'module' , 'gcli/ui/inputter', 'gcli/ui/completer', 'gcli/ui/tooltip', 'gcli/ui/focus', 'gcli/cli', 'gcli/types/javascript', 'gcli/types/node', 'gcli/types/resource', 'gcli/host', 'gcli/ui/intro', 'gcli/canon'], function(require, exports, module) {
+define('gcli/ui/ffdisplay', ['require', 'exports', 'module' , 'gcli/ui/inputter', 'gcli/ui/completer', 'gcli/ui/tooltip', 'gcli/ui/focus', 'gcli/cli', 'gcli/types/javascript', 'gcli/types/node', 'gcli/types/resource', 'util/host', 'gcli/ui/intro', 'gcli/canon'], function(require, exports, module) {
+
+'use strict';
 
 var Inputter = require('gcli/ui/inputter').Inputter;
 var Completer = require('gcli/ui/completer').Completer;
 var Tooltip = require('gcli/ui/tooltip').Tooltip;
 var FocusManager = require('gcli/ui/focus').FocusManager;
 
 var Requisition = require('gcli/cli').Requisition;
 
 var cli = require('gcli/cli');
 var jstype = require('gcli/types/javascript');
 var nodetype = require('gcli/types/node');
 var resource = require('gcli/types/resource');
-var host = require('gcli/host');
+var host = require('util/host');
 var intro = require('gcli/ui/intro');
 
 var CommandOutputManager = require('gcli/canon').CommandOutputManager;
 
 /**
  * Handy utility to inject the content document (i.e. for the viewed page,
  * not for chrome) into the various components.
  */
 function setContentDocument(document) {
   if (document) {
-    // TODO: this unwrapping smells
-    // jstype.setGlobalObject(unwrap(document.defaultView));
     nodetype.setDocument(document);
     resource.setDocument(document);
   }
   else {
     resource.unsetDocument();
     nodetype.unsetDocument();
     jstype.unsetGlobalObject();
   }
@@ -9359,28 +9747,32 @@ exports.FFDisplay = FFDisplay;
  *
  * 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/ui/inputter', ['require', 'exports', 'module' , 'gcli/util', 'gcli/types', 'gcli/history', 'text!gcli/ui/inputter.css'], function(require, exports, module) {
-
-
-var util = require('gcli/util');
-var KeyEvent = require('gcli/util').KeyEvent;
+define('gcli/ui/inputter', ['require', 'exports', 'module' , 'util/promise', 'util/util', 'gcli/types', 'gcli/history', 'text!gcli/ui/inputter.css'], function(require, exports, module) {
+
+'use strict';
+
+var Promise = require('util/promise');
+var util = require('util/util');
+var KeyEvent = require('util/util').KeyEvent;
 
 var Status = require('gcli/types').Status;
 var History = require('gcli/history').History;
 
 var inputterCss = require('text!gcli/ui/inputter.css');
 
 
+var RESOLVED = Promise.resolve(undefined);
+
 /**
  * A wrapper to take care of the functions concerning an input element
  * @param options Object containing user customization properties, including:
  * - scratchpad (default=none)
  * - promptWidth (default=22px)
  * @param components Object that links to other UI components. GCLI provided:
  * - requisition
  * - focusManager
@@ -9425,16 +9817,19 @@ function Inputter(options, components) {
   // Cursor position affects hint severity
   this.onMouseUp = this.onMouseUp.bind(this);
   this.element.addEventListener('mouseup', this.onMouseUp, false);
 
   if (this.focusManager) {
     this.focusManager.addMonitoredElement(this.element, 'input');
   }
 
+  // Start looking like an asynchronous completion isn't happening
+  this._completed = RESOLVED;
+
   this.requisition.onTextChange.add(this.textChanged, this);
 
   this.assignment = this.requisition.getAssignmentAt(0);
   this.onAssignmentChange = util.createEvent('Inputter.onAssignmentChange');
   this.onInputChange = util.createEvent('Inputter.onInputChange');
 
   this.onResize = util.createEvent('Inputter.onResize');
   this.onWindowResize = this.onWindowResize.bind(this);
@@ -9679,17 +10074,17 @@ Inputter.prototype._checkAssignment = fu
  * This function updates the data model. It sets the caret to the end of the
  * input. It does not make any similarity checks so calling this function with
  * it's current value resets the cursor position.
  * It does not execute the input or affect the history.
  * This function should not be called internally, by Inputter and never as a
  * result of a keyboard event on this.element or bug 676520 could be triggered.
  */
 Inputter.prototype.setInput = function(str) {
-  this.requisition.update(str);
+  return this.requisition.update(str);
 };
 
 /**
  * Counterpart to |setInput| for moving the cursor.
  * @param cursor An object shaped like { start: x, end: y }
  */
 Inputter.prototype.setCursor = function(cursor) {
   this._caretChange = Caret.NO_CHANGE;
@@ -9742,76 +10137,87 @@ Inputter.prototype.onKeyDown = function(
       else {
         this.element.blur();
       }
     }
   }
 };
 
 /**
- * The main keyboard processing loop
+ * Handler for use with DOM events, which just calls the promise enabled
+ * handleKeyUp function but checks the exit state of the promise so we know
+ * if something went wrong.
  */
 Inputter.prototype.onKeyUp = function(ev) {
+  this.handleKeyUp(ev).then(null, console.error);
+};
+
+/**
+ * The main keyboard processing loop
+ * @return A promise that resolves (to undefined) when the actions kicked off
+ * by this handler are completed.
+ */
+Inputter.prototype.handleKeyUp = function(ev) {
   if (this.focusManager && ev.keyCode === KeyEvent.DOM_VK_F1) {
     this.focusManager.helpRequest();
-    return;
+    return RESOLVED;
   }
 
   if (this.focusManager && ev.keyCode === KeyEvent.DOM_VK_ESCAPE) {
     this.focusManager.removeHelp();
-    return;
+    return RESOLVED;
   }
 
   if (ev.keyCode === KeyEvent.DOM_VK_UP) {
     if (this.tooltip && this.tooltip.isMenuShowing) {
       this.changeChoice(-1);
     }
     else if (this.element.value === '' || this._scrollingThroughHistory) {
       this._scrollingThroughHistory = true;
-      this.requisition.update(this.history.backward());
+      return 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(this.assignment);
         // See notes on focusManager.onInputChange in onKeyDown
         if (this.focusManager) {
           this.focusManager.onInputChange();
         }
       }
       else {
         this.changeChoice(-1);
       }
     }
-    return;
+    return RESOLVED;
   }
 
   if (ev.keyCode === KeyEvent.DOM_VK_DOWN) {
     if (this.tooltip && this.tooltip.isMenuShowing) {
       this.changeChoice(+1);
     }
     else if (this.element.value === '' || this._scrollingThroughHistory) {
       this._scrollingThroughHistory = true;
-      this.requisition.update(this.history.forward());
+      return this.requisition.update(this.history.forward());
     }
     else {
       // See notes above for the UP key
       if (this.assignment.getStatus() === Status.VALID) {
         this.requisition.decrement(this.assignment);
         // See notes on focusManager.onInputChange in onKeyDown
         if (this.focusManager) {
           this.focusManager.onInputChange();
         }
       }
       else {
         this.changeChoice(+1);
       }
     }
-    return;
+    return RESOLVED;
   }
 
   // RETURN checks status and might exec
   if (ev.keyCode === KeyEvent.DOM_VK_RETURN) {
     var worst = this.requisition.getStatus();
     // Deny RETURN unless the command might work
     if (worst === Status.VALID) {
       this._scrollingThroughHistory = false;
@@ -9822,64 +10228,74 @@ Inputter.prototype.onKeyUp = function(ev
       // If we can't execute the command, but there is a menu choice to use
       // then use it.
       if (!this.tooltip.selectChoice()) {
         this.focusManager.setError(true);
       }
     }
 
     this._choice = null;
-    return;
+    return RESOLVED;
   }
 
   if (ev.keyCode === KeyEvent.DOM_VK_TAB && !ev.shiftKey) {
     // Being able to complete 'nothing' is OK if there is some context, but
-    // when there is nothing on the command line it jsut looks bizarre.
+    // when there is nothing on the command line it just looks bizarre.
     var hasContents = (this.element.value.length > 0);
+
     // If the TAB keypress took the cursor from another field to this one,
     // then they get the keydown/keypress, and we get the keyup. In this
     // case we don't want to do any completion.
     // If the time of the keydown/keypress of TAB was close (i.e. within
     // 1 second) to the time of the keyup then we assume that we got them
     // both, and do the completion.
     if (hasContents && this.lastTabDownAt + 1000 > ev.timeStamp) {
       // It's possible for TAB to not change the input, in which case the
       // textChanged event will not fire, and the caret move will not be
       // processed. So we check that this is done first
       this._caretChange = Caret.TO_ARG_END;
       var inputState = this.getInputState();
       this._processCaretChange(inputState);
+
       if (this._choice == null) {
         this._choice = 0;
       }
-      this.requisition.complete(inputState.cursor, this._choice);
+
+      // The changes made by complete may happen asynchronously, so after the
+      // the call to complete() we should avoid making changes before the end
+      // of the event loop
+      this._completed = this.requisition.complete(inputState.cursor,
+                                                  this._choice);
     }
     this.lastTabDownAt = 0;
     this._scrollingThroughHistory = false;
 
-    this._choice = null;
-    this.onChoiceChange({ choice: this._choice });
-    return;
+    return this._completed.then(function() {
+      this._choice = null;
+      this.onChoiceChange({ choice: this._choice });
+    }.bind(this));
   }
 
   // Give the scratchpad (if enabled) a chance to activate
   if (this.scratchpad && this.scratchpad.shouldActivate(ev)) {
     if (this.scratchpad.activate(this.element.value)) {
-      this.requisition.update('');
-    }
-    return;
+      return this.requisition.update('');
+    }
+    return RESOLVED;
   }
 
   this._scrollingThroughHistory = false;
   this._caretChange = Caret.NO_CHANGE;
 
-  this.requisition.update(this.element.value);
-
-  this._choice = null;
-  this.onChoiceChange({ choice: this._choice });
+  this._completed = this.requisition.update(this.element.value);
+
+  return this._completed.then(function() {
+    this._choice = null;
+    this.onChoiceChange({ choice: this._choice });
+  }.bind(this));
 };
 
 /**
  * Used by onKeyUp for UP/DOWN to change the current choice from an options
  * menu.
  */
 Inputter.prototype.changeChoice = function(amount) {
   if (this._choice == null) {
@@ -9912,17 +10328,16 @@ Inputter.prototype.getInputState = funct
       end: this.element.selectionEnd
     }
   };
 
   // Workaround for potential XUL bug 676520 where textbox gives incorrect
   // values for its content
   if (input.typed == null) {
     input = { typed: '', cursor: { start: 0, end: 0 } };
-    console.log('fixing input.typed=""', input);
   }
 
   // Workaround for a Bug 717268 (which is really a jsdom bug)
   if (input.cursor.start == null) {
     input.cursor.start = 0;
   }
 
   return input;
@@ -9945,16 +10360,18 @@ exports.Inputter = Inputter;
  * 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/history', ['require', 'exports', 'module' ], function(require, exports, module) {
 
+'use strict';
+
 /**
  * A History object remembers commands that have been entered in the past and
  * provides an API for accessing them again.
  * See Bug 681340: Search through history (like C-r in bash)?
  */
 function History() {
   // This is the actual buffer where previous commands are kept.
   // 'this._buffer[0]' should always be equal the empty string. This is so
@@ -10018,21 +10435,23 @@ define("text!gcli/ui/inputter.css", [], 
  *
  * 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/ui/completer', ['require', 'exports', 'module' , 'gcli/util', 'gcli/ui/domtemplate', 'text!gcli/ui/completer.html'], function(require, exports, module) {
-
-
-var util = require('gcli/util');
-var domtemplate = require('gcli/ui/domtemplate');
+define('gcli/ui/completer', ['require', 'exports', 'module' , 'util/promise', 'util/util', 'util/domtemplate', 'text!gcli/ui/completer.html'], function(require, exports, module) {
+
+'use strict';
+
+var Promise = require('util/promise');
+var util = require('util/util');
+var domtemplate = require('util/domtemplate');
 
 var completerHtml = require('text!gcli/ui/completer.html');
 
 /**
  * Completer is an 'input-like' element that sits  an input element annotating
  * it with visual goodness.
  * @param options Object containing user customization properties, including:
  * - scratchpad (default=none) A way to move JS content to custom JS editor
@@ -10122,156 +10541,193 @@ Completer.prototype.update = function(ev
     this.element.appendChild(template.firstChild);
   }
 };
 
 /**
  * Calculate the properties required by the template process for completer.html
  */
 Completer.prototype._getCompleterTemplateData = function() {
+  // Some of the data created by this function can be calculated synchronously
+  // but other parts depend on predictions which are asynchronous.
+  var promisedDirectTabText = Promise.defer();
+  var promisedArrowTabText = Promise.defer();
+  var promisedEmptyParameters = Promise.defer();
+
   var input = this.inputter.getInputState();
-
-  // directTabText is for when the current input is a prefix of the completion
-  // arrowTabText is for when we need to use an -> to show what will be used
-  var directTabText = '';
-  var arrowTabText = '';
   var current = this.requisition.getAssignmentAt(input.cursor.start);
-  var emptyParameters = [];
+  var predictionPromise = undefined;
 
   if (input.typed.trim().length !== 0) {
-    var cArg = current.arg;
-    var prediction = current.getPredictionAt(this.choice);
-
-    if (prediction) {
-      var tabText = prediction.name;
-      var existing = cArg.text;
-
-      // Normally the cursor being just before whitespace means that you are
-      // 'in' the previous argument, which means that the prediction is based
-      // on that argument, however NamedArguments break this by having 2 parts
-      // so we need to prepend the tabText with a space for NamedArguments,
-      // but only when there isn't already a space at the end of the prefix
-      // (i.e. ' --name' not ' --name ')
-      if (current.isInName()) {
-        tabText = ' ' + tabText;
-      }
-
-      if (existing !== tabText) {
-        // Decide to use directTabText or arrowTabText
-        // Strip any leading whitespace from the user inputted value because the
-        // tabText will never have leading whitespace.
-        var inputValue = existing.replace(/^\s*/, '');
-        var isStrictCompletion = tabText.indexOf(inputValue) === 0;
-        if (isStrictCompletion && input.cursor.start === input.typed.length) {
-          // Display the suffix of the prediction as the completion
-          var numLeadingSpaces = existing.match(/^(\s*)/)[0].length;
-
-          directTabText = tabText.slice(existing.length - numLeadingSpaces);
+    predictionPromise = current.getPredictionAt(this.choice);
+  }
+
+  Promise.resolve(predictionPromise).then(function(prediction) {
+    // directTabText is for when the current input is a prefix of the completion
+    // arrowTabText is for when we need to use an -> to show what will be used
+    var directTabText = '';
+    var arrowTabText = '';
+    var emptyParameters = [];
+
+    if (input.typed.trim().length !== 0) {
+      var cArg = current.arg;
+
+      if (prediction) {
+        var tabText = prediction.name;
+        var existing = cArg.text;
+
+        // Normally the cursor being just before whitespace means that you are
+        // 'in' the previous argument, which means that the prediction is based
+        // on that argument, however NamedArguments break this by having 2 parts
+        // so we need to prepend the tabText with a space for NamedArguments,
+        // but only when there isn't already a space at the end of the prefix
+        // (i.e. ' --name' not ' --name ')
+        if (current.isInName()) {
+          tabText = ' ' + tabText;
+        }
+
+        if (existing !== tabText) {
+          // Decide to use directTabText or arrowTabText
+          // Strip any leading whitespace from the user inputted value because
+          // the tabText will never have leading whitespace.
+          var inputValue = existing.replace(/^\s*/, '');
+          var isStrictCompletion = tabText.indexOf(inputValue) === 0;
+          if (isStrictCompletion && input.cursor.start === input.typed.length) {
+            // Display the suffix of the prediction as the completion
+            var numLeadingSpaces = existing.match(/^(\s*)/)[0].length;
+
+            directTabText = tabText.slice(existing.length - numLeadingSpaces);
+          }
+          else {
+            // Display the '-> prediction' at the end of the completer element
+            // \u21E5 is the JS escape right arrow
+            arrowTabText = '\u21E5 ' + tabText;
+          }
+        }
+      }
+      else {
+        // There's no prediction, but if this is a named argument that needs a
+        // value (that is without any) then we need to show that one is needed
+        // For example 'git commit --message ', clearly needs some more text
+        if (cArg.type === 'NamedArgument' && cArg.text === '') {
+          emptyParameters.push('<' + current.param.type.name + '>\u00a0');
         }
-        else {
-          // Display the '-> prediction' at the end of the completer element
-          // \u21E5 is the JS escape right arrow
-          arrowTabText = '\u21E5 ' + tabText;
+      }
+    }
+
+    // Add a space between the typed text (+ directTabText) and the hints,
+    // making sure we don't add 2 sets of padding
+    if (directTabText !== '') {
+      directTabText += '\u00a0';
+    }
+    else if (!this.requisition.typedEndsWithSeparator()) {
+      emptyParameters.unshift('\u00a0');
+    }
+
+    // Calculate the list of parameters to be filled in
+    // We generate an array of emptyParameter markers for each positional
+    // parameter to the current command.
+    // Generally each emptyParameter marker begins with a space to separate it
+    // from whatever came before, unless what comes before ends in a space.
+
+    this.requisition.getAssignments().forEach(function(assignment) {
+      // Named arguments are handled with a group [options] marker
+      if (!assignment.param.isPositionalAllowed) {
+        return;
+      }
+
+      // No hints if we've got content for this parameter
+      if (assignment.arg.toString().trim() !== '') {
+        return;
+      }
+
+      if (directTabText !== '' && current === assignment) {
+        return;
+      }
+
+      var text = (assignment.param.isDataRequired) ?
+          '<' + assignment.param.name + '>\u00a0' :
+          '[' + assignment.param.name + ']\u00a0';
+
+      emptyParameters.push(text);
+    }.bind(this));
+
+    var command = this.requisition.commandAssignment.value;
+    var addOptionsMarker = false;
+
+    // We add an '[options]' marker when there are named parameters that are
+    // not filled in and not hidden, and we don't have any directTabText
+    if (command && command.hasNamedParameters) {
+      command.params.forEach(function(param) {
+        var arg = this.requisition.getAssignment(param.name).arg;
+        if (!param.isPositionalAllowed && !param.hidden
+                && arg.type === "BlankArgument") {
+          addOptionsMarker = true;
         }
-      }
-    }
-    else {
-      // There's no prediction, but if this is a named argument that needs a
-      // value (that is without any) then we need to show that one is needed
-      // For example 'git commit --message ', clearly needs some more text
-      if (cArg.type === 'NamedArgument' && cArg.text === '') {
-        emptyParameters.push('<' + current.param.type.name + '>\u00a0');
-      }
-    }
-  }
-
-  // Add a space between the typed text (+ directTabText) and the hints,
-  // making sure we don't add 2 sets of padding
-  if (directTabText !== '') {
-    directTabText += '\u00a0';
-  }
-  else if (!this.requisition.typedEndsWithSeparator()) {
-    emptyParameters.unshift('\u00a0');
-  }
-
+      }, this);
+    }
+
+    if (addOptionsMarker) {
+      // Add an nbsp if we don't have one at the end of the input or if
+      // this isn't the first param we've mentioned
+      emptyParameters.push('[options]\u00a0');
+    }
+
+    promisedDirectTabText.resolve(directTabText);
+    promisedArrowTabText.resolve(arrowTabText);
+    promisedEmptyParameters.resolve(emptyParameters);
+  }.bind(this), console.error);
+
+  return {
+    statusMarkup: this._getStatusMarkup(input),
+    unclosedJs: this._getUnclosedJs(),
+    scratchLink: this._getScratchLink(),
+    directTabText: promisedDirectTabText.promise,
+    arrowTabText: promisedArrowTabText.promise,
+    emptyParameters: promisedEmptyParameters.promise
+  };
+};
+
+/**
+ * Calculate the statusMarkup required to show wavy lines underneath the input
+ * text (like that of an inline spell-checker) which used by the template
+ * process for completer.html
+ */
+Completer.prototype._getStatusMarkup = function(input) {
   // statusMarkup is wrapper around requisition.getInputStatusMarkup converting
   // space to &nbsp; in the string member (for HTML display) and status to an
   // appropriate class name (i.e. lower cased, prefixed with gcli-in-)
   var statusMarkup = this.requisition.getInputStatusMarkup(input.cursor.start);
+
   statusMarkup.forEach(function(member) {
     member.string = member.string.replace(/ /g, '\u00a0'); // i.e. &nbsp;
     member.className = 'gcli-in-' + member.status.toString().toLowerCase();
   }, this);
 
-  // Calculate the list of parameters to be filled in
-  // We generate an array of emptyParameter markers for each positional
-  // parameter to the current command.
-  // Generally each emptyParameter marker begins with a space to separate it
-  // from whatever came before, unless what comes before ends in a space.
-
+  return statusMarkup;
+};
+
+/**
+ * Is the entered command a JS command with no closing '}'?
+ */
+Completer.prototype._getUnclosedJs = function() {
+  // TWEAK: This code should be considered for promotion to Requisition
   var command = this.requisition.commandAssignment.value;
-  var jsCommand = command && command.name === '{';
-
-  this.requisition.getAssignments().forEach(function(assignment) {
-    // Named arguments are handled with a group [options] marker
-    if (!assignment.param.isPositionalAllowed) {
-      return;
-    }
-
-    // No hints if we've got content for this parameter
-    if (assignment.arg.toString().trim() !== '') {
-      return;
-    }
-
-    if (directTabText !== '' && current === assignment) {
-      return;
-    }
-
-    var text = (assignment.param.isDataRequired) ?
-        '<' + assignment.param.name + '>\u00a0' :
-        '[' + assignment.param.name + ']\u00a0';
-
-    emptyParameters.push(text);
-  }.bind(this));
-
-  var addOptionsMarker = false;
-  // We add an '[options]' marker when there are named parameters that are
-  // not filled in and not hidden, and we don't have any directTabText
-  if (command && command.hasNamedParameters) {
-    command.params.forEach(function(param) {
-      var arg = this.requisition.getAssignment(param.name).arg;
-      if (!param.isPositionalAllowed && !param.hidden
-              && arg.type === "BlankArgument") {
-        addOptionsMarker = true;
-      }
-    }, this);
-  }
-
-  if (addOptionsMarker) {
-    // Add an nbsp if we don't have one at the end of the input or if
-    // this isn't the first param we've mentioned
-    emptyParameters.push('[options]\u00a0');
-  }
-
-  // Is the entered command a JS command with no closing '}'?
-  // TWEAK: This code should be considered for promotion to Requisition
-  var unclosedJs = jsCommand &&
+  return command && command.name === '{' &&
       this.requisition.getAssignment(0).arg.suffix.indexOf('}') === -1;
-
-  // The text for the 'jump to scratchpad' feature, or '' if it is disabled
-  var link = this.scratchpad && jsCommand ? this.scratchpad.linkText : '';
-
-  return {
-    statusMarkup: statusMarkup,
-    directTabText: directTabText,
-    emptyParameters: emptyParameters,
-    arrowTabText: arrowTabText,
-    unclosedJs: unclosedJs,
-    scratchLink: link
-  };
+};
+
+/**
+ * The text for the 'jump to scratchpad' feature, or '' if it is disabled
+ */
+Completer.prototype._getScratchLink = function() {
+  var command = this.requisition.commandAssignment.value;
+  return this.scratchpad && command && command.name === '{' ?
+      this.scratchpad.linkText :
+      '';
 };
 
 exports.Completer = Completer;
 
 
 });
 define("text!gcli/ui/completer.html", [], "\n" +
   "<description\n" +
@@ -10297,24 +10753,25 @@ define("text!gcli/ui/completer.html", []
  *
  * 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/ui/tooltip', ['require', 'exports', 'module' , 'gcli/util', 'gcli/cli', 'gcli/ui/fields', 'gcli/ui/domtemplate', 'text!gcli/ui/tooltip.css', 'text!gcli/ui/tooltip.html'], function(require, exports, module) {
-
-
-var util = require('gcli/util');
+define('gcli/ui/tooltip', ['require', 'exports', 'module' , 'util/util', 'util/domtemplate', 'gcli/cli', 'gcli/ui/fields', 'text!gcli/ui/tooltip.css', 'text!gcli/ui/tooltip.html'], function(require, exports, module) {
+
+'use strict';
+
+var util = require('util/util');
+var domtemplate = require('util/domtemplate');
+
 var CommandAssignment = require('gcli/cli').CommandAssignment;
-
 var fields = require('gcli/ui/fields');
-var domtemplate = require('gcli/ui/domtemplate');
 
 var tooltipCss = require('text!gcli/ui/tooltip.css');
 var tooltipHtml = require('text!gcli/ui/tooltip.html');
 
 
 /**
  * A widget to display an inline dialog which allows the user to fill out
  * the arguments to a command.
@@ -10458,18 +10915,20 @@ Tooltip.prototype.assignmentChanged = fu
   this._updatePosition();
 };
 
 /**
  * Forward the event to the current field
  */
 Tooltip.prototype.choiceChanged = function(ev) {
   if (this.field && this.field.setChoiceIndex) {
-    var choice = this.assignment.conversion.constrainPredictionIndex(ev.choice);
-    this.field.setChoiceIndex(choice);
+    var conversion = this.assignment.conversion;
+    conversion.constrainPredictionIndex(ev.choice).then(function(choice) {
+      this.field.setChoiceIndex(choice);
+    }.bind(this)).then(null, console.error);
   }
 };
 
 /**
  * Allow the inputter to use RETURN to chose the current menu item when
  * it can't execute the command line
  * @return true if there was a selection to use, false otherwise
  */
@@ -10479,18 +10938,18 @@ Tooltip.prototype.selectChoice = functio
   }
   return false;
 };
 
 /**
  * Called by the onFieldChange event on the current Field
  */
 Tooltip.prototype.fieldChanged = function(ev) {
-  var options = { argUpdate: true, matchPadding: true };
-  this.requisition.setAssignment(this.assignment, ev.conversion.arg, options);
+  this.requisition.setAssignment(this.assignment, ev.conversion.arg,
+                                 { matchPadding: true });
 
   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/Makefile.in
+++ b/browser/devtools/commandline/test/Makefile.in
@@ -8,31 +8,28 @@ topsrcdir = @top_srcdir@
 srcdir    = @srcdir@
 VPATH     = @srcdir@
 relativesrcdir  = @relativesrcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 MOCHITEST_BROWSER_FILES = \
   browser_cmd_addon.js \
-  browser_cmd_calllog.js \
-  browser_cmd_calllog_chrome.js \
-  browser_dbg_cmd_break.html \
-  browser_dbg_cmd_break.js \
+  $(browser_cmd_calllog.js disabled until bug 845831 is fixed) \
+  $(browser_cmd_calllog_chrome.js disabled until bug 845831 is fixed) \
   browser_cmd_commands.js \
   browser_cmd_cookie.js \
-  browser_cmd_integrate.js \
   browser_cmd_jsb.js \
   browser_cmd_jsb_script.jsi \
   browser_cmd_pagemod_export.html \
   browser_cmd_pagemod_export.js \
   browser_cmd_pref.js \
   browser_cmd_restart.js \
   browser_cmd_screenshot.html \
-  browser_cmd_screenshot_perwindowpb.js \
+  browser_cmd_screenshot.js \
   browser_cmd_settings.js \
   browser_gcli_canon.js \
   browser_gcli_cli.js \
   browser_gcli_completion.js \
   browser_gcli_exec.js \
   browser_gcli_focus.js \
   browser_gcli_history.js \
   browser_gcli_incomplete.js \
@@ -47,22 +44,12 @@ MOCHITEST_BROWSER_FILES = \
   browser_gcli_spell.js \
   browser_gcli_split.js \
   browser_gcli_tokenize.js \
   browser_gcli_tooltip.js \
   browser_gcli_types.js \
   browser_gcli_util.js \
   head.js \
   helpers.js \
-  helpers_perwindowpb.js \
   mockCommands.js \
   $(NULL)
 
-ifneq ($(OS_ARCH),WINNT)
-MOCHITEST_BROWSER_FILES += \
-  browser_dbg_cmd.html \
-  browser_dbg_cmd.js \
-  $(NULL)
-else
-$(filter disabled-temporarily--bug-820221, browser_dbg_cmd.js)
-endif
-
 include $(topsrcdir)/config/rules.mk
--- a/browser/devtools/commandline/test/browser_cmd_addon.js
+++ b/browser/devtools/commandline/test/browser_cmd_addon.js
@@ -1,108 +1,136 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // Tests that the addon commands works as they should
 
-let imported = {};
-Components.utils.import("resource:///modules/devtools/BuiltinCommands.jsm", imported);
+let CmdAddonFlags = (Cu.import("resource:///modules/devtools/BuiltinCommands.jsm", {})).CmdAddonFlags;
+
+let tests = {};
 
 function test() {
-  DeveloperToolbarTest.test("about:blank", [ GAT_test ]);
+  helpers.addTabWithToolbar("about:blank", function(options) {
+    return helpers.runTests(options, tests);
+  }).then(finish);
 }
 
-function GAT_test() {
-  var GAT_ready = DeveloperToolbarTest.checkCalled(function() {
-    Services.obs.removeObserver(GAT_ready, "gcli_addon_commands_ready", false);
+tests.gatTest = function(options) {
+  let deferred = Promise.defer();
+
+  // hack to reduce stack size as a result of bug 842347
+  let onGatReadyInterjection = function() {
+    executeSoon(onGatReady);
+  };
+
+  let onGatReady = function() {
+    Services.obs.removeObserver(onGatReadyInterjection, "gcli_addon_commands_ready", false);
     info("gcli_addon_commands_ready notification received, running tests");
 
-    helpers.setInput('addon list dictionary');
-    helpers.check({
-      input:  'addon list dictionary',
-      hints:                       '',
-      markup: 'VVVVVVVVVVVVVVVVVVVVV',
-      status: 'VALID'
-    });
-
-    helpers.setInput('addon list extension');
-    helpers.check({
-      input:  'addon list extension',
-      hints:                      '',
-      markup: 'VVVVVVVVVVVVVVVVVVVV',
-      status: 'VALID'
-    });
-
-    helpers.setInput('addon list locale');
-    helpers.check({
-      input:  'addon list locale',
-      hints:                   '',
-      markup: 'VVVVVVVVVVVVVVVVV',
-      status: 'VALID'
-    });
-
-    helpers.setInput('addon list plugin');
-    helpers.check({
-      input:  'addon list plugin',
-      hints:                   '',
-      markup: 'VVVVVVVVVVVVVVVVV',
-      status: 'VALID'
-    });
-
-    helpers.setInput('addon list theme');
-    helpers.check({
-      input:  'addon list theme',
-      hints:                  '',
-      markup: 'VVVVVVVVVVVVVVVV',
-      status: 'VALID'
-    });
+    let auditDone = helpers.audit(options, [
+      {
+        setup: 'addon list dictionary',
+        check: {
+          input:  'addon list dictionary',
+          hints:                       '',
+          markup: 'VVVVVVVVVVVVVVVVVVVVV',
+          status: 'VALID'
+        }
+      },
+      {
+        setup: 'addon list extension',
+        check: {
+          input:  'addon list extension',
+          hints:                      '',
+          markup: 'VVVVVVVVVVVVVVVVVVVV',
+          status: 'VALID'
+        }
+      },
+      {
+        setup: 'addon list locale',
+        check: {
+          input:  'addon list locale',
+          hints:                   '',
+          markup: 'VVVVVVVVVVVVVVVVV',
+          status: 'VALID'
+        }
+      },
+      {
+        setup: 'addon list plugin',
+        check: {
+          input:  'addon list plugin',
+          hints:                   '',
+          markup: 'VVVVVVVVVVVVVVVVV',
+          status: 'VALID'
+        }
+      },
+      {
+        setup: 'addon list theme',
+        check: {
+          input:  'addon list theme',
+          hints:                  '',
+          markup: 'VVVVVVVVVVVVVVVV',
+          status: 'VALID'
+        }
+      },
+      {
+        setup: 'addon list all',
+        check: {
+          input:  'addon list all',
+          hints:                '',
+          markup: 'VVVVVVVVVVVVVV',
+          status: 'VALID'
+        }
+      },
+      {
+        setup: 'addon disable Test_Plug-in_1.0.0.0',
+        check: {
+          input:  'addon disable Test_Plug-in_1.0.0.0',
+          hints:                                    '',
+          markup: 'VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV',
+          status: 'VALID'
+        }
+      },
+      {
+        setup: 'addon disable WRONG',
+        check: {
+          input:  'addon disable WRONG',
+          hints:                     '',
+          markup: 'VVVVVVVVVVVVVVEEEEE',
+          status: 'ERROR'
+        }
+      },
+      {
+        setup: 'addon enable Test_Plug-in_1.0.0.0',
+        check: {
+          input:  'addon enable Test_Plug-in_1.0.0.0',
+          hints:                                   '',
+          markup: 'VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV',
+          status: 'VALID',
+          args: {
+            command: { name: 'addon enable' },
+            name: { value: 'Test Plug-in', status: 'VALID' },
+          }
+        },
+        exec: {