Merge from mozilla-central.
authorDavid Anderson <danderson@mozilla.com>
Mon, 27 Feb 2012 12:40:46 -0800
changeset 105937 c757b4a747a5d92e54403998abe229cbff299a78
parent 105936 c6f122f35328c5fbdca218e7c613666bfef48207 (current diff)
parent 87880 8ea5c983743fbb7d6bcac41acea8f124eaf15e73 (diff)
child 105938 1714eb4edf547a0099c63a1f7e4c145ca7a35526
push id14706
push usereakhgari@mozilla.com
push dateTue, 11 Sep 2012 20:39:52 +0000
treeherdermozilla-inbound@d50bf1edaabe [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone13.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-central.
accessible/src/base/Statistics.h
accessible/src/base/nsARIAMap.cpp
accessible/src/base/nsAccessible.cpp
browser/app/profile/firefox.js
browser/base/content/browser.js
browser/base/content/browser.xul
browser/base/content/highlighter.css
browser/base/content/tabbrowser.xml
browser/components/nsBrowserGlue.js
browser/components/tabview/test/head.js
browser/devtools/highlighter/test/Makefile.in
browser/devtools/highlighter/test/browser_inspector_editor.js
browser/devtools/highlighter/test/browser_inspector_registertools.js
browser/devtools/highlighter/test/browser_inspector_tab_switch.js
browser/devtools/styleinspector/StyleInspector.jsm
browser/devtools/webconsole/HUDService.jsm
browser/devtools/webconsole/test/browser_gcli_web.js
browser/locales/en-US/chrome/browser/browser.dtd
browser/locales/en-US/chrome/browser/devtools/inspector.properties
browser/themes/pinstripe/livemark-item.png
browser/themes/winstripe/livemark-item-aero.png
browser/themes/winstripe/livemark-item.png
config/autoconf.mk.in
configure.in
content/base/public/nsINode.h
content/base/src/nsDOMTokenList.cpp
content/base/src/nsDocument.cpp
content/base/src/nsDocument.h
content/base/src/nsEventSource.cpp
content/base/src/nsGenericElement.cpp
content/base/src/nsNodeUtils.cpp
content/base/test/Makefile.in
content/canvas/src/CustomQS_Canvas2D.h
content/canvas/src/WebGLContext.cpp
content/html/content/src/nsGenericHTMLElement.cpp
content/html/content/src/nsHTMLCanvasElement.cpp
content/html/content/src/nsHTMLMediaElement.cpp
content/html/content/src/nsHTMLOptionElement.cpp
content/html/content/src/nsHTMLSelectElement.cpp
content/html/content/src/nsHTMLSelectElement.h
content/media/MediaResource.cpp
content/smil/nsSMILTimedElement.cpp
content/xul/templates/src/nsXULTemplateBuilder.cpp
docshell/base/nsDocShell.cpp
dom/base/nsDOMClassInfo.cpp
dom/base/nsJSEnvironment.cpp
dom/indexedDB/IDBIndex.cpp
dom/indexedDB/IDBObjectStore.cpp
dom/interfaces/canvas/nsIDOMWebGLRenderingContext.idl
dom/interfaces/css/nsIDOMCSS2Properties.idl
dom/plugins/base/nsJSNPRuntime.cpp
dom/workers/Events.cpp
dom/workers/File.cpp
dom/workers/WorkerPrivate.cpp
gfx/angle/README.mozilla
gfx/angle/src/libGLESv2/Context.cpp
gfx/angle/src/libGLESv2/Context.h
gfx/angle/src/libGLESv2/libGLESv2.cpp
gfx/thebes/gfxPlatform.h
gfx/thebes/gfxPlatformFontList.cpp
gfx/thebes/gfxPlatformFontList.h
gfx/thebes/gfxRect.h
js/jsd/jsd_val.c
js/src/Makefile.in
js/src/MemoryMetrics.cpp
js/src/configure.in
js/src/frontend/BytecodeEmitter.cpp
js/src/frontend/FoldConstants.cpp
js/src/frontend/ParseNode.h
js/src/gc/Barrier.h
js/src/ion/CompileInfo.h
js/src/jit-test/tests/jaeger/bug563000/eif-trap-newvar.js
js/src/jit-test/tests/jaeger/bug563000/eif-trap.js
js/src/jit-test/tests/jaeger/bug563000/trap-from-add-ool.js
js/src/jit-test/tests/jaeger/bug563000/trap-parent.js
js/src/jit-test/tests/jaeger/bug563000/trap-self-as-parent.js
js/src/jit-test/tests/jaeger/bug563000/trap-self-from-trap.js
js/src/jit-test/tests/jaeger/bug563000/trap-self.js
js/src/jsanalyze.cpp
js/src/jsapi.cpp
js/src/jsapi.h
js/src/jsarray.cpp
js/src/jsarray.h
js/src/jsbool.cpp
js/src/jscntxt.cpp
js/src/jscntxt.h
js/src/jscompartment.cpp
js/src/jsdate.cpp
js/src/jsdate.h
js/src/jsgc.cpp
js/src/jsgc.h
js/src/jsinfer.cpp
js/src/jsinterp.cpp
js/src/jsiter.cpp
js/src/jsnum.cpp
js/src/jsnum.h
js/src/jsobj.cpp
js/src/jsobj.h
js/src/jsobjinlines.h
js/src/json.cpp
js/src/jsopcode.cpp
js/src/jsopcode.h
js/src/jsopcode.tbl
js/src/jspubtd.h
js/src/jsscript.cpp
js/src/jsstr.cpp
js/src/jstypedarray.cpp
js/src/jsweakmap.cpp
js/src/jsxdrapi.h
js/src/jsxml.cpp
js/src/methodjit/Compiler.cpp
js/src/methodjit/Compiler.h
js/src/methodjit/FastOps.cpp
js/src/methodjit/InvokeHelpers.cpp
js/src/methodjit/LoopState.cpp
js/src/methodjit/MonoIC.cpp
js/src/methodjit/StubCalls.cpp
js/src/methodjit/TypedArrayIC.h
js/src/shell/js.cpp
js/src/vm/Debugger.cpp
js/xpconnect/src/XPCComponents.cpp
js/xpconnect/src/XPCConvert.cpp
js/xpconnect/src/XPCJSRuntime.cpp
js/xpconnect/src/XPCQuickStubs.h
js/xpconnect/src/qsgen.py
js/xpconnect/wrappers/XrayWrapper.cpp
layout/base/FrameLayerBuilder.cpp
layout/base/nsBidiPresUtils.cpp
layout/base/nsCSSRendering.cpp
layout/generic/nsFloatManager.cpp
layout/reftests/canvas/reftest.list
layout/reftests/transform-3d/scale3d-2-ref.html
layout/style/Declaration.cpp
layout/style/nsCSSParser.cpp
layout/style/nsCSSProps.cpp
layout/style/nsCSSProps.h
layout/style/nsComputedDOMStyle.cpp
layout/style/nsDOMCSSDeclaration.cpp
layout/style/nsRuleNode.cpp
layout/style/nsStyleCoord.cpp
layout/style/nsStyleCoord.h
layout/style/nsStyleStruct.cpp
layout/style/nsStyleStruct.h
layout/style/test/property_database.js
mobile/android/base/GeckoApp.java
mobile/android/base/GeckoAppShell.java
mobile/android/base/GeckoDirProvider.java
mobile/android/base/LauncherShortcuts.java.in
mobile/android/base/Makefile.in
mobile/android/base/resources/layout/gecko_menu.xml
mobile/xul/app/mobile.js
modules/libpref/src/init/all.js
mozglue/android/APKOpen.cpp
netwerk/base/src/nsSocketTransportService2.cpp
netwerk/cache/nsDiskCacheDeviceSQL.cpp
netwerk/cache/nsDiskCacheDeviceSQL.h
netwerk/protocol/http/SpdySession.cpp
netwerk/protocol/http/SpdySession.h
netwerk/protocol/http/nsHttpChannel.cpp
netwerk/protocol/http/nsHttpConnection.cpp
netwerk/protocol/http/nsHttpConnectionInfo.cpp
netwerk/protocol/http/nsHttpConnectionInfo.h
netwerk/protocol/http/nsHttpConnectionMgr.cpp
netwerk/protocol/http/nsHttpConnectionMgr.h
netwerk/protocol/http/nsHttpHandler.cpp
netwerk/protocol/http/nsHttpHandler.h
parser/html/nsHtml5TreeOpExecutor.cpp
parser/html/nsHtml5TreeOpExecutor.h
security/manager/ssl/src/nsNSSIOLayer.cpp
security/manager/ssl/src/nsNSSIOLayer.h
services/sync/tests/unit/test_bookmark_engine.js
storage/src/mozStorageConnection.cpp
testing/mochitest/runtests.py
testing/xpcshell/xpcshell.ini
toolkit/components/downloads/nsDownloadScanner.h
toolkit/components/places/History.cpp
toolkit/components/places/SQLFunctions.cpp
toolkit/components/places/nsNavBookmarks.cpp
toolkit/components/places/nsNavBookmarks.h
toolkit/components/places/nsNavHistory.cpp
toolkit/components/places/nsNavHistoryQuery.cpp
toolkit/components/places/nsNavHistoryResult.cpp
toolkit/components/places/nsPlacesImportExportService.cpp
toolkit/components/places/nsPlacesImportExportService.h
toolkit/components/places/tests/autocomplete/test_livemarks.js
toolkit/components/places/tests/chrome/test_329534.xul
toolkit/components/places/tests/head_common.js
toolkit/components/places/tests/unit/test_exclude_livemarks.js
toolkit/components/places/tests/unit/test_livemarkService_getLivemarkIdForFeedURI.js
toolkit/components/telemetry/TelemetryHistograms.h
toolkit/components/telemetry/TelemetryPing.js
toolkit/components/telemetry/tests/unit/test_TelemetryPing.js
toolkit/content/widgets/videocontrols.xml
toolkit/mozapps/update/nsUpdateService.js
toolkit/mozapps/update/test/unit/head_update.js.in
toolkit/themes/pinstripe/global/media/videocontrols.css
toolkit/themes/winstripe/global/media/videocontrols.css
uriloader/exthandler/win/nsMIMEInfoWin.cpp
widget/android/AndroidBridge.cpp
widget/android/AndroidBridge.h
widget/android/nsWindow.cpp
widget/xpwidgets/GfxInfoX11.cpp
widget/xpwidgets/GfxInfoX11.h
xpcom/base/nsCycleCollector.cpp
xpcom/ds/nsCheapSets.cpp
xpcom/ds/nsCheapSets.h
xpcom/glue/nsTHashtable.h
xpcom/io/nsILocalFile.idl
xpcom/io/nsLocalFileWin.cpp
--- a/accessible/src/base/Statistics.h
+++ b/accessible/src/base/Statistics.h
@@ -55,31 +55,31 @@ namespace statistics {
 
   /**
    * Report that ISimpleDOM* has been used.
    */
   inline void ISimpleDOMUsed()
   {
     static bool firstTime = true;
     if (firstTime) {
-      Telemetry::Accumulate(Telemetry::ISIMPLE_DOM_USAGE, 1);
+      Telemetry::Accumulate(Telemetry::A11Y_ISIMPLEDOM_USAGE, 1);
       firstTime = false;
     }
   }
 
   /**
    * Report that IAccessibleTable has been used.
    */
   inline void IAccessibleTableUsed()
-    { Telemetry::Accumulate(Telemetry::IACCESSIBLE_TABLE_USAGE, 1); }
+    { Telemetry::Accumulate(Telemetry::A11Y_IATABLE_USAGE, 1); }
 
   /**
    * Report that XForms accessibility has been instantiated.
    */
   inline void XFormsAccessibleUsed()
-    { Telemetry::Accumulate(Telemetry::XFORMS_ACCESSIBLE_USED, 1); }
+    { Telemetry::Accumulate(Telemetry::A11Y_XFORMS_USAGE, 1); }
 
 } // namespace statistics
 } // namespace a11y
 } // namespace mozilla
 
 #endif
 
--- a/accessible/src/base/nsARIAMap.cpp
+++ b/accessible/src/base/nsARIAMap.cpp
@@ -131,17 +131,17 @@ nsRoleMapEntry nsARIAMap::gWAIRoleMap[] 
     kNoReqStates,
     eARIASelectable,
     eARIAReadonly
   },
   {
     "combobox",
     roles::COMBOBOX,
     kUseMapRole,
-    eHasValueMinMax,
+    eNoValue,
     eOpenCloseAction,
     eNoLiveAttr,
     states::COLLAPSED | states::HASPOPUP,
     eARIAAutoComplete,
     eARIAReadonly
   },
   {
     "dialog",
--- a/accessible/src/base/nsAccessible.cpp
+++ b/accessible/src/base/nsAccessible.cpp
@@ -849,19 +849,20 @@ nsAccessible::ChildAtPoint(PRInt32 aX, P
   if (!foundFrame || !(content = foundFrame->GetContent()))
     return fallbackAnswer;
 
   // Get accessible for the node with the point or the first accessible in
   // the DOM parent chain.
   nsDocAccessible* contentDocAcc = GetAccService()->
     GetDocAccessible(content->OwnerDoc());
 
-  // contentDocAcc in some circumstances can be NULL
-  // See https://bugzilla.mozilla.org/show_bug.cgi?id=729861
-  NS_ENSURE_TRUE(contentDocAcc, fallbackAnswer);
+  // contentDocAcc in some circumstances can be NULL. See bug 729861
+  NS_ASSERTION(contentDocAcc, "could not get the document accessible");
+  if (!contentDocAcc)
+    return fallbackAnswer;
 
   nsAccessible* accessible = contentDocAcc->GetAccessibleOrContainer(content);
   if (!accessible)
     return fallbackAnswer;
 
   if (accessible == this) {
     // Manually walk through accessible children and see if the are within this
     // point. Skip offscreen or invisible accessibles. This takes care of cases
--- a/accessible/src/msaa/Compatibility.cpp
+++ b/accessible/src/msaa/Compatibility.cpp
@@ -112,16 +112,26 @@ Compatibility::Init()
     statistics::A11yConsumers(SEROTEK);
 
   if (::GetModuleHandleW(L"nvdaHelperRemote"))
     statistics::A11yConsumers(NVDA);
 
   if (::GetModuleHandleW(L"OsmHooks"))
     statistics::A11yConsumers(COBRA);
 
+  if (::GetModuleHandleW(L"WebFinderRemote"))
+    statistics::A11yConsumers(ZOOMTEXT);
+
+  if (::GetModuleHandleW(L"Kazahook"))
+    statistics::A11yConsumers(KAZAGURU);
+
+  if (::GetModuleHandleW(L"TextExtractorImpl32") ||
+      ::GetModuleHandleW(L"TextExtractorImpl64"))
+    statistics::A11yConsumers(YOUDAO);
+
   // Turn off new tab switching for Jaws and WE.
   if (sMode & JAWSMode || sMode & WEMode) {
     // Check to see if the pref for disallowing CtrlTab is already set. If so,
     // bail out (respect the user settings). If not, set it.
     if (!Preferences::HasUserValue("browser.ctrlTab.disallowForScreenReaders"))
       Preferences::SetBool("browser.ctrlTab.disallowForScreenReaders", true);
   }
 }
--- a/accessible/src/msaa/Compatibility.h
+++ b/accessible/src/msaa/Compatibility.h
@@ -102,17 +102,20 @@ private:
    */
   enum {
     NVDA = 0,
     JAWS = 1,
     OLDJAWS = 2,
     WE = 3,
     DOLPHIN = 4,
     SEROTEK = 5,
-    COBRA = 6
+    COBRA = 6,
+    ZOOMTEXT = 7,
+    KAZAGURU = 8,
+    YOUDAO = 9
   };
 
 private:
   static PRUint32 sMode;
 };
 
 } // a11y namespace
 } // mozilla namespace
--- a/accessible/tests/mochitest/events/test_focus_general.html
+++ b/accessible/tests/mochitest/events/test_focus_general.html
@@ -101,20 +101,24 @@
         // other platforms requires setting a ui.key.menuAccessKeyFocuses
         // preference.
         gQueue.push(new toggleTopMenu(editableDoc, new topMenuChecker()));
         gQueue.push(new toggleTopMenu(editableDoc, new focusChecker(editableDoc)));
       }
       gQueue.push(new synthContextMenu(editableDoc, new contextMenuChecker()));
       gQueue.push(new synthDownKey(editableDoc, new focusContextMenuItemChecker()));
       gQueue.push(new synthEscapeKey(editableDoc, new focusChecker(editableDoc)));
+      if (SEAMONKEY) {
+        todo(false, "shift tab from editable document fails on (Windows) SeaMonkey! (Bug 718235)");
+      } else {
       if (LINUX)
-        todo(false, "shift tab from editable document Fails on linux!");
+        todo(false, "shift tab from editable document fails on linux!");
       else
         gQueue.push(new synthShiftTab("link", new focusChecker("link")));
+      } // ! SEAMONKEY
 
       gQueue.invoke(); // Will call SimpleTest.finish();
     }
 
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(doTests);
   </script>
 </head>
--- a/b2g/app/b2g.js
+++ b/b2g/app/b2g.js
@@ -50,18 +50,19 @@ pref("dom.telephony.app.phone.url", "htt
 
 // Device pixel to CSS px ratio, in percent. Set to -1 to calculate based on display density.
 pref("browser.viewport.scaleRatio", -1);
 
 /* disable text selection */
 pref("browser.ignoreNativeFrameTextSelection", true);
 
 /* cache prefs */
-pref("browser.cache.disk.enable", false);
-pref("browser.cache.disk.capacity", 0); // kilobytes
+pref("browser.cache.disk.enable", true);
+pref("browser.cache.disk.capacity", 55000); // kilobytes
+pref("browser.cache.disk.parent_directory", "/cache");
 pref("browser.cache.disk.smart_size.enabled", false);
 pref("browser.cache.disk.smart_size.first_run", false);
 
 pref("browser.cache.memory.enable", true);
 pref("browser.cache.memory.capacity", 1024); // kilobytes
 
 /* image cache prefs */
 pref("image.cache.size", 1048576); // bytes
--- a/b2g/chrome/content/shell.xul
+++ b/b2g/chrome/content/shell.xul
@@ -27,11 +27,11 @@
   <commandset id="mainCommandSet">
     <command id="cmd_close" oncommand="CommandUpdater.doCommand(this.id);"/>
   </commandset>
 
   <browser id="homescreen"
            type="content-primary"
            flex="1"
            style="overflow: hidden;"
-           src="data:text/html;base64,PCFET0NUWVBFIGh0bWw+PGh0bWw+PGJvZHkgc3R5bGU9ImJhY2tncm91bmQ6YmxhY2s7Ij48L2JvZHk+PC9odG1sPgo="/>
+           src="data:text/html,%3C!DOCTYPE html>%3Cbody style='background:black;'>"/>
 </window>
 
--- a/browser/app/blocklist.xml
+++ b/browser/app/blocklist.xml
@@ -1,10 +1,10 @@
 <?xml version="1.0"?>
-<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist" lastupdate="1329176667000">
+<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist" lastupdate="1330033499000">
   <emItems>
       <emItem  blockID="i58" id="webmaster@buzzzzvideos.info">
                         <versionRange  minVersion="0" maxVersion="*">
                     </versionRange>
                   </emItem>
       <emItem  blockID="i41" id="{99079a25-328f-4bd4-be04-00955acaa0a7}">
                         <versionRange  minVersion="0.1" maxVersion="4.3.1.00" severity="1">
                     </versionRange>
@@ -106,27 +106,33 @@
       <emItem  blockID="i53" id="{a3a5c777-f583-4fef-9380-ab4add1bc2a8}">
                         <versionRange  minVersion="2.0.3" maxVersion="2.0.3">
                     </versionRange>
                   </emItem>
       <emItem  blockID="i59" id="ghostviewer@youtube2.com">
                         <versionRange  minVersion="0" maxVersion="*">
                     </versionRange>
                   </emItem>
-      <emItem  blockID="i51" id="admin@youtubeplayer.com">
-                        <versionRange  minVersion="0" maxVersion="*">
+      <emItem  blockID="i70" id="psid-vhvxQHMZBOzUZA@jetpack">
+                        <versionRange  minVersion="0" maxVersion="*" severity="1">
                     </versionRange>
                   </emItem>
+      <emItem  blockID="i7" id="{2224e955-00e9-4613-a844-ce69fccaae91}">
+                        </emItem>
       <emItem  blockID="i46" id="{841468a1-d7f4-4bd3-84e6-bb0f13a06c64}">
                         <versionRange  minVersion="0.1" maxVersion="*">
                       <targetApplication  id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
                               <versionRange  minVersion="9.0a1" maxVersion="9.0" />
                           </targetApplication>
                     </versionRange>
                   </emItem>
+      <emItem  blockID="i67" id="youtube2@youtube2.com">
+                        <versionRange  minVersion="0" maxVersion="*">
+                    </versionRange>
+                  </emItem>
       <emItem  blockID="i60" id="youtb3@youtb3.com">
                         <versionRange  minVersion="0" maxVersion="*">
                     </versionRange>
                   </emItem>
       <emItem  blockID="i23" id="firefox@bandoo.com">
                         <versionRange  minVersion="5.0" maxVersion="5.0" severity="1">
                       <targetApplication  id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
                               <versionRange  minVersion="3.7a1pre" maxVersion="*" />
@@ -156,30 +162,38 @@
                     </versionRange>
                   </emItem>
       <emItem  blockID="i44" id="sigma@labs.mozilla">
                         </emItem>
       <emItem  blockID="i5" id="support@daemon-tools.cc">
                         <versionRange  minVersion=" " maxVersion="1.0.0.5">
                     </versionRange>
                   </emItem>
+      <emItem  blockID="i69" id="{977f3b97-5461-4346-92c8-a14c749b77c9}">
+                        <versionRange  minVersion="0" maxVersion="*" severity="1">
+                    </versionRange>
+                  </emItem>
       <emItem  blockID="i6" id="{3f963a5b-e555-4543-90e2-c3908898db71}">
                         <versionRange  minVersion=" " maxVersion="8.5">
                     </versionRange>
                   </emItem>
       <emItem  blockID="i12" id="masterfiler@gmail.com">
                         <versionRange  severity="3">
                     </versionRange>
                   </emItem>
       <emItem  blockID="i48" id="admin@youtubespeedup.com">
                         </emItem>
       <emItem  blockID="i20" id="{AB2CE124-6272-4b12-94A9-7303C7397BD1}">
                         <versionRange  minVersion="0.1" maxVersion="5.2.0.7164" severity="1">
                     </versionRange>
                   </emItem>
+      <emItem  blockID="i68" id="flashupdate@adobe.com">
+                        <versionRange  minVersion="0" maxVersion="*">
+                    </versionRange>
+                  </emItem>
       <emItem  blockID="i47" id="youtube@youtube2.com">
                         </emItem>
       <emItem  blockID="i62" id="jid0-EcdqvFOgWLKHNJPuqAnawlykCGZ@jetpack">
                         <versionRange  minVersion="0" maxVersion="*">
                     </versionRange>
                   </emItem>
       <emItem  blockID="i17" id="{3252b9ae-c69a-4eaf-9502-dc9c1f6c009e}">
                         <versionRange  minVersion="2.2" maxVersion="2.2">
@@ -199,18 +213,20 @@
       <emItem  blockID="i19" id="{46551EC9-40F0-4e47-8E18-8E5CF550CFB8}">
                         <versionRange  minVersion="1.1b1" maxVersion="1.1b1">
                     </versionRange>
                   </emItem>
       <emItem  blockID="i3" id="langpack-vi-VN@firefox.mozilla.org">
                         <versionRange  minVersion="2.0" maxVersion="2.0">
                     </versionRange>
                   </emItem>
-      <emItem  blockID="i7" id="{2224e955-00e9-4613-a844-ce69fccaae91}">
-                        </emItem>
+      <emItem  blockID="i51" id="admin@youtubeplayer.com">
+                        <versionRange  minVersion="0" maxVersion="*">
+                    </versionRange>
+                  </emItem>
       <emItem  blockID="i52" id="ff-ext@youtube">
                         <versionRange  minVersion="0" maxVersion="*">
                     </versionRange>
                   </emItem>
       <emItem  blockID="i24" id="{6E19037A-12E3-4295-8915-ED48BC341614}">
                         <versionRange  minVersion="0.1" maxVersion="1.3.328.4" severity="1">
                       <targetApplication  id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
                               <versionRange  minVersion="3.7a1pre" maxVersion="*" />
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -794,17 +794,17 @@ pref("browser.sessionstore.max_windows_u
 // number of crashes that can occur before the about:sessionrestore page is displayed
 // (this pref has no effect if more than 6 hours have passed since the last crash)
 pref("browser.sessionstore.max_resumed_crashes", 1);
 // restore_on_demand overrides MAX_CONCURRENT_TAB_RESTORES (sessionstore constant)
 // and restore_hidden_tabs. When true, tabs will not be restored until they are
 // focused (also applies to tabs that aren't visible). When false, the values
 // for MAX_CONCURRENT_TAB_RESTORES and restore_hidden_tabs are respected.
 // Selected tabs are always restored regardless of this pref.
-pref("browser.sessionstore.restore_on_demand", false);
+pref("browser.sessionstore.restore_on_demand", true);
 // Whether to automatically restore hidden tabs (i.e., tabs in other tab groups) or not
 pref("browser.sessionstore.restore_hidden_tabs", false);
 // If restore_on_demand is set, pinned tabs are restored on startup by default.
 // When set to true, this pref overrides that behavior, and pinned tabs will only
 // be restored when they are focused.
 pref("browser.sessionstore.restore_pinned_tabs_on_demand", false);
 
 // allow META refresh by default
@@ -1023,16 +1023,19 @@ pref("services.sync.prefs.sync.xpinstall
 #endif
 
 // Disable the error console
 pref("devtools.errorconsole.enabled", false);
 
 // Enable the Inspector
 pref("devtools.inspector.enabled", true);
 pref("devtools.inspector.htmlHeight", 112);
+pref("devtools.inspector.htmlPanelOpen", false);
+pref("devtools.inspector.sidebarOpen", false);
+pref("devtools.inspector.activeSidebar", "ruleview");
 
 // Enable the Debugger
 pref("devtools.debugger.enabled", false);
 
 // The default Debugger UI height
 pref("devtools.debugger.ui.height", 250);
 
 // Enable the style inspector
--- a/browser/base/content/browser-sets.inc
+++ b/browser/base/content/browser-sets.inc
@@ -20,16 +20,17 @@
 # the Initial Developer. All Rights Reserved.
 #
 # Contributor(s):
 #   Ben Goodger <ben@bengoodger.com> (v2.0)
 #   Blake Ross <blakeross@telocity.com>
 #   Shawn Wilsher <me@shawnwilsher.com>
 #   Ehsan Akhgari <ehsan.akhgari@gmail.com>
 #   Rob Campbell <rcampbell@mozilla.com>
+#   Paul Rouget <paul@mozilla.com>
 #
 # Alternatively, the contents of this file may be used under the terms of
 # either the GNU General Public License Version 2 or later (the "GPL"), or
 # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 # in which case the provisions of the GPL or the LGPL are applicable instead
 # of those above. If you wish to allow use of your version of this file only
 # under the terms of either the GPL or the LGPL, and not to allow others to
 # use your version of this file under the terms of the MPL, indicate your
@@ -148,31 +149,39 @@
 
   <commandset id="inspectorCommands">
     <command id="Inspector:Inspect"
              oncommand="InspectorUI.toggleInspection();"/>
     <command id="Inspector:Sidebar"
              oncommand="InspectorUI.toggleSidebar();"/>
     <command id="Inspector:Tilt"
              oncommand="Tilt.initialize();"/>
+    <command id="Inspector:HTMLPanel"
+             oncommand="InspectorUI.toggleHTMLPanel();"/>
+    <command id="Inspector:CopyInner"
+             oncommand="InspectorUI.copyInnerHTML();"/>
+    <command id="Inspector:CopyOuter"
+             oncommand="InspectorUI.copyOuterHTML();"/>
+    <command id="Inspector:DeleteNode"
+             oncommand="InspectorUI.deleteNode();"/>
   </commandset>
 
   <broadcasterset id="mainBroadcasterSet">
     <broadcaster id="viewBookmarksSidebar" autoCheck="false" label="&bookmarksButton.label;"
                  type="checkbox" group="sidebar" sidebarurl="chrome://browser/content/bookmarks/bookmarksPanel.xul"
                  oncommand="toggleSidebar('viewBookmarksSidebar');"/>
 
     <!-- for both places and non-places, the sidebar lives at
          chrome://browser/content/history/history-panel.xul so there are no
          problems when switching between versions -->
     <broadcaster id="viewHistorySidebar" autoCheck="false" sidebartitle="&historyButton.label;"
                  type="checkbox" group="sidebar"
                  sidebarurl="chrome://browser/content/history/history-panel.xul"
                  oncommand="toggleSidebar('viewHistorySidebar');"/>
-                 
+
     <broadcaster id="viewWebPanelsSidebar" autoCheck="false"
                  type="checkbox" group="sidebar" sidebarurl="chrome://browser/content/web-panels.xul"
                  oncommand="toggleSidebar('viewWebPanelsSidebar');"/>
 
     <!-- popup blocking menu items -->
     <broadcaster id="blockedPopupAllowSite"
                  accesskey="&allowPopups.accesskey;"
                  oncommand="gPopupBlockerObserver.toggleAllowPopupsForSite(event);"/>
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -1648,37 +1648,33 @@ function delayedStartup(isLoadingBlank, 
   }
 
   PlacesToolbarHelper.init();
 
   ctrlTab.readPref();
   gPrefService.addObserver(ctrlTab.prefName, ctrlTab, false);
   gPrefService.addObserver(allTabs.prefName, allTabs, false);
 
-  // Delayed initialization of the livemarks update timer.
-  // Livemark updates don't need to start until after bookmark UI
-  // such as the toolbar has initialized. Starting 5 seconds after
-  // delayedStartup in order to stagger this before the download manager starts.
-  setTimeout(function() PlacesUtils.livemarks.start(), 5000);
-
   // Initialize the download manager some time after the app starts so that
   // auto-resume downloads begin (such as after crashing or quitting with
   // active downloads) and speeds up the first-load of the download manager UI.
   // If the user manually opens the download manager before the timeout, the
   // downloads will start right away, and getting the service again won't hurt.
   setTimeout(function() {
     gDownloadMgr = Cc["@mozilla.org/download-manager;1"].
                    getService(Ci.nsIDownloadManager);
 
+#ifdef XP_WIN
     if (Win7Features) {
       let tempScope = {};
       Cu.import("resource://gre/modules/DownloadTaskbarProgress.jsm",
                 tempScope);
       tempScope.DownloadTaskbarProgress.onBrowserWindowLoad(window);
     }
+#endif
   }, 10000);
 
 #ifndef XP_MACOSX
   updateEditUIVisibility();
   let placesContext = document.getElementById("placesContext");
   placesContext.addEventListener("popupshowing", updateEditUIVisibility, false);
   placesContext.addEventListener("popuphiding", updateEditUIVisibility, false);
 #endif
@@ -3625,22 +3621,22 @@ function FillHistoryMenu(aParent) {
     let entry = sessionHistory.getEntryAtIndex(j, false);
     let uri = entry.URI.spec;
 
     item.setAttribute("uri", uri);
     item.setAttribute("label", entry.title || uri);
     item.setAttribute("index", j);
 
     if (j != index) {
-      try {
-        let iconURL = Cc["@mozilla.org/browser/favicon-service;1"]
-                         .getService(Ci.nsIFaviconService)
-                         .getFaviconForPage(entry.URI).spec;
+      function FHM_getFaviconURLCallback(aURI) {
+        let iconURL = PlacesUtils.favicons.getFaviconLinkForIcon(aURI).spec;
         item.style.listStyleImage = "url(" + iconURL + ")";
-      } catch (ex) {}
+      }
+      PlacesUtils.favicons.getFaviconURLForPage(entry.URI,
+                                                FHM_getFaviconURLCallback);
     }
 
     if (j < index) {
       item.className = "unified-nav-back menuitem-iconic menuitem-with-favicon";
       item.setAttribute("tooltiptext", tooltipBack);
     } else if (j == index) {
       item.setAttribute("type", "radio");
       item.setAttribute("checked", "true");
@@ -6993,21 +6989,23 @@ function getPluginInfo(pluginElement)
     }
   }
 
   return {mimetype: tagMimetype, pluginsPage: pluginsPage};
 }
 
 var gPluginHandler = {
 
+#ifdef MOZ_CRASHREPORTER
   get CrashSubmit() {
     delete this.CrashSubmit;
     Cu.import("resource://gre/modules/CrashSubmit.jsm", this);
     return this.CrashSubmit;
   },
+#endif
 
   // Map the plugin's name to a filtered version more suitable for user UI.
   makeNicePluginName : function (aName, aFilename) {
     if (aName == "Shockwave Flash")
       return "Adobe Flash";
 
     // Clean up the plugin name by stripping off any trailing version numbers
     // or "plugin". EG, "Foo Bar Plugin 1.23_02" --> "Foo Bar"
@@ -7137,24 +7135,26 @@ var gPluginHandler = {
                {plugins: missingPluginsArray, browser: gBrowser.selectedBrowser});
   },
 
   // Callback for user clicking on a disabled plugin
   managePlugins: function (aEvent) {
     BrowserOpenAddonsMgr("addons://list/plugin");
   },
 
+#ifdef MOZ_CRASHREPORTER
   // Callback for user clicking "submit a report" link
   submitReport : function(pluginDumpID, browserDumpID) {
     // The crash reporter wants a DOM element it can append an IFRAME to,
     // which it uses to submit a form. Let's just give it gBrowser.
     this.CrashSubmit.submit(pluginDumpID);
     if (browserDumpID)
       this.CrashSubmit.submit(browserDumpID);
   },
+#endif
 
   // Callback for user clicking a "reload page" link
   reloadPage: function (browser) {
     browser.reload();
   },
 
   // Callback for user clicking the help icon
   openHelpPage: function () {
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -33,16 +33,17 @@
 #   Dão Gottwald <dao@mozilla.com>
 #   Ehsan Akhgari <ehsan.akhgari@gmail.com>
 #   Robert Strong <robert.bugzilla@gmail.com>
 #   Rob Campbell <rcampbell@mozilla.com>
 #   Patrick Walton <pcwalton@mozilla.com>
 #   David Dahl <ddahl@mozilla.com>
 #   Frank Yan <fyan@mozilla.com>
 #   Victor Porof <vporof@mozilla.com>
+#   Paul Rouget <paul@mozilla.com>
 #
 # Alternatively, the contents of this file may be used under the terms of
 # either the GNU General Public License Version 2 or later (the "GPL"), or
 # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 # in which case the provisions of the GPL or the LGPL are applicable instead
 # of those above. If you wish to allow use of your version of this file only
 # under the terms of either the GPL or the LGPL, and not to allow others to
 # use your version of this file under the terms of the MPL, indicate your
@@ -222,30 +223,31 @@
                 class="editBookmarkPanelBottomButton"
                 label="&editBookmark.done.label;"
                 default="true"
                 oncommand="StarUI.panel.hidePopup();"/>
 #endif
       </hbox>
     </panel>
 
-    <panel id="inspector-tree-panel"
-           orient="vertical"
-           hidden="true"
-           ignorekeys="true"
-           noautofocus="true"
-           noautohide="true"
-           titlebar="normal"
-           close="true"
-           label="&inspectPanelTitle.label;">
-      <hbox id="tree-panel-resizer-box" align="end">
-        <spacer flex="1" />
-        <resizer dir="bottomend" />
-      </hbox>
-    </panel>
+    <menupopup id="inspector-node-popup">
+      <menuitem id="inspectorHTMLCopyInner"
+                label="&inspectorHTMLCopyInner.label;"
+                accesskey="&inspectorHTMLCopyInner.accesskey;"
+                command="Inspector:CopyInner"/>
+      <menuitem id="inspectorHTMLCopyOuter"
+                label="&inspectorHTMLCopyOuter.label;"
+                accesskey="&inspectorHTMLCopyOuter.accesskey;"
+                command="Inspector:CopyOuter"/>
+      <menuseparator/>
+      <menuitem id="inspectorHTMLDelete"
+                label="&inspectorHTMLDelete.label;"
+                accesskey="&inspectorHTMLDelete.accesskey;"
+                command="Inspector:DeleteNode"/>
+    </menupopup>
 
     <menupopup id="toolbar-context-menu"
                onpopupshowing="onViewToolbarsPopupShowing(event);">
       <menuseparator/>
       <menuitem command="cmd_ToggleTabsOnTop"
                 type="checkbox"
                 label="&viewTabsOnTop.label;"
                 accesskey="&viewTabsOnTop.accesskey;"/>
@@ -989,55 +991,54 @@
     </hbox>
   </hbox>
 
   <vbox id="browser-bottombox" layer="true">
     <toolbar id="inspector-toolbar"
              class="devtools-toolbar"
              nowindowdrag="true"
              hidden="true">
-      <vbox flex="1">
-        <resizer id="inspector-top-resizer" flex="1" 
-                 dir="top" disabled="true"
-                 element="inspector-tree-box"/>
-        <hbox>
 #ifdef XP_MACOSX
-          <toolbarbutton id="highlighter-closebutton"
-                         oncommand="InspectorUI.closeInspectorUI(false);"
-                         tooltiptext="&inspectCloseButton.tooltiptext;"/>
+      <toolbarbutton id="highlighter-closebutton"
+                     oncommand="InspectorUI.closeInspectorUI(false);"
+                     tooltiptext="&inspectCloseButton.tooltiptext;"/>
 #endif
-          <toolbarbutton id="inspector-inspect-toolbutton"
-                         class="devtools-toolbarbutton"
-                         label="&inspectButton.label;"
-                         accesskey="&inspectButton.accesskey;"
-                         command="Inspector:Inspect"/>
-          <arrowscrollbox id="inspector-breadcrumbs"
-                          flex="1" orient="horizontal"
-                          clicktoscroll="true"/>
-          <hbox id="inspector-tools">
-            <toolbarbutton id="inspector-3D-button"
-                           class="devtools-toolbarbutton"
-                           hidden="true"
-                           label="&inspect3DViewButton.label;"
-                           accesskey="&inspect3DViewButton.accesskey;"
-                           command="Inspector:Tilt"/>
-            <toolbarbutton id="inspector-style-button"
-                           class="devtools-toolbarbutton"
-                           label="&inspectStyleButton.label;"
-                           accesskey="&inspectStyleButton.accesskey;"
-                           command="Inspector:Sidebar"/>
-            <!-- registered tools go here -->
-          </hbox>
+      <toolbarbutton id="inspector-inspect-toolbutton"
+                     class="devtools-toolbarbutton"
+                     label="&inspectButton.label;"
+                     accesskey="&inspectButton.accesskey;"
+                     command="Inspector:Inspect"/>
+      <toolbarbutton id="inspector-treepanel-toolbutton"
+                     class="devtools-toolbarbutton"
+                     label="&htmlPanel.label;"
+                     accesskey="&htmlPanel.accesskey;"
+                     tooltiptext="&htmlPanel.tooltiptext;"
+                     command="Inspector:HTMLPanel"/>
+      <arrowscrollbox id="inspector-breadcrumbs"
+                      flex="1" orient="horizontal"
+                      clicktoscroll="true"/>
+      <hbox id="inspector-tools">
+        <toolbarbutton id="inspector-3D-button"
+                       class="devtools-toolbarbutton"
+                       hidden="true"
+                       label="&inspect3DViewButton.label;"
+                       accesskey="&inspect3DViewButton.accesskey;"
+                       command="Inspector:Tilt"/>
+        <toolbarbutton id="inspector-style-button"
+                       class="devtools-toolbarbutton"
+                       label="&inspectStyleButton.label;"
+                       accesskey="&inspectStyleButton.accesskey;"
+                       command="Inspector:Sidebar"/>
+        <!-- registered tools go here -->
+      </hbox>
 #ifndef XP_MACOSX
-          <toolbarbutton id="highlighter-closebutton"
-                         oncommand="InspectorUI.closeInspectorUI(false);"
-                         tooltiptext="&inspectCloseButton.tooltiptext;"/>
+      <toolbarbutton id="highlighter-closebutton"
+                     oncommand="InspectorUI.closeInspectorUI(false);"
+                     tooltiptext="&inspectCloseButton.tooltiptext;"/>
 #endif
-        </hbox>
-      </vbox>
     </toolbar>
     <toolbar id="addon-bar"
              toolbarname="&addonBarCmd.label;" accesskey="&addonBarCmd.accesskey;"
              collapsed="true"
              class="toolbar-primary chromeclass-toolbar"
              context="toolbar-context-menu" toolboxid="navigator-toolbox"
              mode="icons" iconsize="small" defaulticonsize="small"
              lockiconsize="true"
--- a/browser/base/content/highlighter.css
+++ b/browser/base/content/highlighter.css
@@ -29,24 +29,16 @@
 #highlighter-veil-middlebox:-moz-locale-dir(rtl) {
   -moz-box-direction: reverse;
 }
 
 .inspector-breadcrumbs-button {
   direction: ltr;
 }
 
-#inspector-top-resizer {
-  display: none;
-}
-
-#inspector-toolbar[treepanel-open] > vbox > #inspector-top-resizer {
-  display: -moz-box;
-}
-
 /*
  * Node Infobar
  */
 
 #highlighter-nodeinfobar-container {
   position: absolute;
   max-width: 95%;
 }
--- a/browser/base/content/newtab/grid.js
+++ b/browser/base/content/newtab/grid.js
@@ -18,18 +18,20 @@ let gGrid = {
    * The cached DOM fragment for sites.
    */
   _siteFragment: null,
 
   /**
    * All cells contained in the grid.
    */
   get cells() {
+    let cells = [];
     let children = this.node.querySelectorAll("li");
-    let cells = [new Cell(this, child) for each (child in children)];
+    for (let i = 0; i < children.length; i++)
+      cells.push(new Cell(this, children[i]));
 
     // Replace the getter with our cached value.
     Object.defineProperty(this, "cells", {value: cells, enumerable: true});
 
     return cells;
   },
 
   /**
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -688,18 +688,18 @@
         <body>
           <![CDATA[
             var browser = this.getBrowserForTab(aTab);
             browser.mIconURL = aURI instanceof Ci.nsIURI ? aURI.spec : aURI;
 
             if (aURI && this.mFaviconService) {
               if (!(aURI instanceof Ci.nsIURI))
                 aURI = makeURI(aURI);
-              this.mFaviconService.setAndLoadFaviconForPage(browser.currentURI,
-                                                            aURI, false);
+              this.mFaviconService.setAndFetchFaviconForPage(browser.currentURI,
+                                                             aURI, false);
             }
 
             if ((browser.mIconURL || "") != aTab.getAttribute("image")) {
               if (browser.mIconURL)
                 aTab.setAttribute("image", browser.mIconURL);
               else
                 aTab.removeAttribute("image");
               this._tabAttrModified(aTab);
--- a/browser/base/content/test/Makefile.in
+++ b/browser/base/content/test/Makefile.in
@@ -220,16 +220,17 @@ endif
                  browser_tab_dragdrop.js \
                  browser_tab_dragdrop2.js \
                  browser_tab_dragdrop2_frame1.xul \
                  browser_tabfocus.js \
                  browser_tabs_isActive.js \
                  browser_tabs_owner.js \
                  browser_urlbarCopying.js \
                  browser_urlbarEnter.js \
+                 browser_urlbarRevert.js \
                  browser_urlbarTrimURLs.js \
                  browser_urlHighlight.js \
                  browser_visibleFindSelection.js \
                  browser_visibleTabs.js \
                  browser_visibleTabs_contextMenu.js \
                  browser_visibleTabs_bookmarkAllPages.js \
                  browser_visibleTabs_bookmarkAllTabs.js \
                  browser_visibleTabs_tabPreview.js \
--- a/browser/base/content/test/browser_bug422590.js
+++ b/browser/base/content/test/browser_bug422590.js
@@ -3,18 +3,18 @@ function test() {
   ignoreAllUncaughtExceptions();
   
   // test the main (normal) browser window
   testCustomize(window, testChromeless);
 }
 
 function testChromeless() {
   // test a chromeless window
-  var newWin = openDialog("chrome://browser/content/", "_blank",
-                      "chrome,dialog=no,toolbar=no", "about:blank");
+  var newWin = openDialog(getBrowserURL(), "_blank",
+                          "chrome,dialog=no,toolbar=no", "about:blank");
   ok(newWin, "got new window");
 
   function runWindowTest() {
     // Check that the search bar is hidden
     var searchBar = newWin.BrowserSearch.searchBar;
     ok(searchBar, "got search bar");
 
     var searchBarBO = searchBar.boxObject;
--- a/browser/base/content/test/browser_tabMatchesInAwesomebar.js
+++ b/browser/base/content/test/browser_tabMatchesInAwesomebar.js
@@ -72,34 +72,35 @@ var gTestSteps = [
       loadTab(gBrowser.tabs[i], TEST_URL_BASES[0] + gTabCounter);
   },
   function() {
     info("Running step 4");
     let ps = Services.prefs;
     ps.setBoolPref("browser.privatebrowsing.keep_current_session", true);
     ps.setBoolPref("browser.tabs.warnOnClose", false);
 
+    // Make sure that all restored tabs are loaded without waiting for the user
+    // to bring them to the foreground. We ensure this by resetting the
+    // related preference (see the "firefox.js" defaults file for details).
+    ps.setBoolPref("browser.sessionstore.restore_on_demand", false);
+
     gPrivateBrowsing.privateBrowsingEnabled = true;
 
     executeSoon(function() {
       ensure_opentabs_match_db(nextStep);
     });
   },
   function() {
     info("Running step 5");
     gPrivateBrowsing.privateBrowsingEnabled = false;
 
     executeSoon(function() {
       let ps = Services.prefs;
-      try {
-        ps.clearUserPref("browser.privatebrowsing.keep_current_session");
-      } catch (ex) {}
-      try {
-        ps.clearUserPref("browser.tabs.warnOnClose");
-      } catch (ex) {}
+      ps.clearUserPref("browser.privatebrowsing.keep_current_session");
+      ps.clearUserPref("browser.tabs.warnOnClose");
 
       ensure_opentabs_match_db(nextStep);
     });
   },
   function() {
     info("Running step 6 - ensure we don't register subframes as open pages");
     let tab = gBrowser.addTab();
     tab.linkedBrowser.addEventListener("load", function () {
@@ -164,32 +165,31 @@ var gTestSteps = [
   },
   function() {
     info("Running step 12 - leave private browsing mode");
 
     Services.obs.addObserver(function(aSubject, aTopic, aData) {
       Services.obs.removeObserver(arguments.callee, "private-browsing-transition-complete");
 
       let ps = Services.prefs;
-      try {
-        ps.clearUserPref("browser.privatebrowsing.keep_current_session");
-      } catch (ex) {}
-      try {
-        ps.clearUserPref("browser.tabs.warnOnClose");
-      } catch (ex) {}
+      ps.clearUserPref("browser.privatebrowsing.keep_current_session");
+      ps.clearUserPref("browser.tabs.warnOnClose");
 
       for (let i = 1; i < gBrowser.tabs.length; i++)
         waitForRestoredTab(gBrowser.tabs[i]);
 
     }, "private-browsing-transition-complete", false);
 
     gPrivateBrowsing.privateBrowsingEnabled = false;
   },
   function() {
     info("Running step 13 - close all tabs");
+
+    Services.prefs.clearUserPref("browser.sessionstore.restore_on_demand");
+
     gBrowser.addTab("about:blank", {skipAnimation: true});
     while (gBrowser.tabs.length > 1) {
       info("Removing tab: " + gBrowser.tabs[0].linkedBrowser.currentURI.spec);
       gBrowser.selectTabAtIndex(0);
       gBrowser.removeCurrentTab();
     }
     ensure_opentabs_match_db(nextStep);
   }
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/browser_urlbarRevert.js
@@ -0,0 +1,29 @@
+function test() {
+  waitForExplicitFinish();
+
+  let tab = gBrowser.addTab("http://example.com");
+  gBrowser.selectedTab = tab;
+
+  onLoad(function () {
+    let originalValue = gURLBar.value;
+
+    gBrowser.userTypedValue = "foobar";
+    gBrowser.selectedTab = gBrowser.tabs[0];
+    gBrowser.selectedTab = tab;
+    is(gURLBar.value, "foobar", "location bar displays typed value");
+
+    gURLBar.focus();
+    EventUtils.synthesizeKey("VK_ESCAPE", {});
+    is(gURLBar.value, originalValue, "ESC reverted the location bar value");
+
+    gBrowser.removeTab(tab);
+    finish();
+  });
+}
+
+function onLoad(callback) {
+  gBrowser.selectedBrowser.addEventListener("pageshow", function loadListener() {
+    gBrowser.selectedBrowser.removeEventListener("pageshow", loadListener, false);
+    executeSoon(callback);
+  });
+}
--- a/browser/components/distribution.js
+++ b/browser/components/distribution.js
@@ -40,16 +40,20 @@ EXPORTED_SYMBOLS = [ "DistributionCustom
 const Ci = Components.interfaces;
 const Cc = Components.classes;
 const Cr = Components.results;
 const Cu = Components.utils;
 
 const DISTRIBUTION_CUSTOMIZATION_COMPLETE_TOPIC =
   "distribution-customization-complete";
 
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
+                                  "resource://gre/modules/PlacesUtils.jsm");
+
 function DistributionCustomizer() {
   let dirSvc = Cc["@mozilla.org/file/directory_service;1"].
                getService(Ci.nsIProperties);
   let iniFile = dirSvc.get("XCurProcD", Ci.nsIFile);
   iniFile.append("distribution");
   iniFile.append("distribution.ini");
   if (iniFile.exists())
     this._iniFile = iniFile;
@@ -73,37 +77,16 @@ DistributionCustomizer.prototype = {
     }
     catch (e) {
       locale = "en-US";
     }
     this.__defineGetter__("_locale", function() locale);
     return this._locale;
   },
 
-  get _bmSvc() {
-    let svc = Cc["@mozilla.org/browser/nav-bookmarks-service;1"].
-              getService(Ci.nsINavBookmarksService);
-    this.__defineGetter__("_bmSvc", function() svc);
-    return this._bmSvc;
-  },
-
-  get _annoSvc() {
-    let svc = Cc["@mozilla.org/browser/annotation-service;1"].
-              getService(Ci.nsIAnnotationService);
-    this.__defineGetter__("_annoSvc", function() svc);
-    return this._annoSvc;
-  },
-
-  get _livemarkSvc() {
-    let svc = Cc["@mozilla.org/browser/livemark-service;2"].
-              getService(Ci.nsILivemarkService);
-    this.__defineGetter__("_livemarkSvc", function() svc);
-    return this._livemarkSvc;
-  },
-
   get _prefSvc() {
     let svc = Cc["@mozilla.org/preferences-service;1"].
               getService(Ci.nsIPrefService);
     this.__defineGetter__("_prefSvc", function() svc);
     return this._prefSvc;
   },
 
   get _prefs() {
@@ -162,73 +145,75 @@ DistributionCustomizer.prototype = {
       }
     }
 
     let prependIndex = 0;
     for (let iid = 0; iid <= maxItemId; iid++) {
       if (!items[iid])
         continue;
 
-      let index = this._bmSvc.DEFAULT_INDEX;
+      let index = PlacesUtils.bookmarks.DEFAULT_INDEX;
       let newId;
 
       switch (items[iid]["type"]) {
       case "default":
         break;
 
       case "folder":
         if (iid < defaultItemId)
           index = prependIndex++;
 
-        newId = this._bmSvc.createFolder(parentId, items[iid]["title"], index);
+        newId = PlacesUtils.bookmarks.createFolder(parentId,
+                                                   items[iid]["title"],
+                                                   index);
 
         this._parseBookmarksSection(newId, "BookmarksFolder-" +
                                     items[iid]["folderId"]);
 
         if (items[iid]["description"])
-          this._annoSvc.setItemAnnotation(newId,
-                                          "bookmarkProperties/description",
-                                          items[iid]["description"], 0,
-                                          this._annoSvc.EXPIRE_NEVER);
+          PlacesUtils.annotations.setItemAnnotation(newId,
+                                                    "bookmarkProperties/description",
+                                                    items[iid]["description"], 0,
+                                                    PlacesUtils.annotations.EXPIRE_NEVER);
 
         break;
 
       case "separator":
         if (iid < defaultItemId)
           index = prependIndex++;
-        this._bmSvc.insertSeparator(parentId, index);
+        PlacesUtils.bookmarks.insertSeparator(parentId, index);
         break;
 
       case "livemark":
         if (iid < defaultItemId)
           index = prependIndex++;
 
         // Don't bother updating the livemark contents on creation.
-        newId = this._livemarkSvc.
-          createLivemarkFolderOnly(parentId,
-                                   items[iid]["title"],
-                                   this._makeURI(items[iid]["siteLink"]),
-                                   this._makeURI(items[iid]["feedLink"]),
-                                   index);
+        PlacesUtils.livemarks.addLivemark({ title: items[iid]["title"]
+                                          , parentId: parentId
+                                          , index: index
+                                          , feedURI: this._makeURI(items[iid]["feedLink"])
+                                          , siteURI: this._makeURI(items[iid]["siteLink"])
+                                          });
         break;
 
       case "bookmark":
       default:
         if (iid < defaultItemId)
           index = prependIndex++;
 
-        newId = this._bmSvc.insertBookmark(parentId,
-                                           this._makeURI(items[iid]["link"]),
-                                           index, items[iid]["title"]);
+        newId = PlacesUtils.bookmarks.insertBookmark(parentId,
+                                                     this._makeURI(items[iid]["link"]),
+                                                     index, items[iid]["title"]);
 
         if (items[iid]["description"])
-          this._annoSvc.setItemAnnotation(newId,
-                                          "bookmarkProperties/description",
-                                          items[iid]["description"], 0,
-                                          this._annoSvc.EXPIRE_NEVER);
+          PlacesUtils.annotations.setItemAnnotation(newId,
+                                                    "bookmarkProperties/description",
+                                                    items[iid]["description"], 0,
+                                                    PlacesUtils.annotations.EXPIRE_NEVER);
 
         break;
       }
     }
   },
 
   _customizationsApplied: false,
   applyCustomizations: function DIST_applyCustomizations() {
@@ -272,20 +257,20 @@ DistributionCustomizer.prototype = {
     let bmProcessed = false;
     try {
       bmProcessed = this._prefs.getBoolPref(bmProcessedPref);
     }
     catch (e) {}
 
     if (!bmProcessed) {
       if (sections["BookmarksMenu"])
-        this._parseBookmarksSection(this._bmSvc.bookmarksMenuFolder,
+        this._parseBookmarksSection(PlacesUtils.bookmarksMenuFolderId,
                                     "BookmarksMenu");
       if (sections["BookmarksToolbar"])
-        this._parseBookmarksSection(this._bmSvc.toolbarFolder,
+        this._parseBookmarksSection(PlacesUtils.toolbarFolderId,
                                     "BookmarksToolbar");
       this._prefs.setBoolPref(bmProcessedPref, true);
     }
     return this._checkCustomizationComplete();
   },
 
   _prefDefaultsApplied: false,
   applyPrefDefaults: function DIST_applyPrefDefaults() {
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -772,16 +772,25 @@ BrowserGlue.prototype = {
     const PREF_TELEMETRY_PROMPTED = "toolkit.telemetry.prompted";
     const PREF_TELEMETRY_ENABLED  = "toolkit.telemetry.enabled";
     const PREF_TELEMETRY_REJECTED  = "toolkit.telemetry.rejected";
     const PREF_TELEMETRY_INFOURL  = "toolkit.telemetry.infoURL";
     const PREF_TELEMETRY_SERVER_OWNER = "toolkit.telemetry.server_owner";
     // This is used to reprompt users when privacy message changes
     const TELEMETRY_PROMPT_REV = 2;
 
+    function appendTelemetryNotification(notifyBox, message, buttons, hideclose) {
+      let notification = notifyBox.appendNotification(message, "telemetry", null,
+						      notifyBox.PRIORITY_INFO_LOW,
+						      buttons);
+      notification.setAttribute("hideclose", hideclose);
+      notification.persistence = -1;  // Until user closes it
+      return notification;
+    }
+
     var telemetryPrompted = null;
     try {
       telemetryPrompted = Services.prefs.getIntPref(PREF_TELEMETRY_PROMPTED);
     } catch(e) {}
     // If the user has seen the latest telemetry prompt, do not prompt again
     // else clear old prefs and reprompt
     if (telemetryPrompted === TELEMETRY_PROMPT_REV)
       return;
@@ -818,33 +827,30 @@ BrowserGlue.prototype = {
                         Services.prefs.setBoolPref(PREF_TELEMETRY_REJECTED, true);
                       }
                     }
                   ];
 
     // Set pref to indicate we've shown the notification.
     Services.prefs.setIntPref(PREF_TELEMETRY_PROMPTED, TELEMETRY_PROMPT_REV);
 
-    var notification = notifyBox.appendNotification(telemetryPrompt, "telemetry", null, notifyBox.PRIORITY_INFO_LOW, buttons);
-    notification.setAttribute("hideclose", true);
-    notification.persistence = -1;  // Until user closes it
-
+    let notification = appendTelemetryNotification(notifyBox, telemetryPrompt,
+						   buttons, true);
     let XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
     let link = notification.ownerDocument.createElementNS(XULNS, "label");
     link.className = "text-link telemetry-text-link";
     link.setAttribute("value", browserBundle.GetStringFromName("telemetryLinkLabel"));
     link.addEventListener('click', function() {
       // Open the learn more url in a new tab
       browser.selectedTab = browser.addTab(Services.prefs.getCharPref(PREF_TELEMETRY_INFOURL));
       // Remove the notification on which the user clicked
       notification.parentNode.removeNotification(notification, true);
       // Add a new notification to that tab, with no "Learn more" link
       notifyBox = browser.getNotificationBox();
-      notification = notifyBox.appendNotification(telemetryPrompt, "telemetry", null, notifyBox.PRIORITY_INFO_LOW, buttons);
-      notification.persistence = -1; // Until user closes it
+      appendTelemetryNotification(notifyBox, telemetryPrompt, buttons, true);
     }, false);
     let description = notification.ownerDocument.getAnonymousElementByAttribute(notification, "anonid", "messageText");
     description.appendChild(link);
   },
 #endif
 
   _showPluginUpdatePage: function BG__showPluginUpdatePage() {
     Services.prefs.setBoolPref(PREF_PLUGINS_NOTIFYUSER, false);
@@ -1295,17 +1301,17 @@ BrowserGlue.prototype = {
   ensurePlacesDefaultQueriesInitialized:
   function BG_ensurePlacesDefaultQueriesInitialized() {
     // This is actual version of the smart bookmarks, must be increased every
     // time smart bookmarks change.
     // When adding a new smart bookmark below, its newInVersion property must
     // be set to the version it has been added in, we will compare its value
     // to users' smartBookmarksVersion and add new smart bookmarks without
     // recreating old deleted ones.
-    const SMART_BOOKMARKS_VERSION = 2;
+    const SMART_BOOKMARKS_VERSION = 3;
     const SMART_BOOKMARKS_ANNO = "Places/SmartBookmark";
     const SMART_BOOKMARKS_PREF = "browser.places.smartBookmarksVersion";
 
     // TODO bug 399268: should this be a pref?
     const MAX_RESULTS = 10;
 
     // Get current smart bookmarks version.  If not set, create them.
     let smartBookmarksCurrentVersion = 0;
@@ -1341,17 +1347,16 @@ BrowserGlue.prototype = {
             title: bundle.GetStringFromName("recentlyBookmarkedTitle"),
             uri: NetUtil.newURI("place:folder=BOOKMARKS_MENU" +
                                 "&folder=UNFILED_BOOKMARKS" +
                                 "&folder=TOOLBAR" +
                                 "&queryType=" +
                                 Ci.nsINavHistoryQueryOptions.QUERY_TYPE_BOOKMARKS +
                                 "&sort=" +
                                 Ci.nsINavHistoryQueryOptions.SORT_BY_DATEADDED_DESCENDING +
-                                "&excludeItemIfParentHasAnnotation=livemark%2FfeedURI" +
                                 "&maxResults=" + MAX_RESULTS +
                                 "&excludeQueries=1"),
             parent: PlacesUtils.bookmarksMenuFolderId,
             position: menuIndex++,
             newInVersion: 1
           },
           RecentTags: {
             title: bundle.GetStringFromName("recentTagsTitle"),
--- a/browser/components/places/content/bookmarkProperties.js
+++ b/browser/components/places/content/bookmarkProperties.js
@@ -276,23 +276,36 @@ var BookmarkPropertiesPanel = {
                                      .getKeywordForBookmark(this._itemId);
           // Load In Sidebar
           this._loadInSidebar = PlacesUtils.annotations
                                            .itemHasAnnotation(this._itemId,
                                                               PlacesUIUtils.LOAD_IN_SIDEBAR_ANNO);
           break;
 
         case "folder":
-          if (PlacesUtils.itemIsLivemark(this._itemId)) {
-            this._itemType = LIVEMARK_CONTAINER;
-            this._feedURI = PlacesUtils.livemarks.getFeedURI(this._itemId);
-            this._siteURI = PlacesUtils.livemarks.getSiteURI(this._itemId);
-          }
-          else
-            this._itemType = BOOKMARK_FOLDER;
+          this._itemType = BOOKMARK_FOLDER;
+          PlacesUtils.livemarks.getLivemark(
+            { id: this._itemId },
+            (function (aStatus, aLivemark) {
+              if (Components.isSuccessCode(aStatus)) {
+                this._itemType = LIVEMARK_CONTAINER;
+                this._feedURI = aLivemark.feedURI;
+                this._siteURI = aLivemark.siteURI;
+                this._fillEditProperties();
+
+                let acceptButton = document.documentElement.getButton("accept");
+                acceptButton.disabled = !this._inputIsValid();
+
+                let newHeight = window.outerHeight +
+                                this._element("descriptionField").boxObject.height;
+                window.resizeTo(window.outerWidth, newHeight);
+              }
+            }).bind(this)
+          );
+
           break;
       }
 
       // Description
       if (PlacesUtils.annotations
                      .itemHasAnnotation(this._itemId, PlacesUIUtils.DESCRIPTION_ANNO)) {
         this._description = PlacesUtils.annotations
                                        .getItemAnnotation(this._itemId,
@@ -336,18 +349,17 @@ var BookmarkPropertiesPanel = {
       case ACTION_EDIT:
         this._fillEditProperties();
         acceptButton.disabled = this._readOnly;
         break;
       case ACTION_ADD:
         this._fillAddProperties();
         // if this is an uri related dialog disable accept button until
         // the user fills an uri value.
-        if (this._itemType == BOOKMARK_ITEM ||
-            this._itemType == LIVEMARK_CONTAINER)
+        if (this._itemType == BOOKMARK_ITEM)
           acceptButton.disabled = !this._inputIsValid();
         break;
     }
 
     // When collapsible elements change their collapsed attribute we must
     // resize the dialog.
     // sizeToContent is not usable due to bug 90276, so we'll use resizeTo
     // instead and cache the element size. See WSucks in the legacy
@@ -513,26 +525,16 @@ var BookmarkPropertiesPanel = {
    */
   _inputIsValid: function BPP__inputIsValid() {
     if (this._itemType == BOOKMARK_ITEM &&
         !this._containsValidURI("locationField"))
       return false;
     if (this._isAddKeywordDialog && !this._element("keywordField").value.length)
       return false;
 
-    // Feed Location has to be a valid URI;
-    // Site Location has to be a valid URI or empty
-    if (this._itemType == LIVEMARK_CONTAINER) {
-      if (!this._containsValidURI("feedLocationField"))
-        return false;
-      if (!this._containsValidURI("siteLocationField") &&
-          (this._element("siteLocationField").value.length > 0))
-        return false;
-    }
-
     return true;
   },
 
   /**
    * Determines whether the XUL textbox with the given ID contains a
    * string that can be converted into an nsIURI.
    *
    * @param aTextboxID
--- a/browser/components/places/content/browserPlacesViews.js
+++ b/browser/components/places/content/browserPlacesViews.js
@@ -1,49 +1,11 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is Places Frontend Code.
- *
- * The Initial Developer of the Original Code is
- * Google Inc.
- * Portions created by the Initial Developer are Copyright (C) 2005
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *   Annie Sullivan <annie.sullivan@gmail.com>
- *   Ben Goodger <beng@google.com>
- *   Myk Melez <myk@mozilla.org>
- *   Marco Bonardo <mak77@bonardo.net>
- *   Asaf Romano <mano@mozilla.com>
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
+/* 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/. */
 
 Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
 Components.utils.import("resource://gre/modules/Services.jsm");
 
 /**
  * The base view implements everything that's common to the toolbar and
  * menu views.
  */
@@ -165,17 +127,18 @@ PlacesViewBase.prototype = {
     let index = PlacesUtils.bookmarks.DEFAULT_INDEX;
     let container = this._resultNode;
     let orientation = Ci.nsITreeView.DROP_BEFORE;
     let isTag = false;
 
     let selectedNode = this.selectedNode;
     if (selectedNode) {
       let popup = document.popupNode;
-      if (!popup._placesNode || popup._placesNode == this._resultNode) {
+      if (!popup._placesNode || popup._placesNode == this._resultNode ||
+          popup._placesNode.itemId == -1) {
         // If a static menuitem is selected, or if the root node is selected,
         // the insertion point is inside the folder, at the end.
         container = selectedNode;
         orientation = Ci.nsITreeView.DROP_ON;
       }
       else {
         // In all other cases the insertion point is before that node.
         container = selectedNode.parent;
@@ -205,63 +168,72 @@ PlacesViewBase.prototype = {
 
   _cleanPopup: function PVB_cleanPopup(aPopup) {
     // Remove places popup children and update markers to keep track of
     // their indices.
     let start = aPopup._startMarker != -1 ? aPopup._startMarker + 1 : 0;
     let end = aPopup._endMarker != -1 ? aPopup._endMarker :
                                         aPopup.childNodes.length;
     let items = [];
-    let placesNodeFound = false;
+
+    // Automatically adjust the start and the end markers.
+    let firstNonStaticNodeFound = false;
     for (let i = start; i < end; ++i) {
       let item = aPopup.childNodes[i];
       if (item.getAttribute("builder") == "end") {
         // we need to do this for menus that have static content at the end but
         // are initially empty, eg. the history menu, we need to know where to
         // start inserting new items.
         aPopup._endMarker = i;
         break;
       }
+
       if (item._placesNode) {
         items.push(item);
-        placesNodeFound = true;
+        firstNonStaticNodeFound = true;
       }
       else {
-        // This is static content...
-        if (!placesNodeFound)
-          // ...at the start of the popup
-          // Initialized in menu.xml, in the base binding
+        // This is static content.
+        if (!firstNonStaticNodeFound) {
+          // We are at the beginning of the popup, in static content.
+          // The markers are initialized in menu.xml, in the base binding.
           aPopup._startMarker++;
+        }
         else {
-          // ...after places nodes
+          // We are at the end of the popup, after places nodes
           aPopup._endMarker = i;
           break;
         }
       }
     }
 
     for (let i = 0; i < items.length; ++i) {
       aPopup.removeChild(items[i]);
       if (aPopup._endMarker != -1)
         aPopup._endMarker--;
     }
   },
 
   _rebuildPopup: function PVB__rebuildPopup(aPopup) {
     this._cleanPopup(aPopup);
 
-    // If this is a livemark container check if the status menuitem has
-    // to be added or removed.
-    if (PlacesUtils.nodeIsLivemarkContainer(aPopup._placesNode))
-      this._ensureLivemarkStatusMenuItem(aPopup);
-
     let resultNode = aPopup._placesNode;
     if (!resultNode.containerOpen)
       return;
 
+    if (resultNode._feedURI) {
+      aPopup.removeAttribute("emptyplacesresult");
+      if (aPopup._emptyMenuItem) {
+        aPopup._emptyMenuItem.hidden = true;
+      }
+      aPopup._built = true;
+      this._populateLivemarkPopup(aPopup);
+      return;
+    }
+
     let cc = resultNode.childCount;
     if (cc > 0) {
       aPopup.removeAttribute("emptyplacesresult");
       if (aPopup._emptyMenuItem)
         aPopup._emptyMenuItem.hidden = true;
 
       for (let i = 0; i < cc; ++i) {
         let child = resultNode.getChild(i);
@@ -298,21 +270,24 @@ PlacesViewBase.prototype = {
     aPopup._emptyMenuItem = document.createElement("menuitem");
     aPopup._emptyMenuItem.setAttribute("label", label);
     aPopup._emptyMenuItem.setAttribute("disabled", true);
     aPopup.appendChild(aPopup._emptyMenuItem);
   },
 
   _createMenuItemForPlacesNode:
   function PVB__createMenuItemForPlacesNode(aPlacesNode) {
+    delete aPlacesNode._DOMElement;
     let element;
     let type = aPlacesNode.type;
-    if (type == Ci.nsINavHistoryResultNode.RESULT_TYPE_SEPARATOR)
+    if (type == Ci.nsINavHistoryResultNode.RESULT_TYPE_SEPARATOR) {
       element = document.createElement("menuseparator");
+    }
     else {
+      let itemId = aPlacesNode.itemId;
       if (PlacesUtils.uriTypes.indexOf(type) != -1) {
         element = document.createElement("menuitem");
         element.className = "menuitem-iconic bookmark-item menuitem-with-favicon";
         element.setAttribute("scheme",
                              PlacesUIUtils.guessUrlSchemeForUI(aPlacesNode.uri));
       }
       else if (PlacesUtils.containerTypes.indexOf(type) != -1) {
         element = document.createElement("menu");
@@ -322,19 +297,29 @@ PlacesViewBase.prototype = {
           element.setAttribute("query", "true");
           if (PlacesUtils.nodeIsTagQuery(aPlacesNode))
             element.setAttribute("tagContainer", "true");
           else if (PlacesUtils.nodeIsDay(aPlacesNode))
             element.setAttribute("dayContainer", "true");
           else if (PlacesUtils.nodeIsHost(aPlacesNode))
             element.setAttribute("hostContainer", "true");
         }
-        else if (aPlacesNode.itemId != -1) {
-          if (PlacesUtils.nodeIsLivemarkContainer(aPlacesNode))
-            element.setAttribute("livemark", "true");
+        else if (itemId != -1) {
+          PlacesUtils.livemarks.getLivemark(
+            { id: itemId },
+            function (aStatus, aLivemark) {
+              if (Components.isSuccessCode(aStatus)) {
+                element.setAttribute("livemark", "true");
+                // Set an expando on the node, controller will use it to build
+                // its metadata.
+                aPlacesNode._feedURI = aLivemark.feedURI;
+                aPlacesNode._siteURI = aLivemark.siteURI;
+              }
+            }
+          );
         }
 
         let popup = document.createElement("menupopup");
         popup._placesNode = PlacesUtils.asContainer(aPlacesNode);
         if (this._nativeView) {
           popup._startMarker = -1;
           popup._endMarker = -1;
         }
@@ -387,52 +372,94 @@ PlacesViewBase.prototype = {
     }
 
     if (aPopup._endMarker != -1)
       aPopup._endMarker++;
 
     return element;
   },
 
+  _setLivemarkSiteURIMenuItem:
+  function PVB__setLivemarkSiteURIMenuItem(aPopup) {
+    let siteUrl = aPopup._placesNode._siteURI ? aPopup._placesNode._siteURI.spec
+                                              : null;
+    if (!siteUrl && aPopup._siteURIMenuitem) {
+      aPopup.removeChild(aPopup._siteURIMenuitem);
+      aPopup._siteURIMenuitem = null;
+      aPopup._startMarker--;
+      aPopup.removeChild(aPopup._siteURIMenuseparator);
+      aPopup._siteURIMenuseparator = null;
+      aPopup._startMarker--;
+    }
+    else if (siteUrl && !aPopup._siteURIMenuitem) {
+      // Add "Open (Feed Name)" menuitem.
+      aPopup._siteURIMenuitem = document.createElement("menuitem");
+      aPopup._siteURIMenuitem.className = "openlivemarksite-menuitem";
+      aPopup._siteURIMenuitem.setAttribute("targetURI", siteUrl);
+      aPopup._siteURIMenuitem.setAttribute("oncommand",
+        "openUILink(this.getAttribute('targetURI'), event);");
+
+      // If a user middle-clicks this item we serve the oncommand event.
+      // We are using checkForMiddleClick because of Bug 246720.
+      // Note: stopPropagation is needed to avoid serving middle-click
+      // with BT_onClick that would open all items in tabs.
+      aPopup._siteURIMenuitem.setAttribute("onclick",
+        "checkForMiddleClick(this, event); event.stopPropagation();");
+      let label =
+        PlacesUIUtils.getFormattedString("menuOpenLivemarkOrigin.label",
+                                         [aPopup.parentNode.getAttribute("label")])
+      aPopup._siteURIMenuitem.setAttribute("label", label);
+      aPopup.insertBefore(aPopup._siteURIMenuitem,
+                          aPopup.childNodes.item(aPopup._startMarker + 1));
+      aPopup._startMarker++;
+
+      aPopup._siteURIMenuseparator = document.createElement("menuseparator");
+      aPopup.insertBefore(aPopup._siteURIMenuseparator,
+                         aPopup.childNodes.item(aPopup._startMarker + 1));
+      aPopup._startMarker++;
+    }
+  },
+
   /**
    * Add, update or remove the livemark status menuitem.
    * @param aPopup
    *        The livemark container popup
+   * @param aStatus
+   *        The livemark status
    */
-  _ensureLivemarkStatusMenuItem:
-  function PVB_ensureLivemarkStatusMenuItem(aPopup) {
+  _setLivemarkStatusMenuItem:
+  function PVB_setLivemarkStatusMenuItem(aPopup, aStatus) {
     let itemId = aPopup._placesNode.itemId;
-    let as = PlacesUtils.annotations;
+    let statusMenuitem = aPopup._statusMenuitem;
+    let stringId = "";
+    if (aStatus == Ci.mozILivemark.STATUS_LOADING)
+      stringId = "bookmarksLivemarkLoading";
+    else if (aStatus == Ci.mozILivemark.STATUS_FAILED)
+      stringId = "bookmarksLivemarkFailed";
 
-    let lmStatus = null;
-    if (as.itemHasAnnotation(itemId, PlacesUtils.LMANNO_LOADFAILED))
-      lmStatus = "bookmarksLivemarkFailed";
-    else if (as.itemHasAnnotation(itemId, PlacesUtils.LMANNO_LOADING))
-      lmStatus = "bookmarksLivemarkLoading";
-
-    let lmStatusElt = aPopup._lmStatusMenuItem;
-    if (lmStatus && !lmStatusElt) {
+    if (stringId && !statusMenuitem) {
       // Create the status menuitem and cache it in the popup object.
-      lmStatusElt = document.createElement("menuitem");
-      lmStatusElt.setAttribute("lmStatus", lmStatus);
-      lmStatusElt.setAttribute("label", PlacesUIUtils.getString(lmStatus));
-      lmStatusElt.setAttribute("disabled", true);
-      aPopup.insertBefore(lmStatusElt,
+      statusMenuitem = document.createElement("menuitem");
+      statusMenuitem.setAttribute("livemarkStatus", stringId);
+      statusMenuitem.setAttribute("label", PlacesUIUtils.getString(stringId));
+      statusMenuitem.setAttribute("disabled", true);
+      aPopup.insertBefore(statusMenuitem,
                           aPopup.childNodes.item(aPopup._startMarker + 1));
-      aPopup._lmStatusMenuItem = lmStatusElt;
+      aPopup._statusMenuitem = statusMenuitem;
       aPopup._startMarker++;
     }
-    else if (lmStatus && lmStatusElt.getAttribute("lmStatus") != lmStatus) {
+    else if (stringId &&
+             statusMenuitem.getAttribute("livemarkStatus") != stringId) {
       // Status has changed, update the cached status menuitem.
-      lmStatusElt.setAttribute("label", this.getString(lmStatus));
+      statusMenuitem.setAttribute("label", PlacesUIUtils.getString(stringId));
     }
-    else if (!lmStatus && lmStatusElt) {
-      // No status, remove the cached menuitem.
-      aPopup.removeChild(aPopup._lmStatusMenuItem);
-      aPopup._lmStatusMenuItem = null;
+    else if (!stringId && statusMenuitem) {
+      // The livemark has finished loading.
+      aPopup.removeChild(aPopup._statusMenuitem);
+      aPopup._statusMenuitem = null;
       aPopup._startMarker--;
     }
   },
 
   toggleCutNode: function PVB_toggleCutNode(aNode, aValue) {
     let elt = aNode._DOMElement;
     if (elt) {
       // We may get the popup for menus, but we need the menu itself.
@@ -483,24 +510,32 @@ PlacesViewBase.prototype = {
     let elt = aPlacesNode._DOMElement;
     if (!elt)
       throw "aPlacesNode must have _DOMElement set";
 
     // All livemarks have a feedURI, so use it as our indicator of a livemark
     // being modified.
     if (aAnno == PlacesUtils.LMANNO_FEEDURI) {
       let menu = elt.parentNode;
-      if (!menu.hasAttribute("livemark"))
+      if (!menu.hasAttribute("livemark")) {
         menu.setAttribute("livemark", "true");
-    }
+      }
 
-    if ([PlacesUtils.LMANNO_LOADING,
-         PlacesUtils.LMANNO_LOADFAILED].indexOf(aAnno) != -1) {
-      // Loading status changed, update the livemark status menuitem.
-      this._ensureLivemarkStatusMenuItem(elt);
+      PlacesUtils.livemarks.getLivemark(
+        { id: aPlacesNode.itemId },
+        (function (aStatus, aLivemark) {
+          if (Components.isSuccessCode(aStatus)) {
+            // Set an expando on the node, controller will use it to build
+            // its metadata.
+            aPlacesNode._feedURI = aLivemark.feedURI;
+            aPlacesNode._siteURI = aLivemark.siteURI;
+            this.invalidateContainer(aPlacesNode);
+          }
+        }).bind(this)
+      );
     }
   },
 
   nodeTitleChanged:
   function PVB_nodeTitleChanged(aPlacesNode, aNewTitle) {
     let elt = aPlacesNode._DOMElement;
     if (!elt)
       throw "aPlacesNode must have _DOMElement set";
@@ -575,17 +610,34 @@ PlacesViewBase.prototype = {
       // No worries: If elt is the last item (i.e. no nextSibling),
       // _insertNewItem/_insertNewItemToPopup will insert the new element as
       // the last item.
       let nextElt = elt.nextSibling;
       this._insertNewItemToPopup(aNewPlacesNode, parentElt, nextElt);
     }
   },
 
-  nodeHistoryDetailsChanged: function() { },
+  nodeHistoryDetailsChanged:
+  function PVB_nodeHistoryDetailsChanged(aPlacesNode, aTime, aCount) {
+    if (aPlacesNode.parent && aPlacesNode.parent._feedURI) {
+      // Find the node in the parent.
+      let popup = aPlacesNode.parent._DOMElement;
+      for (let i = popup._startMarker; i < popup.childNodes.length; i++) {
+        let child = popup.childNodes[i];
+        if (child._placesNode && child._placesNode.uri == aPlacesNode.uri) {
+          if (aCount)
+            child.setAttribute("visited", "true");
+          else
+            child.removeAttribute("visited");
+          break;
+        }
+      }
+    }
+  },
+
   nodeTagsChanged: function() { },
   nodeDateAddedChanged: function() { },
   nodeLastModifiedChanged: function() { },
   nodeKeywordChanged: function() { },
   sortingChanged: function() { },
   batching: function() { },
 
   nodeInserted:
@@ -637,20 +689,70 @@ PlacesViewBase.prototype = {
     }
   },
 
   containerStateChanged:
   function PVB_containerStateChanged(aPlacesNode, aOldState, aNewState) {
     if (aNewState == Ci.nsINavHistoryContainerResultNode.STATE_OPENED ||
         aNewState == Ci.nsINavHistoryContainerResultNode.STATE_CLOSED) {
       this.invalidateContainer(aPlacesNode);
+
+      if (PlacesUtils.nodeIsFolder(aPlacesNode)) {
+        let queryOptions = PlacesUtils.asQuery(this._result.root).queryOptions;
+        if (queryOptions.excludeItems) {
+          return;
+        }
+
+        PlacesUtils.livemarks.getLivemark({ id: aPlacesNode.itemId },
+          (function (aStatus, aLivemark) {
+            if (Components.isSuccessCode(aStatus)) {
+              let shouldInvalidate = !aPlacesNode._feedURI;
+              aPlacesNode._feedURI = aLivemark.feedURI;
+              aPlacesNode._siteURI = aLivemark.siteURI;
+              if (aNewState == Ci.nsINavHistoryContainerResultNode.STATE_OPENED) {
+                aLivemark.registerForUpdates(aPlacesNode, this);
+                aLivemark.reload();
+                if (shouldInvalidate)
+                  this.invalidateContainer(aPlacesNode);
+              }
+              else {
+                aLivemark.unregisterForUpdates(aPlacesNode);
+              }
+            }
+          }).bind(this)
+        );
+      }
     }
-    else {
-      throw "Unexpected state passed to containerStateChanged";
-    }
+  },
+
+  _populateLivemarkPopup: function PVB__populateLivemarkPopup(aPopup)
+  {
+    this._setLivemarkSiteURIMenuItem(aPopup);
+    this._setLivemarkStatusMenuItem(aPopup, Ci.mozILivemark.STATUS_LOADING);
+
+    PlacesUtils.livemarks.getLivemark({ id: aPopup._placesNode.itemId },
+      (function (aStatus, aLivemark) {
+        let placesNode = aPopup._placesNode;
+        if (!Components.isSuccessCode(aStatus) || !placesNode.containerOpen)
+          return;
+
+        this._setLivemarkStatusMenuItem(aPopup, aLivemark.status);
+        this._cleanPopup(aPopup);
+
+        let children = aLivemark.getNodesForContainer(placesNode);
+        for (let i = 0; i < children.length; i++) {
+          let child = children[i];
+          this.nodeInserted(placesNode, child, i);
+          if (child.accessCount)
+            child._DOMElement.setAttribute("visited", true);
+          else
+            child._DOMElement.removeAttribute("visited");
+        }
+      }).bind(this)
+    );
   },
 
   invalidateContainer: function PVB_invalidateContainer(aPlacesNode) {
     let elt = aPlacesNode._DOMElement;
     if (!elt)
       throw "aPlacesNode must have _DOMElement set";
 
     elt._built = false;
@@ -691,97 +793,65 @@ PlacesViewBase.prototype = {
    * @param aPopup
    *        a Places popup.
    */
   _mayAddCommandsItems: function PVB__mayAddCommandsItems(aPopup) {
     // The command items are never added to the root popup.
     if (aPopup == this._rootElt)
       return;
 
-    // Check if the popup contains at least 2 menuitems with places nodes
-    let numURINodes = 0;
-    let currentChild = aPopup.firstChild;
-    while (currentChild) {
-      if (currentChild.localName == "menuitem" && currentChild._placesNode) {
-        if (++numURINodes == 2)
-          break;
+    let hasMultipleURIs = false;
+
+    // Check if the popup contains at least 2 menuitems with places nodes.
+    // We don't currently support opening multiple uri nodes when they are not
+    // populated by the result.
+    if (aPopup._placesNode.childCount > 0) {
+      let currentChild = aPopup.firstChild;
+      let numURINodes = 0;
+      while (currentChild) {
+        if (currentChild.localName == "menuitem" && currentChild._placesNode) {
+          if (++numURINodes == 2)
+            break;
+        }
+        currentChild = currentChild.nextSibling;
       }
-      currentChild = currentChild.nextSibling;
-    }
-
-    let hasMultipleURIs = numURINodes > 1;
-    let itemId = aPopup._placesNode.itemId;
-    let siteURIString = "";
-    if (itemId != -1 && PlacesUtils.itemIsLivemark(itemId)) {
-      let siteURI = PlacesUtils.livemarks.getSiteURI(itemId);
-      if (siteURI)
-        siteURIString = siteURI.spec;
+      hasMultipleURIs = numURINodes > 1;
     }
 
-    if (!siteURIString && aPopup._endOptOpenSiteURI) {
-      aPopup.removeChild(aPopup._endOptOpenSiteURI);
-      aPopup._endOptOpenSiteURI = null;
-    }
+    if (!hasMultipleURIs) {
+      // We don't have to show any option.
+      if (aPopup._endOptOpenAllInTabs) {
+        aPopup.removeChild(aPopup._endOptOpenAllInTabs);
+        aPopup._endOptOpenAllInTabs = null;
+        aPopup._endMarker--;
 
-    if (!hasMultipleURIs && aPopup._endOptOpenAllInTabs) {
-      aPopup.removeChild(aPopup._endOptOpenAllInTabs);
-      aPopup._endOptOpenAllInTabs = null;
-    }
-
-    if (!(hasMultipleURIs || siteURIString)) {
-      // We don't have to show any option.
-      if (aPopup._endOptSeparator) {
         aPopup.removeChild(aPopup._endOptSeparator);
         aPopup._endOptSeparator = null;
-        aPopup._endMarker = -1;
+        aPopup._endMarker--;
       }
-      return;
     }
-
-    if (!aPopup._endOptSeparator) {
+    else if (!aPopup._endOptOpenAllInTabs) {
       // Create a separator before options.
       aPopup._endOptSeparator = document.createElement("menuseparator");
       aPopup._endOptSeparator.className = "bookmarks-actions-menuseparator";
-      aPopup._endMarker = aPopup.childNodes.length;
       aPopup.appendChild(aPopup._endOptSeparator);
-    }
-
-    if (siteURIString && !aPopup._endOptOpenSiteURI) {
-      // Add "Open (Feed Name)" menuitem if it's a livemark with a siteURI.
-      aPopup._endOptOpenSiteURI = document.createElement("menuitem");
-      aPopup._endOptOpenSiteURI.className = "openlivemarksite-menuitem";
-      aPopup._endOptOpenSiteURI.setAttribute("targetURI", siteURIString);
-      aPopup._endOptOpenSiteURI.setAttribute("oncommand",
-          "openUILink(this.getAttribute('targetURI'), event);");
+      aPopup._endMarker++;
 
-      // If a user middle-clicks this item we serve the oncommand event
-      // We are using checkForMiddleClick because of Bug 246720
-      // Note: stopPropagation is needed to avoid serving middle-click
-      // with BT_onClick that would open all items in tabs.
-      aPopup._endOptOpenSiteURI.setAttribute("onclick",
-          "checkForMiddleClick(this, event); event.stopPropagation();");
-      aPopup._endOptOpenSiteURI.setAttribute("label",
-          PlacesUIUtils.getFormattedString("menuOpenLivemarkOrigin.label",
-          [aPopup.parentNode.getAttribute("label")]));
-      aPopup.appendChild(aPopup._endOptOpenSiteURI);
-    }
-
-    if (hasMultipleURIs && !aPopup._endOptOpenAllInTabs) {
-      // Add the "Open All in Tabs" menuitem if there are
-      // at least two menuitems with places result nodes.
+      // Add the "Open All in Tabs" menuitem.
       aPopup._endOptOpenAllInTabs = document.createElement("menuitem");
       aPopup._endOptOpenAllInTabs.className = "openintabs-menuitem";
       aPopup._endOptOpenAllInTabs.setAttribute("oncommand",
         "PlacesUIUtils.openContainerNodeInTabs(this.parentNode._placesNode, event, " +
                                                "PlacesUIUtils.getViewForNode(this));");
       aPopup._endOptOpenAllInTabs.setAttribute("onclick",
         "checkForMiddleClick(this, event); event.stopPropagation();");
       aPopup._endOptOpenAllInTabs.setAttribute("label",
         gNavigatorBundle.getString("menuOpenAllInTabs.label"));
       aPopup.appendChild(aPopup._endOptOpenAllInTabs);
+      aPopup._endMarker++;
     }
   },
 
   _onPopupShowing: function PVB__onPopupShowing(aEvent) {
     // Avoid handling popupshowing of inner views.
     let popup = aEvent.originalTarget;
     if (popup._placesNode && PlacesUIUtils.getViewForNode(popup) == this) {
       if (!popup._placesNode.containerOpen)
@@ -896,16 +966,17 @@ PlacesToolbar.prototype = {
       // a rebuild of the toolbar, it has to be rebuilt.
       // Otherwise, it will be initialized when the toolbar overflows.
       this._chevronPopup.place = this.place;
     }
   },
 
   _insertNewItem:
   function PT__insertNewItem(aChild, aBefore) {
+    delete aChild._DOMElement;
     let type = aChild.type;
     let button;
     if (type == Ci.nsINavHistoryResultNode.RESULT_TYPE_SEPARATOR) {
       button = document.createElement("toolbarseparator");
     }
     else {
       button = document.createElement("toolbarbutton");
       button.className = "bookmark-item";
@@ -918,18 +989,29 @@ PlacesToolbar.prototype = {
         button.setAttribute("type", "menu");
         button.setAttribute("container", "true");
 
         if (PlacesUtils.nodeIsQuery(aChild)) {
           button.setAttribute("query", "true");
           if (PlacesUtils.nodeIsTagQuery(aChild))
             button.setAttribute("tagContainer", "true");
         }
-        else if (PlacesUtils.nodeIsLivemarkContainer(aChild)) {
-          button.setAttribute("livemark", "true");
+        else if (PlacesUtils.nodeIsFolder(aChild)) {
+          PlacesUtils.livemarks.getLivemark(
+            { id: aChild.itemId },
+            function (aStatus, aLivemark) {
+              if (Components.isSuccessCode(aStatus)) {
+                button.setAttribute("livemark", "true");
+                // Set an expando on the node, controller will use it to build
+                // its metadata.
+                aChild._feedURI = aLivemark.feedURI;
+                aChild._siteURI = aLivemark.siteURI;
+              }
+            }
+          );
         }
 
         let popup = document.createElement("menupopup");
         popup.setAttribute("placespopup", "true");
         button.appendChild(popup);
         popup._placesNode = PlacesUtils.asContainer(aChild);
 #ifndef XP_MACOSX
         popup.setAttribute("context", "placesContext");
@@ -1183,22 +1265,29 @@ PlacesToolbar.prototype = {
       elt = elt.parentNode;
 
     if (elt.parentNode == this._rootElt) {
       // Node is on the toolbar.
 
       // All livemarks have a feedURI, so use it as our indicator.
       if (aAnno == PlacesUtils.LMANNO_FEEDURI) {
         elt.setAttribute("livemark", true);
-      }
 
-      if ([PlacesUtils.LMANNO_LOADING,
-           PlacesUtils.LMANNO_LOADFAILED].indexOf(aAnno) != -1) {
-        // Loading status changed, update the livemark status menuitem.
-        this._ensureLivemarkStatusMenuItem(elt.firstChild);
+        PlacesUtils.livemarks.getLivemark(
+          { id: aPlacesNode.itemId },
+          (function (aStatus, aLivemark) {
+            if (Components.isSuccessCode(aStatus)) {
+              // Set an expando on the node, controller will use it to build
+              // its metadata.
+              aPlacesNode._feedURI = aLivemark.feedURI;
+              aPlacesNode._siteURI = aLivemark.siteURI;
+              this.invalidateContainer(aPlacesNode);
+            }
+          }).bind(this)
+        );
       }
     }
     else {
       // Node is in a submenu.
       PlacesViewBase.prototype.nodeAnnotationChanged.apply(this, arguments);
     }
   },
 
@@ -1618,23 +1707,26 @@ PlacesToolbar.prototype = {
     if (parent.localName == "toolbarbutton")
       this._openedMenuButton = parent;
 
     return PlacesViewBase.prototype._onPopupShowing.apply(this, arguments);
   },
 
   _onPopupHidden: function PT__onPopupHidden(aEvent) {
     let popup = aEvent.target;
-
+    let placesNode = popup._placesNode;
     // Avoid handling popuphidden of inner views
-    if (popup._placesNode && PlacesUIUtils.getViewForNode(popup) == this) {
+    if (placesNode && PlacesUIUtils.getViewForNode(popup) == this) {
       // UI performance: folder queries are cheap, keep the resultnode open
       // so we don't rebuild its contents whenever the popup is reopened.
-      if (!PlacesUtils.nodeIsFolder(popup._placesNode))
-        popup._placesNode.containerOpen = false;
+      // Though, we want to always close feed containers so their expiration
+      // status will be checked at next opening.
+      if (!PlacesUtils.nodeIsFolder(placesNode) || placesNode._feedURI) {
+        placesNode.containerOpen = false;
+      }
     }
 
     let parent = popup.parentNode;
     if (parent.localName == "toolbarbutton") {
       this._openedMenuButton = null;
       // Clear the dragover attribute if present, if we are dragging into a
       // folder in the hierachy of current opened popup we don't clear
       // this attribute on clearOverFolder.  See Notify for closeTimer.
--- a/browser/components/places/content/controller.js
+++ b/browser/components/places/content/controller.js
@@ -39,17 +39,16 @@
  * ***** END LICENSE BLOCK ***** */
 
 Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
                                   "resource://gre/modules/NetUtil.jsm");
 
 // XXXmano: we should move most/all of these constants to PlacesUtils
 const ORGANIZER_ROOT_BOOKMARKS = "place:folder=BOOKMARKS_MENU&excludeItems=1&queryType=1";
-const ORGANIZER_SUBSCRIPTIONS_QUERY = "place:annotation=livemark%2FfeedURI";
 
 // No change to the view, preserve current selection
 const RELOAD_ACTION_NOTHING = 0;
 // Inserting items new to the view, select the inserted rows
 const RELOAD_ACTION_INSERT = 1;
 // Removing items from the view, select the first item after the last selected
 const RELOAD_ACTION_REMOVE = 2;
 // Moving items within a view, don't treat the dropped items as additional
@@ -203,25 +202,21 @@ PlacesController.prototype = {
       return this._canInsert();
     case "placesCmd_new:separator":
       return this._canInsert() &&
              !PlacesUtils.asQuery(this._view.result.root).queryOptions.excludeItems &&
              this._view.result.sortingMode ==
                  Ci.nsINavHistoryQueryOptions.SORT_BY_NONE;
     case "placesCmd_show:info":
       var selectedNode = this._view.selectedNode;
-      if (selectedNode &&
-          PlacesUtils.getConcreteItemId(selectedNode) != -1  &&
-          !PlacesUtils.nodeIsLivemarkItem(selectedNode))
-        return true;
-      return false;
+      return selectedNode && PlacesUtils.getConcreteItemId(selectedNode) != -1
     case "placesCmd_reload":
       // Livemark containers
       var selectedNode = this._view.selectedNode;
-      return selectedNode && PlacesUtils.nodeIsLivemarkContainer(selectedNode);
+      return selectedNode && !!selectedNode._feedURI;
     case "placesCmd_sortBy:name":
       var selectedNode = this._view.selectedNode;
       return selectedNode &&
              PlacesUtils.nodeIsFolder(selectedNode) &&
              !PlacesUtils.nodeIsReadOnly(selectedNode) &&
              this._view.result.sortingMode ==
                  Ci.nsINavHistoryQueryOptions.SORT_BY_NONE;
     case "placesCmd_createBookmark":
@@ -504,17 +499,17 @@ PlacesController.prototype = {
           if (PlacesUtils.nodeIsBookmark(node)) {
             nodeData["bookmark"] = true;
             PlacesUtils.nodeIsTagQuery(node.parent)
 
             var parentNode = node.parent;
             if (parentNode) {
               if (PlacesUtils.nodeIsTagQuery(parentNode))
                 nodeData["tagChild"] = true;
-              else if (PlacesUtils.nodeIsLivemarkContainer(parentNode))
+              else if (parentNode._feedURI)
                 nodeData["livemarkChild"] = true;
             }
           }
           break;
       }
 
       // annotations
       if (uri) {
@@ -729,18 +724,27 @@ PlacesController.prototype = {
            "This method should be passed a URI as a nsIURI object, not as a string.");
   },
 
   /**
    * Reloads the selected livemark if any.
    */
   reloadSelectedLivemark: function PC_reloadSelectedLivemark() {
     var selectedNode = this._view.selectedNode;
-    if (selectedNode && PlacesUtils.nodeIsLivemarkContainer(selectedNode))
-      PlacesUtils.livemarks.reloadLivemarkFolder(selectedNode.itemId);
+    if (selectedNode) {
+      let itemId = selectedNode.itemId;
+      PlacesUtils.livemarks.getLivemark(
+        { id: itemId },
+        (function(aStatus, aLivemark) {
+          if (Components.isSuccessCode(aStatus)) {
+            aLivemark.reload(true);
+          }
+        }).bind(this)
+      );
+    }
   },
 
   /**
    * Opens the links in the selected folder, or the selected links in new tabs.
    */
   openSelectionInTabs: function PC_openLinksInTabs(aEvent) {
     var node = this._view.selectedNode;
     if (node && PlacesUtils.nodeIsContainer(node))
@@ -1060,20 +1064,22 @@ PlacesController.prototype = {
           addData(PlacesUtils.TYPE_HTML, index, overrideURI);
         }
 
         // This order is _important_! It controls how this and other
         // applications select data to be inserted based on type.
         addData(PlacesUtils.TYPE_X_MOZ_PLACE, i);
 
         // Drop the feed uri for livemark containers
-        if (PlacesUtils.nodeIsLivemarkContainer(node))
-          addURIData(i, PlacesUtils.livemarks.getFeedURI(node.itemId).spec);
-        else if (node.uri)
+        if (node._feedURI) {
+          addURIData(i, node._feedURI.spec);
+        }
+        else if (node.uri) {
           addURIData(i);
+        }
       }
     }
     finally {
       if (!didSuppressNotifications)
         result.suppressNotifications = false;
     }
   },
 
@@ -1133,18 +1139,17 @@ PlacesController.prototype = {
     // of them automatically.
     let copiedFolders = [];
     aNodes.forEach(function (node) {
       if (this._shouldSkipNode(node, copiedFolders))
         return;
       if (PlacesUtils.nodeIsFolder(node))
         copiedFolders.push(node);
 
-      let overrideURI = PlacesUtils.nodeIsLivemarkContainer(node) ?
-        PlacesUtils.livemarks.getFeedURI(node.itemId).spec : null;
+      let overrideURI = node._feedURI ? node._feedURI.spec : null;
       let resolveShortcuts = !PlacesControllerDragHelper.canMoveNode(node);
 
       contents.forEach(function (content) {
         content.entries.push(
           PlacesUtils.wrapNode(node, content.type, overrideURI, resolveShortcuts)
         );
       });
     }, this);
--- a/browser/components/places/content/editBookmarkOverlay.js
+++ b/browser/components/places/content/editBookmarkOverlay.js
@@ -1,44 +1,11 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is the Places Bookmark Properties dialog.
- *
- * The Initial Developer of the Original Code is Google Inc.
- * Portions created by the Initial Developer are Copyright (C) 2006
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *   Asaf Romano <mano@mozilla.com>
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
+/* 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/. */
 
 const LAST_USED_ANNO = "bookmarkPropertiesDialog/folderLastUsed";
 const MAX_FOLDER_ITEM_IN_MENU_LIST = 5;
 
 var gEditItemOverlay = {
   _uri: null,
   _itemId: -1,
   _itemIds: [],
@@ -47,16 +14,17 @@ var gEditItemOverlay = {
   _allTags: [],
   _multiEdit: false,
   _itemType: -1,
   _readOnly: false,
   _hiddenRows: [],
   _observersAdded: false,
   _staticFoldersListBuilt: false,
   _initialized: false,
+  _titleOverride: "",
 
   // the first field which was edited after this panel was initialized for
   // a certain item
   _firstEditedField: "",
 
   get itemId() {
     return this._itemId;
   },
@@ -75,16 +43,18 @@ var gEditItemOverlay = {
   _determineInfo: function EIO__determineInfo(aInfo) {
     // hidden rows
     if (aInfo && aInfo.hiddenRows)
       this._hiddenRows = aInfo.hiddenRows;
     else
       this._hiddenRows.splice(0, this._hiddenRows.length);
     // force-read-only
     this._readOnly = aInfo && aInfo.forceReadOnly;
+    this._titleOverride = aInfo && aInfo.titleOverride ? aInfo.titleOverride
+                                                       : "";
   },
 
   _showHideRows: function EIO__showHideRows() {
     var isBookmark = this._itemId != -1 &&
                      this._itemType == Ci.nsINavBookmarksService.TYPE_BOOKMARK;
     var isQuery = false;
     if (this._uri)
       isQuery = this._uri.schemeIs("place");
@@ -155,42 +125,44 @@ var gEditItemOverlay = {
     this._determineInfo(aInfo);
     if (aFor instanceof Ci.nsIURI) {
       this._itemId = -1;
       this._uri = aFor;
       this._readOnly = true;
     }
     else {
       this._itemId = aFor;
+      // We can't store information on invalid itemIds.
+      this._readOnly = this._readOnly || this._itemId == -1;
+
       var containerId = PlacesUtils.bookmarks.getFolderIdForItem(this._itemId);
       this._itemType = PlacesUtils.bookmarks.getItemType(this._itemId);
       if (this._itemType == Ci.nsINavBookmarksService.TYPE_BOOKMARK) {
         this._uri = PlacesUtils.bookmarks.getBookmarkURI(this._itemId);
-        if (!this._readOnly) // If readOnly wasn't forced through aInfo
-          this._readOnly = PlacesUtils.itemIsLivemark(containerId);
         this._initTextField("keywordField",
                             PlacesUtils.bookmarks
                                        .getKeywordForBookmark(this._itemId));
-        // Load In Sidebar checkbox
         this._element("loadInSidebarCheckbox").checked =
           PlacesUtils.annotations.itemHasAnnotation(this._itemId,
                                                     PlacesUIUtils.LOAD_IN_SIDEBAR_ANNO);
       }
       else {
-        if (!this._readOnly) // If readOnly wasn't forced through aInfo
-          this._readOnly = false;
-
         this._uri = null;
-        this._isLivemark = PlacesUtils.itemIsLivemark(this._itemId);
-        if (this._isLivemark) {
-          var feedURI = PlacesUtils.livemarks.getFeedURI(this._itemId);
-          var siteURI = PlacesUtils.livemarks.getSiteURI(this._itemId);
-          this._initTextField("feedLocationField", feedURI.spec);
-          this._initTextField("siteLocationField", siteURI ? siteURI.spec : "");
-        }
+        this._isLivemark = false;
+        PlacesUtils.livemarks.getLivemark(
+          {id: this._itemId },
+          (function (aStatus, aLivemark) {
+            if (Components.isSuccessCode(aStatus)) {
+              this._isLivemark = true;
+              this._initTextField("feedLocationField", aLivemark.feedURI.spec, true);
+              this._initTextField("siteLocationField", aLivemark.siteURI ? aLivemark.siteURI.spec : "", true);
+              this._showHideRows();
+            }
+          }).bind(this)
+        );
       }
 
       // folder picker
       this._initFolderMenuList(containerId);
 
       // description field
       this._initTextField("descriptionField", 
                           PlacesUIUtils.getItemDescription(this._itemId));
@@ -374,20 +346,27 @@ var gEditItemOverlay = {
     throw Cr.NS_ERROR_NO_INTERFACE;
   },
 
   _element: function EIO__element(aID) {
     return document.getElementById("editBMPanel_" + aID);
   },
 
   _getItemStaticTitle: function EIO__getItemStaticTitle() {
-    if (this._itemId == -1)
-      return PlacesUtils.history.getPageTitle(this._uri);
+    if (this._titleOverride)
+      return this._titleOverride;
 
-    return PlacesUtils.bookmarks.getItemTitle(this._itemId);
+    let title = "";
+    if (this._itemId == -1) {
+      title = PlacesUtils.history.getPageTitle(this._uri);
+    }
+    else {
+      title = PlacesUtils.bookmarks.getItemTitle(this._itemId);
+    }
+    return title;
   },
 
   _initNamePicker: function EIO_initNamePicker() {
     var namePicker = this._element("namePicker");
     namePicker.value = this._getItemStaticTitle();
     namePicker.readOnly = this._readOnly;
 
     // clear the undo stack
@@ -420,16 +399,18 @@ var gEditItemOverlay = {
     this._uri = null;
     this._uris = [];
     this._tags = [];
     this._allTags = [];
     this._itemIds = [];
     this._multiEdit = false;
     this._firstEditedField = "";
     this._initialized = false;
+    this._titleOverride = "";
+    this._readOnly = false;
   },
 
   onTagsFieldBlur: function EIO_onTagsFieldBlur() {
     if (this._updateTags()) // if anything has changed
       this._mayUpdateFirstEditField("tagsField");
   },
 
   _updateTags: function EIO__updateTags() {
@@ -592,46 +573,16 @@ var gEditItemOverlay = {
   onKeywordFieldBlur: function EIO_onKeywordFieldBlur() {
     var keyword = this._element("keywordField").value;
     if (keyword != PlacesUtils.bookmarks.getKeywordForBookmark(this._itemId)) {
       var txn = PlacesUIUtils.ptm.editBookmarkKeyword(this._itemId, keyword);
       PlacesUIUtils.ptm.doTransaction(txn);
     }
   },
 
-  onFeedLocationFieldBlur: function EIO_onFeedLocationFieldBlur() {
-    var uri;
-    try {
-      uri = PlacesUIUtils.createFixedURI(this._element("feedLocationField").value);
-    }
-    catch(ex) { return; }
-
-    var currentFeedURI = PlacesUtils.livemarks.getFeedURI(this._itemId);
-    if (!currentFeedURI.equals(uri)) {
-      var txn = PlacesUIUtils.ptm.editLivemarkFeedURI(this._itemId, uri);
-      PlacesUIUtils.ptm.doTransaction(txn);
-    }
-  },
-
-  onSiteLocationFieldBlur: function EIO_onSiteLocationFieldBlur() {
-    var uri = null;
-    try {
-      uri = PlacesUIUtils.createFixedURI(this._element("siteLocationField").value);
-    }
-    catch(ex) {  }
-
-    var currentSiteURI = PlacesUtils.livemarks.getSiteURI(this._itemId);
-    if ((!uri && !currentSiteURI) ||
-        (uri && currentSiteURI && currentSiteURI.equals(uri))) {
-      return;
-    }
-    var txn = PlacesUIUtils.ptm.editLivemarkSiteURI(this._itemId, uri);
-    PlacesUIUtils.ptm.doTransaction(txn);
-  },
-
   onLoadInSidebarCheckboxCommand:
   function EIO_onLoadInSidebarCheckboxCommand() {
     var loadInSidebarChecked = this._element("loadInSidebarCheckbox").checked;
     var txn = PlacesUIUtils.ptm.setLoadInSidebar(this._itemId,
                                                  loadInSidebarChecked);
     PlacesUIUtils.ptm.doTransaction(txn);
   },
 
@@ -1014,25 +965,29 @@ var gEditItemOverlay = {
                           PlacesUIUtils.getItemDescription(this._itemId));
       break;
     case PlacesUIUtils.LOAD_IN_SIDEBAR_ANNO:
       this._element("loadInSidebarCheckbox").checked =
         PlacesUtils.annotations.itemHasAnnotation(this._itemId,
                                                   PlacesUIUtils.LOAD_IN_SIDEBAR_ANNO);
       break;
     case PlacesUtils.LMANNO_FEEDURI:
-      var feedURISpec = PlacesUtils.livemarks.getFeedURI(this._itemId).spec;
-      this._initTextField("feedLocationField", feedURISpec);
+      let feedURISpec =
+        PlacesUtils.annotations.getItemAnnotation(this._itemId,
+                                                  PlacesUtils.LMANNO_FEEDURI);
+      this._initTextField("feedLocationField", feedURISpec, true);
       break;
     case PlacesUtils.LMANNO_SITEURI:
-      var siteURISpec = "";
-      var siteURI = PlacesUtils.livemarks.getSiteURI(this._itemId);
-      if (siteURI)
-        siteURISpec = siteURI.spec;
-      this._initTextField("siteLocationField", siteURISpec);
+      let siteURISpec = "";
+      try {
+        siteURISpec =
+          PlacesUtils.annotations.getItemAnnotation(this._itemId,
+                                                    PlacesUtils.LMANNO_SITEURI);
+      } catch (ex) {}
+      this._initTextField("siteLocationField", siteURISpec, true);
       break;
     }
   },
 
   onItemMoved: function EIO_onItemMoved(aItemId, aOldParent, aOldIndex,
                                         aNewParent, aNewIndex, aItemType) {
     if (aItemId != this._itemId ||
         aNewParent == this._getFolderIdFromMenuList())
--- a/browser/components/places/content/places.js
+++ b/browser/components/places/content/places.js
@@ -595,19 +595,17 @@ var PlacesOrganizer = {
     var infoBoxExpanderWrapper = document.getElementById("infoBoxExpanderWrapper");
     var additionalInfoBroadcaster = document.getElementById("additionalInfoBroadcaster");
 
     if (!aNode) {
       infoBoxExpanderWrapper.hidden = true;
       return;
     }
     if (aNode.itemId != -1 &&
-        ((PlacesUtils.nodeIsFolder(aNode) &&
-          !PlacesUtils.nodeIsLivemarkContainer(aNode)) ||
-         PlacesUtils.nodeIsLivemarkItem(aNode))) {
+        PlacesUtils.nodeIsFolder(aNode) && !aNode._feedURI) {
       if (infoBox.getAttribute("minimal") == "true")
         infoBox.setAttribute("wasminimal", "true");
       infoBox.removeAttribute("minimal");
       infoBoxExpanderWrapper.hidden = true;
     }
     else {
       if (infoBox.getAttribute("wasminimal") == "true")
         infoBox.setAttribute("minimal", "true");
@@ -683,18 +681,20 @@ var PlacesOrganizer = {
       var itemId = -1;
       if (concreteId != -1 && useConcreteId)
         itemId = concreteId;
       else if (aSelectedNode.itemId != -1)
         itemId = aSelectedNode.itemId;
       else
         itemId = PlacesUtils._uri(aSelectedNode.uri);
 
-      gEditItemOverlay.initPanel(itemId, { hiddenRows: ["folderPicker"],
-                                           forceReadOnly: readOnly });
+      gEditItemOverlay.initPanel(itemId, { hiddenRows: ["folderPicker"]
+                                         , forceReadOnly: readOnly
+                                         , titleOverride: aSelectedNode.title
+                                         });
 
       // Dynamically generated queries, like history date containers, have
       // itemId !=0 and do not exist in history.  For them the panel is
       // read-only, but empty, since it can't get a valid title for the object.
       // In such a case we force the title using the selectedNode one, for UI
       // polishness.
       if (aSelectedNode.itemId == -1 &&
           (PlacesUtils.nodeIsDay(aSelectedNode) ||
--- a/browser/components/places/content/treeView.js
+++ b/browser/components/places/content/treeView.js
@@ -1,46 +1,11 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is Mozilla History System
- *
- * The Initial Developer of the Original Code is
- * Google Inc.
- * Portions created by the Initial Developer are Copyright (C) 2005
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *   Brett Wilson <brettw@gmail.com> (Original author)
- *   Asaf Romano <mano@mozilla.com> (JavaScript version)
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
+/* 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/. */
 
 function PlacesTreeView(aFlatList, aOnOpenFlatContainer) {
   this._tree = null;
   this._result = null;
   this._selection = null;
   this._rootNode = null;
   this._rows = [];
   this._flatList = aFlatList;
@@ -124,16 +89,20 @@ PlacesTreeView.prototype = {
    *        A container result node.
    *
    * @return true if aContainer is a plain container, false otherwise.
    */
   _isPlainContainer: function PTV__isPlainContainer(aContainer) {
     if (aContainer._plainContainer !== undefined)
       return aContainer._plainContainer;
 
+    // Livemarks are always plain containers.
+    if (aContainer._feedURI)
+      return aContainer._plainContainer = true;
+
     // We don't know enough about non-query containers.
     if (!(aContainer instanceof Ci.nsINavHistoryQueryResultNode))
       return aContainer._plainContainer = false;
 
     switch (aContainer.queryOptions.resultType) {
       case Ci.nsINavHistoryQueryOptions.RESULTS_AS_DATE_QUERY:
       case Ci.nsINavHistoryQueryOptions.RESULTS_AS_SITE_QUERY:
       case Ci.nsINavHistoryQueryOptions.RESULTS_AS_DATE_SITE_QUERY:
@@ -323,17 +292,18 @@ PlacesTreeView.prototype = {
         }
       }
 
       this._rows[row] = curChild;
       rowsInserted++;
 
       // Recursively do containers.
       if (!this._flatList &&
-          curChild instanceof Ci.nsINavHistoryContainerResultNode) {
+          curChild instanceof Ci.nsINavHistoryContainerResultNode &&
+          !curChild._feedURI) {
         let resource = this._getResourceForNode(curChild);
         let isopen = resource != null &&
                      PlacesUIUtils.localStore.HasAssertion(resource,
                                                            openLiteral,
                                                            trueLiteral, true);
         if (isopen != curChild.containerOpen)
           aToOpen.push(curChild);
         else if (curChild.containerOpen && curChild.childCount > 0)
@@ -620,30 +590,30 @@ PlacesTreeView.prototype = {
 
       // Update parent when inserting the first item, since twisty has changed.
       if (aParentNode.childCount == 1)
         this._tree.invalidateRow(parentRow);
     }
 
     // Compute the new row number of the node.
     let row = -1;
-    if (aNewIndex == 0 || this._isPlainContainer(aParentNode)) {
+    let cc = aParentNode.childCount;
+    if (aNewIndex == 0 || this._isPlainContainer(aParentNode) || cc == 0) {
       // We don't need to worry about sub hierarchies of the parent node
       // if it's a plain container, or if the new node is its first child.
       if (aParentNode == this._rootNode)
         row = aNewIndex;
       else
         row = parentRow + aNewIndex + 1;
     }
     else {
       // Here, we try to find the next visible element in the child list so we
       // can set the new visible index to be right before that.  Note that we
       // have to search down instead of up, because some siblings could have
       // children themselves that would be in the way.
-      let cc = aParentNode.childCount;
       let separatorsAreHidden = PlacesUtils.nodeIsSeparator(aNode) &&
                                 this.isSorted();
       for (let i = aNewIndex + 1; i < cc; i++) {
         let node = aParentNode.getChild(i);
         if (!separatorsAreHidden || PlacesUtils.nodeIsSeparator(node)) {
           // The children have not been shifted so the next item will have what
           // should be our index.
           row = this._getRowForNode(node, false, parentRow, i);
@@ -659,18 +629,20 @@ PlacesTreeView.prototype = {
                                             aNewIndex - 1);
         row = prevIndex + this._countVisibleRowsForNodeAtRow(prevIndex);
       }
     }
 
     this._rows.splice(row, 0, aNode);
     this._tree.rowCountChanged(row, 1);
 
-    if (PlacesUtils.nodeIsContainer(aNode) && PlacesUtils.asContainer(aNode).containerOpen)
+    if (PlacesUtils.nodeIsContainer(aNode) &&
+        PlacesUtils.asContainer(aNode).containerOpen) {
       this.invalidateContainer(aNode);
+    }
   },
 
   /**
    * THIS FUNCTION DOES NOT HANDLE cases where a collapsed node is being
    * removed but the node it is collapsed with is not being removed (this then
    * just swap out the removee with its collapsing partner). The only time
    * when we really remove things is when deleting URIs, which will apply to
    * all collapsees. This function is called sometimes when resorting items.
@@ -809,64 +781,132 @@ PlacesTreeView.prototype = {
     if (aColumnType != this.COLUMN_TYPE_LASTMODIFIED) {
       let lastModifiedColumn =
         this._findColumnByType(this.COLUMN_TYPE_LASTMODIFIED);
       if (lastModifiedColumn && !lastModifiedColumn.hidden)
         this._tree.invalidateCell(row, lastModifiedColumn);
     }
   },
 
+  _populateLivemarkContainer: function PTV__populateLivemarkContainer(aNode) {
+    PlacesUtils.livemarks.getLivemark({ id: aNode.itemId },
+      (function (aStatus, aLivemark) {
+        let placesNode = aNode;
+        // Need to check containerOpen since getLivemark is async.
+        if (!Components.isSuccessCode(aStatus) || !placesNode.containerOpen)
+          return;
+
+        let children = aLivemark.getNodesForContainer(placesNode);
+        for (let i = 0; i < children.length; i++) {
+          let child = children[i];
+          this.nodeInserted(placesNode, child, i);
+        }
+      }).bind(this));
+  },
+
   nodeTitleChanged: function PTV_nodeTitleChanged(aNode, aNewTitle) {
     this._invalidateCellValue(aNode, this.COLUMN_TYPE_TITLE);
   },
 
   nodeURIChanged: function PTV_nodeURIChanged(aNode, aNewURI) {
     this._invalidateCellValue(aNode, this.COLUMN_TYPE_URI);
   },
 
   nodeIconChanged: function PTV_nodeIconChanged(aNode) {
     this._invalidateCellValue(aNode, this.COLUMN_TYPE_TITLE);
   },
 
   nodeHistoryDetailsChanged:
   function PTV_nodeHistoryDetailsChanged(aNode, aUpdatedVisitDate,
                                          aUpdatedVisitCount) {
+    if (aNode.parent && aNode.parent._feedURI) {
+      // Find the node in the parent.
+      let parentRow = this._flatList ? 0 : this._getRowForNode(aNode.parent);
+      for (let i = parentRow; i < this._rows.length; i++) {
+        let child = this.nodeForTreeIndex(i);
+        if (child.uri == aNode.uri) {
+          delete child._cellProperties;
+          this._invalidateCellValue(child, this.COLUMN_TYPE_TITLE);
+          break;
+        }
+      }
+      return;
+    }
+
     this._invalidateCellValue(aNode, this.COLUMN_TYPE_DATE);
     this._invalidateCellValue(aNode, this.COLUMN_TYPE_VISITCOUNT);
   },
 
   nodeTagsChanged: function PTV_nodeTagsChanged(aNode) {
     this._invalidateCellValue(aNode, this.COLUMN_TYPE_TAGS);
   },
 
   nodeKeywordChanged: function PTV_nodeKeywordChanged(aNode, aNewKeyword) {
     this._invalidateCellValue(aNode, this.COLUMN_TYPE_KEYWORD);
   },
 
   nodeAnnotationChanged: function PTV_nodeAnnotationChanged(aNode, aAnno) {
     if (aAnno == PlacesUIUtils.DESCRIPTION_ANNO) {
       this._invalidateCellValue(aNode, this.COLUMN_TYPE_DESCRIPTION);
-    } else if (aAnno == PlacesUtils.LMANNO_FEEDURI) {
-      // The livemark attribute is set as a cell property on the title cell.
-      this._invalidateCellValue(aNode, this.COLUMN_TYPE_TITLE);
+    }
+    else if (aAnno == PlacesUtils.LMANNO_FEEDURI) {
+      PlacesUtils.livemarks.getLivemark(
+        { id: aNode.itemId },
+        (function (aStatus, aLivemark) {
+          if (Components.isSuccessCode(aStatus)) {
+            aNode._feedURI = aLivemark.feedURI;
+            if (aNode._cellProperties) {
+              aNode._cellProperties.push(this._getAtomFor("livemark"));
+            }
+            // The livemark attribute is set as a cell property on the title cell.
+            this._invalidateCellValue(aNode, this.COLUMN_TYPE_TITLE);
+          }
+        }).bind(this)
+      );
     }
   },
 
   nodeDateAddedChanged: function PTV_nodeDateAddedChanged(aNode, aNewValue) {
     this._invalidateCellValue(aNode, this.COLUMN_TYPE_DATEADDED);
   },
 
   nodeLastModifiedChanged:
   function PTV_nodeLastModifiedChanged(aNode, aNewValue) {
     this._invalidateCellValue(aNode, this.COLUMN_TYPE_LASTMODIFIED);
   },
 
   containerStateChanged:
   function PTV_containerStateChanged(aNode, aOldState, aNewState) {
     this.invalidateContainer(aNode);
+
+    if (PlacesUtils.nodeIsFolder(aNode) ||
+        (this._flatList && aNode == this._rootNode)) {
+      let queryOptions = PlacesUtils.asQuery(this._rootNode).queryOptions;
+      if (queryOptions.excludeItems) {
+        return;
+      }
+
+      PlacesUtils.livemarks.getLivemark({ id: aNode.itemId },
+        (function (aStatus, aLivemark) {
+          if (Components.isSuccessCode(aStatus)) {
+            let shouldInvalidate = !aNode._feedURI;
+            aNode._feedURI = aLivemark.feedURI;
+            if (aNewState == Components.interfaces.nsINavHistoryContainerResultNode.STATE_OPENED) {
+              aLivemark.registerForUpdates(aNode, this);
+              aLivemark.reload();
+              if (shouldInvalidate)
+                this.invalidateContainer(aNode);
+            }
+            else {
+              aLivemark.unregisterForUpdates(aNode);
+            }
+          }
+        }).bind(this)
+      );
+    }
   },
 
   invalidateContainer: function PTV_invalidateContainer(aContainer) {
     NS_ASSERT(this._result, "Need to have a result to update");
     if (!this._tree)
       return;
 
     let startReplacement, replaceCount;
@@ -950,16 +990,23 @@ PlacesTreeView.prototype = {
 
         // If we don't have a parent, we made it all the way to the root
         // and didn't find a match, so we can open our item.
         if (!parent && !item.containerOpen)
           item.containerOpen = true;
       }
     }
 
+    if (aContainer._feedURI) {
+      let queryOptions = PlacesUtils.asQuery(this._result.root).queryOptions;
+      if (!queryOptions.excludeItems) {
+        this._populateLivemarkContainer(aContainer);
+      }
+    }
+
     this._tree.endUpdateBatch();
 
     // Restore selection.
     this._restoreSelection(nodesToReselect, aContainer);
     this.selection.selectEventsSuppressed = false;
   },
 
   _columns: [],
@@ -1104,33 +1151,50 @@ PlacesTreeView.prototype = {
             properties.push(this._getAtomFor("tagContainer"));
           else if (PlacesUtils.nodeIsDay(node))
             properties.push(this._getAtomFor("dayContainer"));
           else if (PlacesUtils.nodeIsHost(node))
             properties.push(this._getAtomFor("hostContainer"));
         }
         else if (nodeType == Ci.nsINavHistoryResultNode.RESULT_TYPE_FOLDER ||
                  nodeType == Ci.nsINavHistoryResultNode.RESULT_TYPE_FOLDER_SHORTCUT) {
-          if (PlacesUtils.nodeIsLivemarkContainer(node))
+          if (node._feedURI) {
             properties.push(this._getAtomFor("livemark"));
+          }
+          else {
+            PlacesUtils.livemarks.getLivemark(
+              { id: node.itemId },
+              (function (aStatus, aLivemark) {
+                if (Components.isSuccessCode(aStatus)) {
+                  node._feedURI = aLivemark.feedURI;
+                  node._cellProperties.push(this._getAtomFor("livemark"));
+                  // The livemark attribute is set as a cell property on the title cell.
+                  this._invalidateCellValue(node, this.COLUMN_TYPE_TITLE);
+                }
+              }).bind(this)
+            );
+          }
         }
 
         if (itemId != -1) {
           let queryName = PlacesUIUtils.getLeftPaneQueryNameFromId(itemId);
           if (queryName)
             properties.push(this._getAtomFor("OrganizerQuery_" + queryName));
         }
       }
       else if (nodeType == Ci.nsINavHistoryResultNode.RESULT_TYPE_SEPARATOR)
         properties.push(this._getAtomFor("separator"));
       else if (PlacesUtils.nodeIsURI(node)) {
         properties.push(this._getAtomFor(PlacesUIUtils.guessUrlSchemeForUI(node.uri)));
-        if (itemId != -1) {
-          if (PlacesUtils.nodeIsLivemarkContainer(node.parent))
-            properties.push(this._getAtomFor("livemarkItem"));
+
+        if (node.parent._feedURI) {
+          properties.push(this._getAtomFor("livemarkItem"));
+          if (node.accessCount) {
+            properties.push(this._getAtomFor("visited"));
+          }
         }
       }
 
       node._cellProperties = properties;
     }
     for (let i = 0; i < node._cellProperties.length; i++)
       aProperties.AppendElement(node._cellProperties[i]);
   },
@@ -1149,17 +1213,17 @@ PlacesTreeView.prototype = {
       if (this._flatList)
         return true;
 
       // treat non-expandable childless queries as non-containers
       if (PlacesUtils.nodeIsQuery(node)) {
         let parent = node.parent;
         if ((PlacesUtils.nodeIsQuery(parent) ||
              PlacesUtils.nodeIsFolder(parent)) &&
-            !node.hasChildren)
+            !PlacesUtils.asQuery(node).hasChildren)
           return PlacesUtils.asQuery(parent).queryOptions.expandQueries;
       }
       return true;
     }
     return false;
   },
 
   isContainerOpen: function PTV_isContainerOpen(aRow) {
@@ -1169,18 +1233,24 @@ PlacesTreeView.prototype = {
     // All containers are listed in the rows array.
     return this._rows[aRow].containerOpen;
   },
 
   isContainerEmpty: function PTV_isContainerEmpty(aRow) {
     if (this._flatList)
       return true;
 
+    let node = this._rows[aRow];
+    if (node._feedURI) {
+      let queryOptions = PlacesUtils.asQuery(this._result.root).queryOptions;
+      return queryOptions.excludeItems;
+    }
+
     // All containers are listed in the rows array.
-    return !this._rows[aRow].hasChildren;
+    return !node.hasChildren;
   },
 
   isSeparator: function PTV_isSeparator(aRow) {
     // All separators are listed in the rows array.
     let node = this._rows[aRow];
     return node && PlacesUtils.nodeIsSeparator(node);
   },
 
@@ -1413,25 +1483,28 @@ PlacesTreeView.prototype = {
       throw Cr.NS_ERROR_UNEXPECTED;
 
     let node = this._rows[aRow];
     if (this._flatList && this._openContainerCallback) {
       this._openContainerCallback(node);
       return;
     }
 
-    let resource = this._getResourceForNode(node);
-    if (resource) {
-      const openLiteral = PlacesUIUtils.RDF.GetResource("http://home.netscape.com/NC-rdf#open");
-      const trueLiteral = PlacesUIUtils.RDF.GetLiteral("true");
+    // Persist containers open status, but never persist livemarks.
+    if (!node._feedURI) {
+      let resource = this._getResourceForNode(node);
+      if (resource) {
+        const openLiteral = PlacesUIUtils.RDF.GetResource("http://home.netscape.com/NC-rdf#open");
+        const trueLiteral = PlacesUIUtils.RDF.GetLiteral("true");
 
-      if (node.containerOpen)
-        PlacesUIUtils.localStore.Unassert(resource, openLiteral, trueLiteral);
-      else
-        PlacesUIUtils.localStore.Assert(resource, openLiteral, trueLiteral, true);
+        if (node.containerOpen)
+          PlacesUIUtils.localStore.Unassert(resource, openLiteral, trueLiteral);
+        else
+          PlacesUIUtils.localStore.Assert(resource, openLiteral, trueLiteral, true);
+      }
     }
 
     node.containerOpen = !node.containerOpen;
   },
 
   cycleHeader: function PTV_cycleHeader(aColumn) {
     if (!this._result)
       throw Cr.NS_ERROR_UNEXPECTED;
@@ -1564,20 +1637,18 @@ PlacesTreeView.prototype = {
     // Only bookmark-nodes are editable, and those are never built lazily
     let node = this._rows[aRow];
     if (!node || node.itemId == -1)
       return false;
 
     // The following items are never editable:
     // * Read-only items.
     // * places-roots
-    // * livemark items
     // * separators
     if (PlacesUtils.nodeIsReadOnly(node) ||
-        PlacesUtils.nodeIsLivemarkItem(node) ||
         PlacesUtils.nodeIsSeparator(node))
       return false;
 
     if (PlacesUtils.nodeIsFolder(node)) {
       let itemId = PlacesUtils.getConcreteItemId(node);
       if (PlacesUtils.isRootItem(itemId))
         return false;
     }
--- a/browser/components/places/tests/browser/browser_457473_no_copy_guid.js
+++ b/browser/components/places/tests/browser/browser_457473_no_copy_guid.js
@@ -36,17 +36,17 @@
  * ***** END LICENSE BLOCK ***** */
 
 function test() {
   // sanity check
   ok(PlacesUtils, "checking PlacesUtils, running in chrome context?");
   ok(PlacesUIUtils, "checking PlacesUIUtils, running in chrome context?");
 
   /*
-  - create, a test folder, add bookmark, separator, livemark to it
+  - create, a test folder, add bookmark, separator to it
   - fetch guids for all
   - copy the folder
   - test that guids are all different
   - undo copy
   - redo copy
   - test that guids for the copy stay the same
   */
 
@@ -61,20 +61,16 @@ function test() {
   testRootNode.containerOpen = true;
   is(testRootNode.childCount, 0, "confirm test root node is a container, and is empty");
 
   // create folder A, fill it w/ each item type
   var folderAId = PlacesUtils.bookmarks.createFolder(testRootId, "A", -1);
   PlacesUtils.bookmarks.insertBookmark(folderAId, PlacesUtils._uri("http://foo"),
                                        -1, "test bookmark");
   PlacesUtils.bookmarks.insertSeparator(folderAId, -1);
-  PlacesUtils.livemarks.createLivemarkFolderOnly(folderAId, "test livemark",
-                                                 PlacesUtils._uri("http://test"),
-                                                 PlacesUtils._uri("http://test"), -1);
-
   var folderANode = testRootNode.getChild(0);
   var folderAGUIDs = getGUIDs(folderANode);
 
   // test the test function
   ok(checkGUIDs(folderANode, folderAGUIDs, true), "confirm guid test works");
 
   // serialize the folder
   var serializedNode = PlacesUtils.wrapNode(folderANode, PlacesUtils.TYPE_X_MOZ_PLACE_CONTAINER);
@@ -120,18 +116,17 @@ function test() {
 }
 
 function getGUIDs(aNode) {
   PlacesUtils.asContainer(aNode);
   aNode.containerOpen = true;
   var GUIDs = {
     folder: PlacesUtils.bookmarks.getItemGUID(aNode.itemId),
     bookmark: PlacesUtils.bookmarks.getItemGUID(aNode.getChild(0).itemId),
-    separator: PlacesUtils.bookmarks.getItemGUID(aNode.getChild(1).itemId),
-    livemark: PlacesUtils.bookmarks.getItemGUID(aNode.getChild(2).itemId)
+    separator: PlacesUtils.bookmarks.getItemGUID(aNode.getChild(1).itemId)
   };
   aNode.containerOpen = false;
   return GUIDs;
 }
 
 function checkGUIDs(aFolderNode, aGUIDs, aShouldMatch) {
 
   function check(aNode, aGUID, aEquals) {
@@ -139,14 +134,13 @@ function checkGUIDs(aFolderNode, aGUIDs,
     return aEquals ? (nodeGUID == aGUID) : (nodeGUID != aGUID);
   }
 
   PlacesUtils.asContainer(aFolderNode);
   aFolderNode.containerOpen = true;
 
   var allMatch = check(aFolderNode, aGUIDs.folder, aShouldMatch) &&
                  check(aFolderNode.getChild(0), aGUIDs.bookmark, aShouldMatch) &&
-                 check(aFolderNode.getChild(1), aGUIDs.separator, aShouldMatch) &&
-                 check(aFolderNode.getChild(2), aGUIDs.livemark, aShouldMatch);
+                 check(aFolderNode.getChild(1), aGUIDs.separator, aShouldMatch)
 
   aFolderNode.containerOpen = false;
   return allMatch;
 }
--- a/browser/components/places/tests/browser/browser_library_views_liveupdate.js
+++ b/browser/components/places/tests/browser/browser_library_views_liveupdate.js
@@ -90,21 +90,16 @@ function startTest() {
   bs.setItemTitle(id, "bmf_edited");
   addedBookmarks.push(id);
   id = bs.insertBookmark(id,
                          PlacesUtils._uri("http://bmf1.mozilla.org/"),
                          bs.DEFAULT_INDEX,
                          "bmf1");
   addedBookmarks.push(id);
   bs.moveItem(id, bs.bookmarksMenuFolder, 0);
-  id = PlacesUtils.livemarks.createLivemarkFolderOnly(
-    bs.bookmarksMenuFolder, "bml",
-    PlacesUtils._uri("http://bml.siteuri.mozilla.org/"),
-    PlacesUtils._uri("http://bml.feeduri.mozilla.org/"), bs.DEFAULT_INDEX);
-  addedBookmarks.push(id);
 
   // TOOLBAR
   ok(true, "*** Acting on toolbar bookmarks");
   bs.insertBookmark(bs.toolbarFolder,
                     PlacesUtils._uri("http://tb1.mozilla.org/"),
                     bs.DEFAULT_INDEX,
                     "tb1");
   bs.setItemTitle(id, "tb1_edited");
@@ -123,20 +118,16 @@ function startTest() {
   bs.setItemTitle(id, "tbf_edited");
   addedBookmarks.push(id);
   id = bs.insertBookmark(id,
                          PlacesUtils._uri("http://tbf1.mozilla.org/"),
                          bs.DEFAULT_INDEX,
                          "bmf1");
   addedBookmarks.push(id);
   bs.moveItem(id, bs.toolbarFolder, 0);
-  id = PlacesUtils.livemarks.createLivemarkFolderOnly(
-    bs.toolbarFolder, "tbl", PlacesUtils._uri("http://tbl.siteuri.mozilla.org/"),
-    PlacesUtils._uri("http://tbl.feeduri.mozilla.org/"), bs.DEFAULT_INDEX);
-  addedBookmarks.push(id);
 
   // UNSORTED
   ok(true, "*** Acting on unsorted bookmarks");
   id = bs.insertBookmark(bs.unfiledBookmarksFolder,
                          PlacesUtils._uri("http://ub1.mozilla.org/"),
                          bs.DEFAULT_INDEX,
                          "ub1");
   bs.setItemTitle(id, "ub1_edited");
@@ -155,21 +146,16 @@ function startTest() {
   bs.setItemTitle(id, "ubf_edited");
   addedBookmarks.push(id);
   id = bs.insertBookmark(id,
                          PlacesUtils._uri("http://ubf1.mozilla.org/"),
                          bs.DEFAULT_INDEX,
                          "ubf1");
   addedBookmarks.push(id);
   bs.moveItem(id, bs.unfiledBookmarksFolder, 0);
-  id = PlacesUtils.livemarks.createLivemarkFolderOnly(
-    bs.unfiledBookmarksFolder, "bubl",
-    PlacesUtils._uri("http://bubl.siteuri.mozilla.org/"),
-    PlacesUtils._uri("http://bubl.feeduri.mozilla.org/"), bs.DEFAULT_INDEX);
-  addedBookmarks.push(id);
 
   // Remove all added bookmarks.
   addedBookmarks.forEach(function (aItem) {
     // If we remove an item after its containing folder has been removed,
     // this will throw, but we can ignore that.
     try {
       bs.removeItem(aItem);
     } catch (ex) {}
@@ -196,37 +182,17 @@ function finishTest() {
  */
 var bookmarksObserver = {
   QueryInterface: XPCOMUtils.generateQI([
     Ci.nsINavBookmarkObserver
   , Ci.nsIAnnotationObserver
   ]),
 
   // nsIAnnotationObserver
-  onItemAnnotationSet: function(aItemId, aAnnotationName) {
-    if (aAnnotationName == PlacesUtils.LMANNO_FEEDURI) {
-      // Check that item is recognized as a livemark.
-      let validator = function(aTreeRowIndex) {
-        let tree = gLibrary.PlacesOrganizer._places;
-        let livemarkAtom = Cc["@mozilla.org/atom-service;1"].
-                           getService(Ci.nsIAtomService).
-                           getAtom("livemark");
-        let properties = Cc["@mozilla.org/supports-array;1"].
-                         createInstance(Ci.nsISupportsArray);
-        tree.view.getCellProperties(aTreeRowIndex,
-                                    tree.columns.getColumnAt(0),
-                                    properties);
-        return properties.GetIndexOf(livemarkAtom) != -1;
-      };
-
-      var [node, index, valid] = getNodeForTreeItem(aItemId, gLibrary.PlacesOrganizer._places, validator);
-      isnot(node, null, "Found new Places node in left pane at " + index);
-      ok(valid, "Node is recognized as a livemark");
-    }
-  },
+  onItemAnnotationSet: function() {},
   onItemAnnotationRemoved: function() {},
   onPageAnnotationSet: function() {},
   onPageAnnotationRemoved: function() {},
 
   // nsINavBookmarkObserver
   onItemAdded: function PSB_onItemAdded(aItemId, aFolderId, aIndex, aItemType,
                                         aURI) {
     var node = null;
--- a/browser/components/places/tests/browser/browser_views_liveupdate.js
+++ b/browser/components/places/tests/browser/browser_views_liveupdate.js
@@ -113,21 +113,16 @@ function startTest() {
   addedBookmarks.push(id);
   id = bs.insertBookmark(id,
                          PlacesUtils._uri("http://bmf1.mozilla.org/"),
                          bs.DEFAULT_INDEX,
                          "bmf1");
   bs.setItemTitle(id, "bmf1_edited");
   addedBookmarks.push(id);
   bs.moveItem(id, bs.bookmarksMenuFolder, 0);
-  id = PlacesUtils.livemarks.createLivemarkFolderOnly(
-    bs.bookmarksMenuFolder, "bml",
-    PlacesUtils._uri("http://bml.siteuri.mozilla.org/"),
-    PlacesUtils._uri("http://bml.feeduri.mozilla.org/"), bs.DEFAULT_INDEX);
-  addedBookmarks.push(id);
 
   // TOOLBAR
   info("*** Acting on toolbar bookmarks");
   id = bs.insertBookmark(bs.toolbarFolder,
                          PlacesUtils._uri("http://tb1.mozilla.org/"),
                          bs.DEFAULT_INDEX,
                          "tb1");
   bs.setItemTitle(id, "tb1_edited");
@@ -149,20 +144,16 @@ function startTest() {
   addedBookmarks.push(id);
   id = bs.insertBookmark(id,
                          PlacesUtils._uri("http://tbf1.mozilla.org/"),
                          bs.DEFAULT_INDEX,
                          "tbf1");
   bs.setItemTitle(id, "tbf1_edited");
   addedBookmarks.push(id);
   bs.moveItem(id, bs.toolbarFolder, 0);
-  id = PlacesUtils.livemarks.createLivemarkFolderOnly(
-    bs.toolbarFolder, "tbl", PlacesUtils._uri("http://tbl.siteuri.mozilla.org/"),
-    PlacesUtils._uri("http://tbl.feeduri.mozilla.org/"), bs.DEFAULT_INDEX);
-  addedBookmarks.push(id);
 
   // UNSORTED
   info("*** Acting on unsorted bookmarks");
   id = bs.insertBookmark(bs.unfiledBookmarksFolder,
                          PlacesUtils._uri("http://ub1.mozilla.org/"),
                          bs.DEFAULT_INDEX,
                          "ub1");
   bs.setItemTitle(id, "ub1_edited");
@@ -182,21 +173,16 @@ function startTest() {
   addedBookmarks.push(id);
   id = bs.insertBookmark(id,
                          PlacesUtils._uri("http://ubf1.mozilla.org/"),
                          bs.DEFAULT_INDEX,
                          "bubf1");
   bs.setItemTitle(id, "bubf1_edited");
   addedBookmarks.push(id);
   bs.moveItem(id, bs.unfiledBookmarksFolder, 0);
-  id = PlacesUtils.livemarks.createLivemarkFolderOnly(
-    bs.unfiledBookmarksFolder, "bubl",
-    PlacesUtils._uri("http://bubl.siteuri.mozilla.org/"),
-    PlacesUtils._uri("http://bubl.feeduri.mozilla.org/"), bs.DEFAULT_INDEX);
-  addedBookmarks.push(id);
 
   // Remove all added bookmarks.
   addedBookmarks.forEach(function (aItem) {
     // If we remove an item after its containing folder has been removed,
     // this will throw, but we can ignore that.
     try {
       bs.removeItem(aItem);
     } catch (ex) {}
@@ -228,48 +214,17 @@ function finishTest() {
  */
 var bookmarksObserver = {
   QueryInterface: XPCOMUtils.generateQI([
     Ci.nsINavBookmarkObserver
   , Ci.nsIAnnotationObserver
   ]),
 
   // nsIAnnotationObserver
-  onItemAnnotationSet: function(aItemId, aAnnotationName) {
-    if (aAnnotationName == PlacesUtils.LMANNO_FEEDURI) {
-      var views = getViewsForFolder(PlacesUtils.bookmarks.getFolderIdForItem(aItemId));
-      ok(views.length > 0, "Found affected views (" + views.length + "): " + views);
-
-      // Check that item is recognized as a livemark.
-      let validator = function(aElementOrTreeIndex) {
-        if (typeof(aElementOrTreeIndex) == "number") {
-          var sidebar = document.getElementById("sidebar");
-          var tree = sidebar.contentDocument.getElementById("bookmarks-view");
-          let livemarkAtom = Cc["@mozilla.org/atom-service;1"].
-                             getService(Ci.nsIAtomService).
-                             getAtom("livemark");
-          let properties = Cc["@mozilla.org/supports-array;1"].
-                           createInstance(Ci.nsISupportsArray);
-          tree.view.getCellProperties(aElementOrTreeIndex,
-                                      tree.columns.getColumnAt(0),
-                                      properties);
-          return properties.GetIndexOf(livemarkAtom) != -1;
-        }
-        else {
-          return aElementOrTreeIndex.hasAttribute("livemark");
-        }
-      };
-
-      for (var i = 0; i < views.length; i++) {
-        var [node, index, valid] = searchItemInView(aItemId, views[i], validator);
-        isnot(node, null, "Found new Places node in " + views[i] + " at " + index);
-        ok(valid, "Node is recognized as a livemark");
-      }
-    }
-  },
+  onItemAnnotationSet: function() {},
   onItemAnnotationRemoved: function() {},
   onPageAnnotationSet: function() {},
   onPageAnnotationRemoved: function() {},
 
   // nsINavBookmarkObserver
   onItemAdded: function PSB_onItemAdded(aItemId, aFolderId, aIndex,
                                         aItemType, aURI) {
     var views = getViewsForFolder(aFolderId);
--- a/browser/components/places/tests/unit/head_bookmarks.js
+++ b/browser/components/places/tests/unit/head_bookmarks.js
@@ -97,15 +97,15 @@ let (XULAppInfo = {
 
 
 const FILENAME_BOOKMARKS_HTML = "bookmarks.html";
 let (backup_date = new Date().toLocaleFormat("%Y-%m-%d")) {
   const FILENAME_BOOKMARKS_JSON = "bookmarks-" + backup_date + ".json";
 }
 
 // Smart bookmarks constants.
-const SMART_BOOKMARKS_VERSION = 2;
+const SMART_BOOKMARKS_VERSION = 3;
 const SMART_BOOKMARKS_ON_TOOLBAR = 1;
 const SMART_BOOKMARKS_ON_MENU = 3; // Takes in count the additional separator.
 
 // Default bookmarks constants.
 const DEFAULT_BOOKMARKS_ON_TOOLBAR = 1;
 const DEFAULT_BOOKMARKS_ON_MENU = 1;
--- a/browser/components/places/tests/unit/test_384370.js
+++ b/browser/components/places/tests/unit/test_384370.js
@@ -31,29 +31,25 @@
  * use your version of this file under the terms of the MPL, indicate your
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
-// The following components need to be initialized to perform tests without
-// asserting in debug builds (Bug 448804).
-Cc["@mozilla.org/browser/livemark-service;2"].getService(Ci.nsILivemarkService);
-Cc["@mozilla.org/feed-processor;1"].createInstance(Ci.nsIFeedProcessor);
-
 const LOAD_IN_SIDEBAR_ANNO = "bookmarkProperties/loadInSidebar";
 const DESCRIPTION_ANNO = "bookmarkProperties/description";
 const POST_DATA_ANNO = "bookmarkProperties/POSTData";
 
 do_check_eq(typeof PlacesUtils, "object");
 
 // main
 function run_test() {
+  do_test_pending();
   /*
     HTML+FEATURES SUMMARY:
     - import legacy bookmarks
     - export as json, import, test (tests integrity of html > json)
     - export as html, import, test (tests integrity of json > html)
 
     BACKUP/RESTORE SUMMARY:
     - create a bookmark in each root
@@ -87,31 +83,35 @@ function run_test() {
   // 2. run the test-suite
   // Note: we do not empty the db before this import to catch bugs like 380999
   try {
     importer.importHTMLFromFile(bookmarksFileOld, true);
   } catch(ex) { do_throw("couldn't import legacy bookmarks file: " + ex); }
   populate();
   validate();
 
-  // Test exporting a Places canonical json file.
-  // 1. export to bookmarks.exported.json
-  // 2. empty bookmarks db
-  // 3. import bookmarks.exported.json
-  // 4. run the test-suite
-  try {
-    PlacesUtils.backups.saveBookmarksToJSONFile(jsonFile);
-  } catch(ex) { do_throw("couldn't export to file: " + ex); }
-  LOG("exported json"); 
-  try {
-    PlacesUtils.restoreBookmarksFromJSONFile(jsonFile);
-  } catch(ex) { do_throw("couldn't import the exported file: " + ex); }
-  LOG("imported json"); 
-  validate();
-  LOG("validated import"); 
+  waitForAsyncUpdates(function () {
+    // Test exporting a Places canonical json file.
+    // 1. export to bookmarks.exported.json
+    // 2. empty bookmarks db
+    // 3. import bookmarks.exported.json
+    // 4. run the test-suite
+    try {
+      PlacesUtils.backups.saveBookmarksToJSONFile(jsonFile);
+    } catch(ex) { do_throw("couldn't export to file: " + ex); }
+    LOG("exported json");
+    try {
+      PlacesUtils.restoreBookmarksFromJSONFile(jsonFile);
+    } catch(ex) { do_throw("couldn't import the exported file: " + ex); }
+    LOG("imported json");
+    validate();
+    LOG("validated import");
+
+    waitForAsyncUpdates(do_test_finished);
+  });
 }
 
 var tagData = [
   { uri: uri("http://slint.us"), tags: ["indie", "kentucky", "music"] },
   { uri: uri("http://en.wikipedia.org/wiki/Diplodocus"), tags: ["dinosaur", "dj", "rad word"] }
 ];
 
 var bookmarkData = [
@@ -238,24 +238,27 @@ function testToolbarFolder() {
 
   // child count (add 2 for pre-existing items)
   do_check_eq(toolbar.childCount, bookmarkData.length + 2);
   
   // livemark
   var livemark = toolbar.getChild(1);
   // title
   do_check_eq("Latest Headlines", livemark.title);
-  // livemark check
-  do_check_true(PlacesUtils.livemarks.isLivemark(livemark.itemId));
-  // site url
-  do_check_eq("http://en-us.fxfeeds.mozilla.com/en-US/firefox/livebookmarks/",
-              PlacesUtils.livemarks.getSiteURI(livemark.itemId).spec);
-  // feed url
-  do_check_eq("http://en-us.fxfeeds.mozilla.com/en-US/firefox/headlines.xml",
-              PlacesUtils.livemarks.getFeedURI(livemark.itemId).spec);
+
+  PlacesUtils.livemarks.getLivemark(
+    { id: livemark.itemId },
+    function (aStatus, aLivemark) {
+      do_check_true(Components.isSuccessCode(aStatus));
+      do_check_eq("http://en-us.fxfeeds.mozilla.com/en-US/firefox/livebookmarks/",
+                  aLivemark.siteURI.spec);
+      do_check_eq("http://en-us.fxfeeds.mozilla.com/en-US/firefox/headlines.xml",
+                  aLivemark.feedURI.spec);
+    }
+  );
 
   // test added bookmark data
   var child = toolbar.getChild(2);
   do_check_eq(child.uri, bookmarkData[0].uri.spec);
   do_check_eq(child.title, bookmarkData[0].title);
   child = toolbar.getChild(3);
   do_check_eq(child.uri, bookmarkData[1].uri.spec);
   do_check_eq(child.title, bookmarkData[1].title);
--- a/browser/components/places/tests/unit/test_457441-import-export-corrupt-bookmarks-html.js
+++ b/browser/components/places/tests/unit/test_457441-import-export-corrupt-bookmarks-html.js
@@ -44,78 +44,80 @@
 // Get Services
 var hs = Cc["@mozilla.org/browser/nav-history-service;1"].
          getService(Ci.nsINavHistoryService);
 var dbConn = hs.QueryInterface(Ci.nsPIPlacesDatabase).DBConnection;
 var bs = Cc["@mozilla.org/browser/nav-bookmarks-service;1"].
          getService(Ci.nsINavBookmarksService);
 var as = Cc["@mozilla.org/browser/annotation-service;1"].
          getService(Ci.nsIAnnotationService);
-var lms = Cc["@mozilla.org/browser/livemark-service;2"].
-          getService(Ci.nsILivemarkService);
 var icos = Cc["@mozilla.org/browser/favicon-service;1"].
            getService(Ci.nsIFaviconService);
 var ps = Cc["@mozilla.org/preferences-service;1"].
          getService(Ci.nsIPrefBranch);
 var ies = Cc["@mozilla.org/browser/places/import-export-service;1"].
           getService(Ci.nsIPlacesImportExportService);
 
 const DESCRIPTION_ANNO = "bookmarkProperties/description";
 const LOAD_IN_SIDEBAR_ANNO = "bookmarkProperties/loadInSidebar";
 const POST_DATA_ANNO = "bookmarkProperties/POSTData";
 
 const TEST_FAVICON_PAGE_URL = "http://en-US.www.mozilla.com/en-US/firefox/central/";
 const TEST_FAVICON_DATA_URL = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAHWSURBVHjaYvz//z8DJQAggJiQOe/fv2fv7Oz8rays/N+VkfG/iYnJfyD/1+rVq7ffu3dPFpsBAAHEAHIBCJ85c8bN2Nj4vwsDw/8zQLwKiO8CcRoQu0DxqlWrdsHUwzBAAIGJmTNnPgYa9j8UqhFElwPxf2MIDeIrKSn9FwSJoRkAEEAM0DD4DzMAyPi/G+QKY4hh5WAXGf8PDQ0FGwJ22d27CjADAAIIrLmjo+MXA9R2kAHvGBA2wwx6B8W7od6CeQcggKCmCEL8bgwxYCbUIGTDVkHDBia+CuotgACCueD3TDQN75D4xmAvCoK9ARMHBzAw0AECiBHkAlC0Mdy7x9ABNA3obAZXIAa6iKEcGlMVQHwWyjYuL2d4v2cPg8vZswx7gHyAAAK7AOif7SAbOqCmn4Ha3AHFsIDtgPq/vLz8P4MSkJ2W9h8ggBjevXvHDo4FQUQg/kdypqCg4H8lUIACnQ/SOBMYI8bAsAJFPcj1AAEEjwVQqLpAbXmH5BJjqI0gi9DTAAgDBBCcAVLkgmQ7yKCZxpCQxqUZhAECCJ4XgMl493ug21ZD+aDAXH0WLM4A9MZPXJkJIIAwTAR5pQMalaCABQUULttBGCCAGCnNzgABBgAMJ5THwGvJLAAAAABJRU5ErkJggg==";
 
-// main
 function run_test() {
+  do_test_pending();
+
   // avoid creating the places smart folder during tests
   ps.setIntPref("browser.places.smartBookmarksVersion", -1);
 
   // import bookmarks from corrupt file
   var corruptBookmarksFile = do_get_file("bookmarks.corrupt.html");
   try {
     ies.importHTMLFromFile(corruptBookmarksFile, true);
   } catch(ex) { do_throw("couldn't import corrupt bookmarks file: " + ex); }
 
   // Check that every bookmark is correct
   // Corrupt bookmarks should not have been imported
   database_check();
-
-  // Create corruption in database
-  var corruptItemId = bs.insertBookmark(bs.toolbarFolder,
-                                        uri("http://test.mozilla.org"),
-                                        bs.DEFAULT_INDEX, "We love belugas");
-  var stmt = dbConn.createStatement("UPDATE moz_bookmarks SET fk = NULL WHERE id = :itemId");
-  stmt.params.itemId = corruptItemId;
-  stmt.execute();
-  stmt.finalize();
+  waitForAsyncUpdates(function() {
+    // Create corruption in database
+    var corruptItemId = bs.insertBookmark(bs.toolbarFolder,
+                                          uri("http://test.mozilla.org"),
+                                          bs.DEFAULT_INDEX, "We love belugas");
+    var stmt = dbConn.createStatement("UPDATE moz_bookmarks SET fk = NULL WHERE id = :itemId");
+    stmt.params.itemId = corruptItemId;
+    stmt.execute();
+    stmt.finalize();
 
-  // Export bookmarks
-  var bookmarksFile = Services.dirsvc.get("ProfD", Ci.nsILocalFile);
-  bookmarksFile.append("bookmarks.exported.html");
-  if (bookmarksFile.exists())
-    bookmarksFile.remove(false);
-  bookmarksFile.create(Ci.nsILocalFile.NORMAL_FILE_TYPE, 0600);
-  if (!bookmarksFile.exists())
-    do_throw("couldn't create file: bookmarks.exported.html");
-  try {
-    ies.exportHTMLToFile(bookmarksFile);
-  } catch(ex) { do_throw("couldn't export to bookmarks.exported.html: " + ex); }
+    // Export bookmarks
+    var bookmarksFile = Services.dirsvc.get("ProfD", Ci.nsILocalFile);
+    bookmarksFile.append("bookmarks.exported.html");
+    if (bookmarksFile.exists())
+      bookmarksFile.remove(false);
+    bookmarksFile.create(Ci.nsILocalFile.NORMAL_FILE_TYPE, 0600);
+    if (!bookmarksFile.exists())
+      do_throw("couldn't create file: bookmarks.exported.html");
+    try {
+      ies.exportHTMLToFile(bookmarksFile);
+    } catch(ex) { do_throw("couldn't export to bookmarks.exported.html: " + ex); }
 
-  // Clear all bookmarks
-  remove_all_bookmarks();
+    // Clear all bookmarks
+    remove_all_bookmarks();
 
-  // Import bookmarks
-  try {
-    ies.importHTMLFromFile(bookmarksFile, true);
-  } catch(ex) { do_throw("couldn't import the exported file: " + ex); }
+    // Import bookmarks
+    try {
+      ies.importHTMLFromFile(bookmarksFile, true);
+    } catch(ex) { do_throw("couldn't import the exported file: " + ex); }
 
-  // Check that every bookmark is correct
-  database_check();
+    // Check that every bookmark is correct
+    database_check();
+
+    waitForAsyncUpdates(do_test_finished);
+  });
 }
 
 /*
  * Check for imported bookmarks correctness
  */
 function database_check() {
   // BOOKMARKS MENU
   var query = hs.getNewQuery();
@@ -187,24 +189,26 @@ function database_check() {
   var toolbar = result.root;
   toolbar.containerOpen = true;
   do_check_eq(toolbar.childCount, 3);
   
   // livemark
   var livemark = toolbar.getChild(1);
   // title
   do_check_eq("Latest Headlines", livemark.title);
-  // livemark check
-  do_check_true(lms.isLivemark(livemark.itemId));
-  // site url
-  do_check_eq("http://en-us.fxfeeds.mozilla.com/en-US/firefox/livebookmarks/",
-              lms.getSiteURI(livemark.itemId).spec);
-  // feed url
-  do_check_eq("http://en-us.fxfeeds.mozilla.com/en-US/firefox/headlines.xml",
-              lms.getFeedURI(livemark.itemId).spec);
+  PlacesUtils.livemarks.getLivemark(
+    { id: livemark.itemId },
+    function (aStatus, aLivemark) {
+      do_check_true(Components.isSuccessCode(aStatus));
+      do_check_eq("http://en-us.fxfeeds.mozilla.com/en-US/firefox/livebookmarks/",
+                  aLivemark.siteURI.spec);
+      do_check_eq("http://en-us.fxfeeds.mozilla.com/en-US/firefox/headlines.xml",
+                  aLivemark.feedURI.spec);
+    }
+  );
 
   // cleanup
   toolbar.containerOpen = false;
 
   // UNFILED BOOKMARKS
   query.setFolders([bs.unfiledBookmarksFolder], 1);
   result = hs.executeQuery(query, hs.getNewQueryOptions());
   var unfiledBookmarks = result.root;
--- a/browser/components/places/tests/unit/test_bookmarks_html.js
+++ b/browser/components/places/tests/unit/test_bookmarks_html.js
@@ -78,17 +78,16 @@ let test_bookmarks = {
     },
   ],
   toolbar: [
     { title: "Getting Started",
       url: "http://en-us.www.mozilla.com/en-US/firefox/central/",
       icon: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAHWSURBVHjaYvz//z8DJQAggJiQOe/fv2fv7Oz8rays/N+VkfG/iYnJfyD/1+rVq7ffu3dPFpsBAAHEAHIBCJ85c8bN2Nj4vwsDw/8zQLwKiO8CcRoQu0DxqlWrdsHUwzBAAIGJmTNnPgYa9j8UqhFElwPxf2MIDeIrKSn9FwSJoRkAEEAM0DD4DzMAyPi/G+QKY4hh5WAXGf8PDQ0FGwJ22d27CjADAAIIrLmjo+MXA9R2kAHvGBA2wwx6B8W7od6CeQcggKCmCEL8bgwxYCbUIGTDVkHDBia+CuotgACCueD3TDQN75D4xmAvCoK9ARMHBzAw0AECiBHkAlC0Mdy7x9ABNA3obAZXIAa6iKEcGlMVQHwWyjYuL2d4v2cPg8vZswx7gHyAAAK7AOif7SAbOqCmn4Ha3AHFsIDtgPq/vLz8P4MSkJ2W9h8ggBjevXvHDo4FQUQg/kdypqCg4H8lUIACnQ/SOBMYI8bAsAJFPcj1AAEEjwVQqLpAbXmH5BJjqI0gi9DTAAgDBBCcAVLkgmQ7yKCZxpCQxqUZhAECCJ4XgMl493ug21ZD+aDAXH0WLM4A9MZPXJkJIIAwTAR5pQMalaCABQUULttBGCCAGCnNzgABBgAMJ5THwGvJLAAAAABJRU5ErkJggg=="
     },
     { title: "Latest Headlines",
-      description: "Livemark test comment",
       url: "http://en-us.fxfeeds.mozilla.com/en-US/firefox/livebookmarks/",
       feedUrl: "http://en-us.fxfeeds.mozilla.com/en-US/firefox/headlines.xml",
     }
   ],
   unfiled: [
     { title: "Example.tld",
       url: "http://example.tld/",
     },
@@ -100,16 +99,20 @@ let gBookmarksFileOld;
 // Places bookmarks.html file pointer.
 let gBookmarksFileNew;
 
 let importer = Cc["@mozilla.org/browser/places/import-export-service;1"].
                getService(Ci.nsIPlacesImportExportService);
 
 function run_test()
 {
+  run_next_test();
+}
+
+add_test(function setup() {
   // Avoid creating smart bookmarks during the test.
   Services.prefs.setIntPref("browser.places.smartBookmarksVersion", -1);
 
   // File pointer to legacy bookmarks file.
   gBookmarksFileOld = do_get_file("bookmarks.preplaces.html");
 
   // File pointer to a new Places-exported bookmarks file.
   gBookmarksFileNew = Services.dirsvc.get("ProfD", Ci.nsILocalFile);
@@ -126,41 +129,49 @@ function run_test()
   // Test importing a pre-Places canonical bookmarks file.
   // 1. import bookmarks.preplaces.html
   // 2. run the test-suite
   // Note: we do not empty the db before this import to catch bugs like 380999
   try {
     importer.importHTMLFromFile(gBookmarksFileOld, true);
   } catch(ex) { do_throw("couldn't import legacy bookmarks file: " + ex); }
 
-  testImportedBookmarks();
+  waitForAsyncUpdates(function () {
+    testImportedBookmarks();
 
-  // Prepare for next tests.
-  try {
-    importer.exportHTMLToFile(gBookmarksFileNew);
-  } catch(ex) { do_throw("couldn't export to file: " + ex); }
+    // Prepare for next tests.
+    try {
+      importer.exportHTMLToFile(gBookmarksFileNew);
+    } catch(ex) { do_throw("couldn't export to file: " + ex); }
 
-  remove_all_bookmarks();
-  run_next_test();
-}
+    waitForAsyncUpdates(function () {
+      remove_all_bookmarks();
+      run_next_test();
+    });
+  });
+});
 
 add_test(function test_import_new()
 {
   // Test importing a Places bookmarks.html file.
   // 1. import bookmarks.exported.html
   // 2. run the test-suite
 
   try {
     importer.importHTMLFromFile(gBookmarksFileNew, true);
   } catch(ex) { do_throw("couldn't import the exported file: " + ex); }
 
-  testImportedBookmarks();
+  waitForAsyncUpdates(function () {
+    testImportedBookmarks();
 
-  remove_all_bookmarks();
-  run_next_test();
+    waitForAsyncUpdates(function () {
+      remove_all_bookmarks();
+      run_next_test();
+    });
+  });
 });
 
 add_test(function test_emptytitle_export()
 {
   // Test exporting and importing with an empty-titled bookmark.
   // 1. import bookmarks
   // 1. create an empty-titled bookmark.
   // 2. export to bookmarks.exported.html
@@ -184,28 +195,32 @@ add_test(function test_emptytitle_export
   } catch(ex) { do_throw("couldn't export to file: " + ex); }
 
   remove_all_bookmarks();
 
   try {
     importer.importHTMLFromFile(gBookmarksFileNew, true);
   } catch(ex) { do_throw("couldn't import the exported file: " + ex); }
 
-  testImportedBookmarks();
+  waitForAsyncUpdates(function () {
+    testImportedBookmarks();
 
-  // Cleanup.
-  test_bookmarks.unfiled.pop();
-  PlacesUtils.bookmarks.removeItem(id);
+    // Cleanup.
+    test_bookmarks.unfiled.pop();
+    PlacesUtils.bookmarks.removeItem(id);
 
-  try {
-    importer.exportHTMLToFile(gBookmarksFileNew);
-  } catch(ex) { do_throw("couldn't export to file: " + ex); }
+    try {
+      importer.exportHTMLToFile(gBookmarksFileNew);
+    } catch(ex) { do_throw("couldn't export to file: " + ex); }
 
-  remove_all_bookmarks();
-  run_next_test();
+    waitForAsyncUpdates(function () {
+      remove_all_bookmarks();
+      run_next_test();
+    });
+  });
 });
 
 add_test(function test_import_preplaces_to_folder()
 {
   // Test importing a pre-Places canonical bookmarks file to a specific folder.
   // 1. create a new folder
   // 2. import bookmarks.preplaces.html to that folder
   // 3. run the test-suite
@@ -213,21 +228,25 @@ add_test(function test_import_preplaces_
   let testFolder = PlacesUtils.bookmarks.createFolder(
     PlacesUtils.bookmarksMenuFolderId, "test-import",
     PlacesUtils.bookmarks.DEFAULT_INDEX
   );
   try {
     importer.importHTMLFromFileToFolder(gBookmarksFileOld, testFolder, false);
   } catch(ex) { do_throw("couldn't import the exported file to folder: " + ex); }
 
-  // Import-to-folder creates subfolders for toolbar and unfiled.
-  testImportedBookmarksToFolder(testFolder);
+  waitForAsyncUpdates(function () {
+    // Import-to-folder creates subfolders for toolbar and unfiled.
+    testImportedBookmarksToFolder(testFolder);
 
-  remove_all_bookmarks();
-  run_next_test();
+    waitForAsyncUpdates(function () {
+      remove_all_bookmarks();
+      run_next_test();
+    });
+  });
 });
 
 add_test(function test_import_to_folder()
 {
   // Test importing a Places canonical bookmarks file to a specific folder.
   // 1. create a new folder
   // 2. import bookmarks.exported.html to that folder
   // 3. run the test-suite
@@ -235,21 +254,25 @@ add_test(function test_import_to_folder(
   let testFolder = PlacesUtils.bookmarks.createFolder(
     PlacesUtils.bookmarksMenuFolderId, "test-import",
     PlacesUtils.bookmarks.DEFAULT_INDEX
   );
   try {
     importer.importHTMLFromFileToFolder(gBookmarksFileNew, testFolder, false);
   } catch(ex) { do_throw("couldn't import the exported file to folder: " + ex); }
 
-  // Import-to-folder creates subfolders for toolbar and unfiled.
-  testImportedBookmarksToFolder(testFolder);
+  waitForAsyncUpdates(function () {
+    // Import-to-folder creates subfolders for toolbar and unfiled.
+    testImportedBookmarksToFolder(testFolder);
 
-  remove_all_bookmarks();
-  run_next_test();
+    waitForAsyncUpdates(function () {
+      remove_all_bookmarks();
+      run_next_test();
+    });
+  });
 });
 
 add_test(function test_import_ontop()
 {
   // Test importing the exported bookmarks.html file *on top of* the existing
   // bookmarks.
   // 1. empty bookmarks db
   // 2. import the exported bookmarks file
@@ -262,20 +285,24 @@ add_test(function test_import_ontop()
   } catch(ex) { do_throw("couldn't import the exported file: " + ex); }
   try {
     importer.exportHTMLToFile(gBookmarksFileNew);
   } catch(ex) { do_throw("couldn't export to file: " + ex); }
   try {
     importer.importHTMLFromFile(gBookmarksFileNew, true);
   } catch(ex) { do_throw("couldn't import the exported file: " + ex); }
 
-  testImportedBookmarks();
+  waitForAsyncUpdates(function () {
+    testImportedBookmarks();
 
-  remove_all_bookmarks();
-  run_next_test();
+    waitForAsyncUpdates(function () {
+      remove_all_bookmarks();
+      run_next_test();
+    });
+  });
 });
 
 function testImportedBookmarks()
 {
   for (let group in test_bookmarks) {
     let root;
     switch (group) {
       case "menu":
@@ -345,18 +372,24 @@ function checkItem(aExpected, aNode)
           do_check_eq(PlacesUtils.bookmarks.getItemDateAdded(id),
                       aExpected.dateAdded);
         break;
       case "lastModified":
           do_check_eq(PlacesUtils.bookmarks.getItemLastModified(id),
                       aExpected.lastModified);
         break;
       case "url":
-        if (!PlacesUtils.livemarks.isLivemark(id))
-          do_check_eq(aNode.uri, aExpected.url);
+        PlacesUtils.livemarks.getLivemark(
+          { id: id },
+          function (aStatus, aLivemark) {
+            if (!Components.isSuccessCode(aStatus)) {
+              do_check_eq(aNode.uri, aExpected.url);
+            }
+          }
+        );
         break;
       case "icon":
         let faviconURI = PlacesUtils.favicons.getFaviconForPage(
           NetUtil.newURI(aExpected.url)
         );
         let dataURL = PlacesUtils.favicons.getFaviconDataAsDataURL(faviconURI);
         // Avoid do_check_eq for console spam.
         do_check_true(dataURL == aExpected.icon);
@@ -373,21 +406,24 @@ function checkItem(aExpected, aNode)
                                .getItemAnnotation(id, PlacesUtils.POST_DATA_ANNO),
                     aExpected.postData);
         break;
       case "charset":
         do_check_eq(PlacesUtils.history.getCharsetForURI(NetUtil.newURI(aNode.uri)),
                     aExpected.charset);
         break;
       case "feedUrl":
-        do_check_true(PlacesUtils.livemarks.isLivemark(id));
-        do_check_eq(PlacesUtils.livemarks.getSiteURI(id).spec,
-                    aExpected.url);
-        do_check_eq(PlacesUtils.livemarks.getFeedURI(id).spec,
-                    aExpected.feedUrl);
+        PlacesUtils.livemarks.getLivemark(
+          { id: id },
+          function (aStatus, aLivemark) {
+            do_check_true(Components.isSuccessCode(aStatus));
+            do_check_eq(aLivemark.siteURI.spec, aExpected.url);
+            do_check_eq(aLivemark.feedURI.spec, Expected.feedUrl);
+          }
+        );
         break;
       case "children":
         let folder = aNode.QueryInterface(Ci.nsINavHistoryContainerResultNode);
         do_check_eq(folder.hasChildren, aExpected.children.length > 0);
         folder.containerOpen = true;
         do_check_eq(folder.childCount, aExpected.children.length);
 
         aExpected.children.forEach(function (item, index) checkItem(item, folder.getChild(index)));
--- a/browser/components/places/tests/unit/test_placesTxn.js
+++ b/browser/components/places/tests/unit/test_placesTxn.js
@@ -34,17 +34,16 @@
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 var bmsvc = PlacesUtils.bookmarks;
-var lmsvc = PlacesUtils.livemarks;
 var ptSvc = PlacesUIUtils.ptm;
 var tagssvc = PlacesUtils.tagging;
 var annosvc = PlacesUtils.annotations;
 
 // create and add bookmarks observer
 var observer = {
   onBeginUpdateBatch: function() {
     this._beginUpdateBatch = true;
@@ -414,94 +413,16 @@ function run_test() {
   do_check_eq(observer._itemChangedId, bkmk1Id);
   do_check_eq(observer._itemChangedProperty, "keyword");
   do_check_eq(observer._itemChangedValue, "kw1"); 
   txn11.undoTransaction();
   do_check_eq(observer._itemChangedId, bkmk1Id);
   do_check_eq(observer._itemChangedProperty, "keyword");
   do_check_eq(observer._itemChangedValue, ""); 
 
-  // Testing create livemark
-  var txn12 = ptSvc.createLivemark(uri("http://feeduri.com"),
-                                   uri("http://siteuri.com"),
-                                   "Livemark1", root);
-  txn12.doTransaction();
-  var lvmkId = observer._itemAddedId;
-  do_check_true(lmsvc.isLivemark(lvmkId));
-  do_check_eq(lmsvc.getSiteURI(lvmkId).spec, "http://siteuri.com/");
-  do_check_eq(lmsvc.getFeedURI(lvmkId).spec, "http://feeduri.com/");
-  txn12.undoTransaction();
-  do_check_false(lmsvc.isLivemark(lvmkId));
-  txn12.redoTransaction();
-  lvmkId = observer._itemAddedId;
-  do_check_true(lmsvc.isLivemark(lvmkId));
-  do_check_eq(lmsvc.getSiteURI(lvmkId).spec, "http://siteuri.com/");
-  do_check_eq(lmsvc.getFeedURI(lvmkId).spec, "http://feeduri.com/");
-
-  // editLivemarkSiteURI
-  var txn13 = ptSvc.editLivemarkSiteURI(lvmkId, uri("http://new-siteuri.com/"));
-  txn13.doTransaction();
-  do_check_eq(observer._itemChangedId, lvmkId);
-  do_check_eq(observer._itemChangedProperty, "livemark/siteURI");
-  do_check_eq(lmsvc.getSiteURI(lvmkId).spec, "http://new-siteuri.com/");
-  txn13.undoTransaction();
-  do_check_eq(observer._itemChangedId, lvmkId);
-  do_check_eq(observer._itemChangedProperty, "livemark/siteURI");
-  do_check_eq(observer._itemChangedValue, "");
-  do_check_eq(lmsvc.getSiteURI(lvmkId).spec, "http://siteuri.com/");
-  txn13.redoTransaction();
-  do_check_eq(observer._itemChangedId, lvmkId);
-  do_check_eq(observer._itemChangedProperty, "livemark/siteURI");
-  do_check_eq(lmsvc.getSiteURI(lvmkId).spec, "http://new-siteuri.com/");
-  txn13.undoTransaction();
-  do_check_eq(observer._itemChangedId, lvmkId);
-  do_check_eq(observer._itemChangedProperty, "livemark/siteURI");
-  do_check_eq(observer._itemChangedValue, "");
-  do_check_eq(lmsvc.getSiteURI(lvmkId).spec, "http://siteuri.com/");
-
-  // editLivemarkFeedURI
-  var txn14 = ptSvc.editLivemarkFeedURI(lvmkId, uri("http://new-feeduri.com/"));
-  txn14.doTransaction();
-  do_check_eq(observer._itemChangedId, lvmkId);
-  do_check_eq(observer._itemChangedProperty, "livemark/feedURI");
-  do_check_eq(lmsvc.getFeedURI(lvmkId).spec, "http://new-feeduri.com/");
-  txn14.undoTransaction();
-  do_check_eq(observer._itemChangedId, lvmkId);
-  do_check_eq(observer._itemChangedProperty, "livemark/feedURI");
-  do_check_eq(observer._itemChangedValue, "");
-  do_check_eq(lmsvc.getFeedURI(lvmkId).spec, "http://feeduri.com/");
-  txn14.redoTransaction();
-  do_check_eq(observer._itemChangedId, lvmkId);
-  do_check_eq(observer._itemChangedProperty, "livemark/feedURI");
-  do_check_eq(observer._itemChangedValue, "");
-  do_check_eq(lmsvc.getFeedURI(lvmkId).spec, "http://new-feeduri.com/");
-  txn14.undoTransaction();
-  do_check_eq(observer._itemChangedId, lvmkId);
-  do_check_eq(observer._itemChangedProperty, "livemark/feedURI");
-  do_check_eq(observer._itemChangedValue, "");
-  do_check_eq(lmsvc.getFeedURI(lvmkId).spec, "http://feeduri.com/");
-
-  // Testing remove livemark
-  // Set an annotation and check that we don't lose it on undo
-  annosvc.setItemAnnotation(lvmkId, "livemark/testAnno", "testAnno",
-                            0, annosvc.EXPIRE_NEVER);
-  var txn15 = ptSvc.removeItem(lvmkId);
-  txn15.doTransaction();
-  do_check_false(lmsvc.isLivemark(lvmkId));
-  do_check_eq(observer._itemRemovedId, lvmkId);
-  txn15.undoTransaction();
-  lvmkId = observer._itemAddedId;
-  do_check_true(lmsvc.isLivemark(lvmkId));
-  do_check_eq(lmsvc.getSiteURI(lvmkId).spec, "http://siteuri.com/");
-  do_check_eq(lmsvc.getFeedURI(lvmkId).spec, "http://feeduri.com/");
-  do_check_eq(annosvc.getItemAnnotation(lvmkId, "livemark/testAnno"), "testAnno");
-  txn15.redoTransaction();
-  do_check_false(lmsvc.isLivemark(lvmkId));
-  do_check_eq(observer._itemRemovedId, lvmkId);
-
   // Test LoadInSidebar transaction.
   var txn16 = ptSvc.setLoadInSidebar(bkmk1Id, true);
   txn16.doTransaction();
   do_check_eq(observer._itemChangedId, bkmk1Id);
   do_check_eq(observer._itemChangedProperty, PlacesUIUtils.LOAD_IN_SIDEBAR_ANNO);
   do_check_eq(observer._itemChanged_isAnnotationProperty, true);
   txn16.undoTransaction();
   do_check_eq(observer._itemChangedId, bkmk1Id);
--- a/browser/components/places/tests/unit/test_txnGUIDs.js
+++ b/browser/components/places/tests/unit/test_txnGUIDs.js
@@ -36,52 +36,70 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 /**
  * This test will ensure any transactions service that is going to create
  * a new item, won't replace the GUID when undoing and redoing the action.
  */
-var bmsvc = PlacesUtils.bookmarks;
-var txnsvc = PlacesUIUtils.ptm;
 
 function test_GUID_persistance(aTxn) {
   aTxn.doTransaction();
-  var itemId = bmsvc.getIdForItemAt(bmsvc.unfiledBookmarksFolder, 0);
-  var GUID = bmsvc.getItemGUID(itemId);
-  aTxn.undoTransaction();
-  aTxn.redoTransaction();
-  do_check_eq(GUID, bmsvc.getItemGUID(itemId));
-  aTxn.undoTransaction();
+  waitForAsyncUpdates(function () {
+    let itemId = PlacesUtils.bookmarks
+                            .getIdForItemAt(PlacesUtils.unfiledBookmarksFolderId, 0);
+    let GUID = PlacesUtils.bookmarks.getItemGUID(itemId);
+    aTxn.undoTransaction();
+    aTxn.redoTransaction();
+    waitForAsyncUpdates(function() {
+      let itemId = PlacesUtils.bookmarks
+                              .getIdForItemAt(PlacesUtils.unfiledBookmarksFolderId, 0);
+      do_check_eq(GUID, PlacesUtils.bookmarks.getItemGUID(itemId));
+      aTxn.undoTransaction();
+      waitForAsyncUpdates(run_next_test);
+    });
+  });
 }
 
 function run_test() {
-  // Create folder.
-  var createFolderTxn = txnsvc.createFolder("Test folder",
-                                            bmsvc.unfiledBookmarksFolder,
-                                            bmsvc.DEFAULT_INDEX);
-  test_GUID_persistance(createFolderTxn);
+  run_next_test();
+}
 
-  // Create bookmark.
-  var createBookmarkTxn = txnsvc.createItem(uri("http://www.example.com"),
-                                            bmsvc.unfiledBookmarksFolder,
-                                            bmsvc.DEFAULT_INDEX,
-                                            "Test bookmark");
-  test_GUID_persistance(createBookmarkTxn);
+add_test(function create_folder() {
+  let createFolderTxn = new PlacesCreateFolderTransaction(
+    "Test folder", PlacesUtils.unfiledBookmarksFolderId,
+    PlacesUtils.bookmarks.DEFAULT_INDEX
+  );
+  test_GUID_persistance(createFolderTxn);
+});
 
-  // Create separator.
-  var createSeparatorTxn = txnsvc.createSeparator(bmsvc.unfiledBookmarksFolder,
-                                                  bmsvc.DEFAULT_INDEX);
-  test_GUID_persistance(createFolderTxn);
+add_test(function create_bookmark() {
+  let createBookmarkTxn = new PlacesCreateBookmarkTransaction(
+    NetUtil.newURI("http://www.example.com"), PlacesUtils.unfiledBookmarksFolderId,
+    PlacesUtils.bookmarks.DEFAULT_INDEX, "Test bookmark"
+  );
+  test_GUID_persistance(createBookmarkTxn);
+});
+  
+add_test(function create_separator() {
+  let createSeparatorTxn = new PlacesCreateSeparatorTransaction(
+    PlacesUtils.unfiledBookmarksFolderId, PlacesUtils.bookmarks.DEFAULT_INDEX
+  );
+  test_GUID_persistance(createSeparatorTxn);
+});
 
-  // Create livemark.
-  var createLivemarkTxn = txnsvc.createLivemark(uri("http://feeduri.com"),
-                                               uri("http://siteuri.com"),
-                                               "Test livemark",
-                                               bmsvc.unfiledBookmarksFolder,
-                                               bmsvc.DEFAULT_INDEX);
+add_test(function tag_uri() {
+  let tagURITxn = new PlacesTagURITransaction(
+    NetUtil.newURI("http://www.example.com"), ["foo"]
+  );
+  test_GUID_persistance(tagURITxn);
+});
+
+add_test(function create_livemark() {
+  let createLivemarkTxn = new PlacesCreateLivemarkTransaction(
+    NetUtil.newURI("http://feeduri.com"), NetUtil.newURI("http://siteuri.com"),
+    "Test livemark", PlacesUtils.unfiledBookmarksFolderId,
+    PlacesUtils.bookmarks.DEFAULT_INDEX
+  );
   test_GUID_persistance(createLivemarkTxn);
+});
 
-  // Tag URI.
-  var tagURITxn = txnsvc.tagURI(uri("http://www.example.com"), ["foo"]);
-  test_GUID_persistance(tagURITxn);
-}
--- a/browser/components/privatebrowsing/src/nsPrivateBrowsingService.js
+++ b/browser/components/privatebrowsing/src/nsPrivateBrowsingService.js
@@ -184,28 +184,16 @@ PrivateBrowsingService.prototype = {
           let plainURL = win.gBrowser.currentURI.spec;
           if (plainURL.indexOf("view-source:") == 0) {
             plainURL = plainURL.substr(12);
             this._viewSrcURLs.push(plainURL);
           }
         }
         win.close();
       }
-        
-      var windowsEnum = Services.wm.getEnumerator("navigator:browser");
-      while (windowsEnum.hasMoreElements()) {
-        var window = windowsEnum.getNext();
-        window.getInterface(Ci.nsIWebNavigation)
-              .QueryInterface(Ci.nsIDocShellTreeItem)
-              .treeOwner
-              .QueryInterface(Ci.nsIInterfaceRequestor)
-              .getInterface(Ci.nsIXULWindow)
-              .docShell.QueryInterface(Ci.nsILoadContext)
-              .usePrivateBrowsing = this._inPrivateBrowsing;
-      }
 
       if (!this._quitting && this._saveSession) {
         let browserWindow = this._getBrowserWindow();
 
 	// if there are open browser windows, load a dummy session to get a distinct 
         // separation between private and non-private sessions
 	if (browserWindow) {
           // set an empty session to transition from/to pb mode, see bug 476463
@@ -224,16 +212,28 @@ PrivateBrowsingService.prototype = {
           browser.removeTab(browser.tabContainer.firstChild);
           browserWindow.getInterface(Ci.nsIWebNavigation)
                        .QueryInterface(Ci.nsIDocShellTreeItem)
                        .treeOwner
                        .QueryInterface(Ci.nsIInterfaceRequestor)
                        .getInterface(Ci.nsIXULWindow)
                        .docShell.contentViewer.resetCloseWindow();
         }
+
+        var windowsEnum = Services.wm.getEnumerator("navigator:browser");
+        while (windowsEnum.hasMoreElements()) {
+          var window = windowsEnum.getNext();
+          window.getInterface(Ci.nsIWebNavigation)
+                .QueryInterface(Ci.nsIDocShellTreeItem)
+                .treeOwner
+                .QueryInterface(Ci.nsIInterfaceRequestor)
+                .getInterface(Ci.nsIXULWindow)
+                .docShell.QueryInterface(Ci.nsILoadContext)
+                .usePrivateBrowsing = this._inPrivateBrowsing;
+        }
       }
     }
     else
       this._saveSession = false;
   },
 
   _onAfterPrivateBrowsingModeChange: function PBS__onAfterPrivateBrowsingModeChange() {
     // nothing to do here if we're enabling at startup or the current session is being
--- a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_beforeunload.js
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_beforeunload.js
@@ -55,36 +55,36 @@ function test() {
       dialogWin.document.documentElement.getButton("accept").click();
     else if (rejectDialog-- > 0)
       dialogWin.document.documentElement.getButton("cancel").click();
   }
 
   Services.obs.addObserver(promptObserver, "common-dialog-loaded", false);
 
   waitForExplicitFinish();
-  let browser1 = gBrowser.getBrowserForTab(gBrowser.addTab());
+  let browser1 = gBrowser.addTab().linkedBrowser;
   browser1.addEventListener("load", function() {
     browser1.removeEventListener("load", arguments.callee, true);
 
-    let browser2 = gBrowser.getBrowserForTab(gBrowser.addTab());
+    let browser2 = gBrowser.addTab().linkedBrowser;
     browser2.addEventListener("load", function() {
       browser2.removeEventListener("load", arguments.callee, true);
 
       rejectDialog = 1;
       pb.privateBrowsingEnabled = true;
 
       ok(!pb.privateBrowsingEnabled, "Private browsing mode should not have been activated");
       is(confirmCalls, 1, "Only one confirm box should be shown");
       is(gBrowser.tabs.length, 3,
          "No tabs should be closed because private browsing mode transition was canceled");
-      is(gBrowser.getBrowserForTab(gBrowser.tabContainer.firstChild).currentURI.spec, "about:blank",
+      is(gBrowser.tabContainer.firstChild.linkedBrowser.currentURI.spec, "about:blank",
          "The first tab should be a blank tab");
-      is(gBrowser.getBrowserForTab(gBrowser.tabContainer.firstChild.nextSibling).currentURI.spec, TEST_PAGE_1,
+      is(gBrowser.tabContainer.firstChild.nextSibling.linkedBrowser.currentURI.spec, TEST_PAGE_1,
          "The middle tab should be the same one we opened");
-      is(gBrowser.getBrowserForTab(gBrowser.tabContainer.lastChild).currentURI.spec, TEST_PAGE_2,
+      is(gBrowser.tabContainer.lastChild.linkedBrowser.currentURI.spec, TEST_PAGE_2,
          "The last tab should be the same one we opened");
       is(rejectDialog, 0, "Only one confirm dialog should have been rejected");
 
       confirmCalls = 0;
       acceptDialog = 2;
       pb.privateBrowsingEnabled = true;
 
       ok(pb.privateBrowsingEnabled, "Private browsing mode should have been activated");
@@ -108,53 +108,59 @@ function test() {
             confirmCalls = 0;
             rejectDialog = 1;
             pb.privateBrowsingEnabled = false;
 
             ok(pb.privateBrowsingEnabled, "Private browsing mode should not have been deactivated");
             is(confirmCalls, 1, "Only one confirm box should be shown");
             is(gBrowser.tabs.length, 2,
                "No tabs should be closed because private browsing mode transition was canceled");
-            is(gBrowser.getBrowserForTab(gBrowser.tabContainer.firstChild).currentURI.spec, TEST_PAGE_1,
+            is(gBrowser.tabContainer.firstChild.linkedBrowser.currentURI.spec, TEST_PAGE_1,
                "The first tab should be the same one we opened");
-            is(gBrowser.getBrowserForTab(gBrowser.tabContainer.lastChild).currentURI.spec, TEST_PAGE_2,
+            is(gBrowser.tabContainer.lastChild.linkedBrowser.currentURI.spec, TEST_PAGE_2,
                "The last tab should be the same one we opened");
             is(rejectDialog, 0, "Only one confirm dialog should have been rejected");
 
+            // Ensure that all restored tabs are loaded without waiting for the
+            // user to bring them to the foreground, by resetting the related
+            // preference (see the "firefox.js" defaults file for details).
+            Services.prefs.setBoolPref("browser.sessionstore.restore_on_demand", false);
+
             confirmCalls = 0;
             acceptDialog = 2;
             pb.privateBrowsingEnabled = false;
 
             ok(!pb.privateBrowsingEnabled, "Private browsing mode should have been deactivated");
             is(confirmCalls, 2, "Only two confirm boxes should be shown");
             is(gBrowser.tabs.length, 3,
                "Incorrect number of tabs after transition into private browsing");
 
             let loads = 0;
             function waitForLoad(event) {
               gBrowser.removeEventListener("load", arguments.callee, true);
 
               if (++loads != 3)
                 return;
 
-              is(gBrowser.getBrowserForTab(gBrowser.tabContainer.firstChild).currentURI.spec, "about:blank",
+              is(gBrowser.tabContainer.firstChild.linkedBrowser.currentURI.spec, "about:blank",
                  "The first tab should be a blank tab");
-              is(gBrowser.getBrowserForTab(gBrowser.tabContainer.firstChild.nextSibling).currentURI.spec, TEST_PAGE_1,
+              is(gBrowser.tabContainer.firstChild.nextSibling.linkedBrowser.currentURI.spec, TEST_PAGE_1,
                  "The middle tab should be the same one we opened");
-              is(gBrowser.getBrowserForTab(gBrowser.tabContainer.lastChild).currentURI.spec, TEST_PAGE_2,
+              is(gBrowser.tabContainer.lastChild.linkedBrowser.currentURI.spec, TEST_PAGE_2,
                  "The last tab should be the same one we opened");
               is(acceptDialog, 0, "Two confirm dialogs should have been accepted");
               is(acceptDialog, 0, "Two prompts should have been raised");
 
               acceptDialog = 2;
               gBrowser.removeTab(gBrowser.tabContainer.lastChild);
               gBrowser.removeTab(gBrowser.tabContainer.lastChild);
               gBrowser.getBrowserAtIndex(gBrowser.tabContainer.selectedIndex).contentWindow.focus();
 
               Services.obs.removeObserver(promptObserver, "common-dialog-loaded", false);
+              Services.prefs.clearUserPref("browser.sessionstore.restore_on_demand");
               finish();
             }
             for (let i = 0; i < gBrowser.browsers.length; ++i)
               gBrowser.browsers[i].addEventListener("load", waitForLoad, true);
           }, true);
           gBrowser.selectedBrowser.loadURI(TEST_PAGE_2);
         }, true);
         gBrowser.selectedBrowser.loadURI(TEST_PAGE_1);
--- a/browser/components/sessionstore/test/browser_580512.js
+++ b/browser/components/sessionstore/test/browser_580512.js
@@ -35,17 +35,17 @@ function checkSecondWin(win) {
   document.documentElement.setAttribute("windowtype", "navigator:browser");
   finish();
 }
 
 function openWinWithCb(cb, argURIs, expectedURIs) {
   if (!expectedURIs)
     expectedURIs = argURIs;
 
-  var win = openDialog("chrome://browser/content/", "_blank",
+  var win = openDialog(getBrowserURL(), "_blank",
                        "chrome,all,dialog=no", argURIs.join("|"));
 
   win.addEventListener("load", function () {
     win.removeEventListener("load", arguments.callee, false);
     info("the window loaded");
 
     var expectedLoads = expectedURIs.length;
 
--- a/browser/components/sessionstore/test/browser_586068-cascaded_restore.js
+++ b/browser/components/sessionstore/test/browser_586068-cascaded_restore.js
@@ -85,16 +85,18 @@ function runNextTest() {
   else {
     ss.setBrowserState(stateBackup);
     executeSoon(finish);
   }
 }
 
 
 function test_cascade() {
+  Services.prefs.setBoolPref("browser.sessionstore.restore_on_demand", false);
+
   // We have our own progress listener for this test, which we'll attach before our state is set
   let progressListener = {
     onStateChange: function (aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) {
       dump("\n\nload: " + aBrowser.currentURI.spec + "\n" + JSON.stringify(countTabs()) + "\n\n");
       if (aBrowser.__SS_restoreState == TAB_STATE_RESTORING &&
           aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
           aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK &&
           aStateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW)
@@ -215,16 +217,18 @@ function test_select() {
   }
 
   window.gBrowser.addTabsProgressListener(progressListener);
   ss.setBrowserState(JSON.stringify(state));
 }
 
 
 function test_multiWindowState() {
+  Services.prefs.setBoolPref("browser.sessionstore.restore_on_demand", false);
+
   // We have our own progress listener for this test, which we'll attach before our state is set
   let progressListener = {
     onStateChange: function (aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) {
       // We only care about load events when the tab still has
       // __SS_restoreState == TAB_STATE_RESTORING on it.
       // Since our listener is attached before the sessionstore one, this works out.
       if (aBrowser.__SS_restoreState == TAB_STATE_RESTORING &&
           aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
@@ -292,16 +296,18 @@ function test_multiWindowState() {
   Services.ww.registerNotification(windowObserver);
 
   window.gBrowser.addTabsProgressListener(progressListener);
   ss.setBrowserState(JSON.stringify(state));
 }
 
 
 function test_setWindowStateNoOverwrite() {
+  Services.prefs.setBoolPref("browser.sessionstore.restore_on_demand", false);
+
   // We have our own progress listener for this test, which we'll attach before our state is set
   let progressListener = {
     onStateChange: function (aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) {
       // We only care about load events when the tab still has
       // __SS_restoreState == TAB_STATE_RESTORING on it.
       // Since our listener is attached before the sessionstore one, this works out.
       if (aBrowser.__SS_restoreState == TAB_STATE_RESTORING &&
           aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
@@ -361,16 +367,18 @@ function test_setWindowStateNoOverwrite(
   }
 
   window.gBrowser.addTabsProgressListener(progressListener);
   ss.setWindowState(window, JSON.stringify(state1), true);
 }
 
 
 function test_setWindowStateOverwrite() {
+  Services.prefs.setBoolPref("browser.sessionstore.restore_on_demand", false);
+
   // We have our own progress listener for this test, which we'll attach before our state is set
   let progressListener = {
     onStateChange: function (aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) {
       // We only care about load events when the tab still has
       // __SS_restoreState == TAB_STATE_RESTORING on it.
       // Since our listener is attached before the sessionstore one, this works out.
       if (aBrowser.__SS_restoreState == TAB_STATE_RESTORING &&
           aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
@@ -430,16 +438,18 @@ function test_setWindowStateOverwrite() 
   }
 
   window.gBrowser.addTabsProgressListener(progressListener);
   ss.setWindowState(window, JSON.stringify(state1), true);
 }
 
 
 function test_setBrowserStateInterrupted() {
+  Services.prefs.setBoolPref("browser.sessionstore.restore_on_demand", false);
+
   // We have our own progress listener for this test, which we'll attach before our state is set
   let progressListener = {
     onStateChange: function (aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) {
       // We only care about load events when the tab still has
       // __SS_restoreState == TAB_STATE_RESTORING on it.
       // Since our listener is attached before the sessionstore one, this works out.
       if (aBrowser.__SS_restoreState == TAB_STATE_RESTORING &&
           aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
@@ -622,16 +632,18 @@ function test_reload() {
   window.gBrowser.addTabsProgressListener(progressListener);
   ss.setBrowserState(JSON.stringify(state));
 }
 
 
 // This doesn't actually test anything, just does a cascaded restore with default
 // settings. This really just sets up to test that reloads work.
 function test_reloadCascadeSetup() {
+  Services.prefs.setBoolPref("browser.sessionstore.restore_on_demand", false);
+
   // We have our own progress listener for this test, which we'll attach before our state is set
   let progressListener = {
     onStateChange: function (aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) {
       if (aBrowser.__SS_restoreState == TAB_STATE_RESTORING &&
           aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
           aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK &&
           aStateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW)
         test_cascadeReloadSetup_progressCallback();
--- a/browser/components/sessionstore/test/head.js
+++ b/browser/components/sessionstore/test/head.js
@@ -32,16 +32,24 @@
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 let ss = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore);
 
+// Some tests here assume that all restored tabs are loaded without waiting for
+// the user to bring them to the foreground. We ensure this by resetting the
+// related preference (see the "firefox.js" defaults file for details).
+Services.prefs.setBoolPref("browser.sessionstore.restore_on_demand", false);
+registerCleanupFunction(function () {
+  Services.prefs.clearUserPref("browser.sessionstore.restore_on_demand");
+});
+
 // This assumes that tests will at least have some state/entries
 function waitForBrowserState(aState, aSetStateCallback) {
   let windows = [window];
   let tabsRestored = 0;
   let expectedTabsRestored = 0;
   let expectedWindows = aState.windows.length;
   let windowsOpen = 1;
   let listening = false;
--- a/browser/components/tabview/test/head.js
+++ b/browser/components/tabview/test/head.js
@@ -1,11 +1,20 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
+// Some tests here assume that all restored tabs are loaded without waiting for
+// the user to bring them to the foreground. We ensure this by resetting the
+// related preference (see the "firefox.js" defaults file for details).
+Services.prefs.setBoolPref("browser.sessionstore.restore_on_demand", false);
+registerCleanupFunction(function () {
+  Services.prefs.clearUserPref("browser.sessionstore.restore_on_demand");
+});
+
+// ----------
 function createEmptyGroupItem(contentWindow, width, height, padding, animate) {
   let pageBounds = contentWindow.Items.getPageBounds();
   pageBounds.inset(padding, padding);
 
   let box = new contentWindow.Rect(pageBounds);
   box.width = width;
   box.height = height;
 
--- a/browser/confvars.sh
+++ b/browser/confvars.sh
@@ -37,27 +37,34 @@
 # ***** END LICENSE BLOCK *****
 
 MOZ_APP_BASENAME=Firefox
 MOZ_APP_VENDOR=Mozilla
 MOZ_UPDATER=1
 MOZ_PHOENIX=1
 
 if test "$OS_ARCH" = "WINNT"; then
+  MOZ_VERIFY_MAR_SIGNATURE=1
   if ! test "$HAVE_64BIT_OS"; then
     MOZ_MAINTENANCE_SERVICE=1
   fi
 fi
 
 MOZ_CHROME_FILE_FORMAT=omni
 MOZ_SAFE_BROWSING=1
 MOZ_SERVICES_SYNC=1
 MOZ_APP_VERSION=$FIREFOX_VERSION
 MOZ_EXTENSIONS_DEFAULT=" gnomevfs"
 # MOZ_APP_DISPLAYNAME will be set by branding/configure.sh
 # Changing either of these values requires a clobber to ensure correct results,
 # because branding dependencies are broken.
 MOZ_BRANDING_DIRECTORY=browser/branding/nightly
 MOZ_OFFICIAL_BRANDING_DIRECTORY=browser/branding/official
 MOZ_APP_ID={ec8030f7-c20a-464f-9b0e-13a3a9e97384}
+# This should usually be the same as the value MAR_CHANNEL_ID.
+# If more than one ID is needed, then you should use a comma separated list
+# of values.
+ACCEPTED_MAR_CHANNEL_IDS=firefox-mozilla-central
+# The MAR_CHANNEL_ID must not contain the following 3 characters: ",\t "
+MAR_CHANNEL_ID=firefox-mozilla-central
 MOZ_PROFILE_MIGRATOR=1
 MOZ_EXTENSION_MANAGER=1
 MOZ_APP_STATIC_INI=1
--- a/browser/devtools/debugger/debugger-view.js
+++ b/browser/devtools/debugger/debugger-view.js
@@ -344,17 +344,17 @@ DebuggerView.Properties = {
    */
   _addScope: function DVP__addScope(aName, aId) {
     // make sure the parent container exists
     if (!this._vars) {
       return null;
     }
 
     // compute the id of the element if not specified
-    aId = aId || (aName + "-scope");
+    aId = aId || (aName.toLowerCase().trim().replace(" ", "-") + "-scope");
 
     // contains generic nodes and functionality
     let element = this._createPropertyElement(aName, aId, "scope", this._vars);
 
     // make sure the element was created successfully
     if (!element) {
       dump("The debugger scope container wasn't created properly: " + aId);
       return null;
@@ -536,22 +536,22 @@ DebuggerView.Properties = {
    * default id set as aVar.id->aKey-property.
    *
    * @param object aVar
    *        The parent variable element.
    * @param {Array} aProperty
    *        An array containing the key and grip properties, specifying
    *        the value and/or type & class of the variable (if the type
    *        is not specified, it will be inferred from the value).
-   *        e.g. ["someProp0": 42]
-   *             ["someProp1": true]
-   *             ["someProp2": "nasu"]
-   *             ["someProp3": { type: "undefined" }]
-   *             ["someProp4": { type: "null" }]
-   *             ["someProp5": { type: "object", class: "Object" }]
+   *        e.g. ["someProp0", 42]
+   *             ["someProp1", true]
+   *             ["someProp2", "nasu"]
+   *             ["someProp3", { type: "undefined" }]
+   *             ["someProp4", { type: "null" }]
+   *             ["someProp5", { type: "object", class: "Object" }]
    * @param string aName
    *        Optional, the property name.
    * @paarm string aId
    *        Optional, an id for the property html node.
    * @return object
    *         The newly created html node representing the added prop.
    */
   _addProperty: function DVP__addProperty(aVar, aProperty, aName, aId) {
--- a/browser/devtools/debugger/test/browser_dbg_debuggerstatement.js
+++ b/browser/devtools/debugger/test/browser_dbg_debuggerstatement.js
@@ -29,17 +29,18 @@ function test_early_debugger_statement(a
 {
   let paused = function(aEvent, aPacket) {
     ok(false, "Pause shouldn't be called before we've attached!\n");
     finish_test();
   };
   gClient.addListener("paused", paused);
   // This should continue without nesting an event loop and calling
   // the onPaused hook, because we haven't attached yet.
-  gTab.linkedBrowser.contentWindow.wrappedJSObject.runDebuggerStatement();
+  // TODO: uncomment this when bug 723563 is fixed.
+  //gTab.linkedBrowser.contentWindow.wrappedJSObject.runDebuggerStatement();
 
   gClient.removeListener("paused", paused);
 
   // Now attach and resume...
   gClient.request({ to: aActor.threadActor, type: "attach" }, function(aResponse) {
     gClient.request({ to: aActor.threadActor, type: "resume" }, function(aResponse) {
       test_debugger_statement(aActor);
     });
--- a/browser/devtools/debugger/test/browser_dbg_listtabs.js
+++ b/browser/devtools/debugger/test/browser_dbg_listtabs.js
@@ -82,17 +82,17 @@ function test_attach_removed_tab()
   removeTab(gTab2);
   gTab2 = null;
   gClient.addListener("paused", function(aEvent, aPacket) {
     ok(false, "Attaching to an exited tab actor shouldn't generate a pause.");
     finish_test();
   });
 
   gClient.request({ to: gTab2Actor, type: "attach" }, function(aResponse) {
-    is(aResponse.type, "exited", "Tab should consider itself exited.");
+    is(aResponse.error, "noSuchActor", "Tab should be gone.");
     finish_test();
   });
 }
 
 function finish_test()
 {
   gClient.close(function() {
     finish();
--- a/browser/devtools/highlighter/TreePanel.jsm
+++ b/browser/devtools/highlighter/TreePanel.jsm
@@ -59,74 +59,51 @@ const INSPECTOR_URI = "chrome://browser/
  */
 function TreePanel(aContext, aIUI) {
   this._init(aContext, aIUI);
 };
 
 TreePanel.prototype = {
   showTextNodesWithWhitespace: false,
   id: "treepanel", // DO NOT LOCALIZE
-  openInDock: true,
+  _open: false,
 
   /**
    * The tree panel container element.
    * @returns xul:panel|xul:vbox|null
    *          xul:panel is returned when the tree panel is not docked, or
    *          xul:vbox when when the tree panel is docked.
    *          null is returned when no container is available.
    */
   get container()
   {
-    if (this.openInDock) {
-      return this.document.getElementById("inspector-tree-box");
-    }
-
-    return this.document.getElementById("inspector-tree-panel");
+    return this.document.getElementById("inspector-tree-box");
   },
 
   /**
    * Main TreePanel boot-strapping method. Initialize the TreePanel with the
    * originating context and the InspectorUI global.
    * @param aContext nsIDOMWindow (xulwindow)
    * @param aIUI global InspectorUI object
    */
   _init: function TP__init(aContext, aIUI)
   {
     this.IUI = aIUI;
     this.window = aContext;
     this.document = this.window.document;
+    this.button =
+     this.IUI.chromeDoc.getElementById("inspector-treepanel-toolbutton");
 
     domplateUtils.setDOM(this.window);
 
     this.DOMHelpers = new DOMHelpers(this.window);
 
     let isOpen = this.isOpen.bind(this);
 
-    this.registrationObject = {
-      id: this.id,
-      label: this.IUI.strings.GetStringFromName("htmlPanel.label"),
-      tooltiptext: this.IUI.strings.GetStringFromName("htmlPanel.tooltiptext"),
-      accesskey: this.IUI.strings.GetStringFromName("htmlPanel.accesskey"),
-      context: this,
-      get isOpen() isOpen(),
-      show: this.open,
-      hide: this.close,
-      onSelect: this.select,
-      panel: this.openInDock ? null : this.container,
-      unregister: this.destroy,
-    };
     this.editingEvents = {};
-
-    if (!this.openInDock) {
-      this._boundClose = this.close.bind(this);
-      this.container.addEventListener("popuphiding", this._boundClose, false);
-    }
-
-    // Register the HTML panel with the highlighter
-    this.IUI.registerTool(this.registrationObject);
   },
 
   /**
    * Initialization function for the TreePanel.
    */
   initializeIFrame: function TP_initializeIFrame()
   {
     if (!this.initializingTreePanel || this.treeLoaded) {
@@ -149,138 +126,96 @@ TreePanel.prototype = {
       this.select(this.IUI.selection, true);
   },
 
   /**
    * Open the inspector's tree panel and initialize it.
    */
   open: function TP_open()
   {
-    if (this.initializingTreePanel && !this.treeLoaded) {
+    if (this._open) {
       return;
     }
 
+    this._open = true;
+
+    this.button.setAttribute("checked", true);
     this.initializingTreePanel = true;
-    if (!this.openInDock)
-      this.container.hidden = false;
 
     this.treeIFrame = this.document.getElementById("inspector-tree-iframe");
     if (!this.treeIFrame) {
       this.treeIFrame = this.document.createElement("iframe");
       this.treeIFrame.setAttribute("id", "inspector-tree-iframe");
       this.treeIFrame.flex = 1;
       this.treeIFrame.setAttribute("type", "content");
-    }
-
-    if (this.openInDock) { // Create vbox
-      this.openDocked();
-      return;
+      this.treeIFrame.setAttribute("context", "inspector-node-popup");
     }
 
-    let resizerBox = this.document.getElementById("tree-panel-resizer-box");
-    this.treeIFrame = this.container.insertBefore(this.treeIFrame, resizerBox);
-
-    let boundLoadedInitializeTreePanel = function loadedInitializeTreePanel()
-    {
-      this.treeIFrame.removeEventListener("load",
-        boundLoadedInitializeTreePanel, true);
-      this.initializeIFrame();
-    }.bind(this);
-
-    let boundTreePanelShown = function treePanelShown()
-    {
-      this.container.removeEventListener("popupshown",
-        boundTreePanelShown, false);
-
-      this.treeIFrame.addEventListener("load",
-        boundLoadedInitializeTreePanel, true);
-
-      let src = this.treeIFrame.getAttribute("src");
-      if (src != INSPECTOR_URI) {
-        this.treeIFrame.setAttribute("src", INSPECTOR_URI);
-      } else {
-        this.treeIFrame.contentWindow.location.reload();
-      }
-    }.bind(this);
-
-    this.container.addEventListener("popupshown", boundTreePanelShown, false);
-
-    const panelWidthRatio = 7 / 8;
-    const panelHeightRatio = 1 / 5;
-
-    let width = parseInt(this.IUI.win.outerWidth * panelWidthRatio);
-    let height = parseInt(this.IUI.win.outerHeight * panelHeightRatio);
-    let y = Math.min(this.document.defaultView.screen.availHeight - height,
-      this.IUI.win.innerHeight);
-
-    this.container.openPopup(this.browser, "overlap", 0, 0,
-      false, false);
-
-    this.container.moveTo(80, y);
-    this.container.sizeTo(width, height);
-  },
-
-  openDocked: function TP_openDocked()
-  {
     let treeBox = null;
-    let toolbar = this.IUI.toolbar.nextSibling; // Addons bar, typically
-    let toolbarParent =
-      this.IUI.browser.ownerDocument.getElementById("browser-bottombox");
     treeBox = this.document.createElement("vbox");
     treeBox.id = "inspector-tree-box";
-    treeBox.state = "open"; // for the registerTools API.
+    treeBox.state = "open";
     try {
       treeBox.height =
         Services.prefs.getIntPref("devtools.inspector.htmlHeight");
     } catch(e) {
       treeBox.height = 112;
     }
 
     treeBox.minHeight = 64;
-    treeBox.flex = 1;
-    toolbarParent.insertBefore(treeBox, toolbar);
+
+    this.splitter = this.document.createElement("splitter");
+    this.splitter.id = "inspector-tree-splitter";
 
-    this.IUI.toolbar.setAttribute("treepanel-open", "true");
+    let container = this.document.getElementById("appcontent");
+    container.appendChild(this.splitter);
+    container.appendChild(treeBox);
 
     treeBox.appendChild(this.treeIFrame);
 
-    let boundLoadedInitializeTreePanel = function loadedInitializeTreePanel()
+    this._boundLoadedInitializeTreePanel = function loadedInitializeTreePanel()
     {
       this.treeIFrame.removeEventListener("load",
-        boundLoadedInitializeTreePanel, true);
+        this._boundLoadedInitializeTreePanel, true);
+      delete this._boundLoadedInitializeTreePanel;
       this.initializeIFrame();
     }.bind(this);
 
     this.treeIFrame.addEventListener("load",
-      boundLoadedInitializeTreePanel, true);
+      this._boundLoadedInitializeTreePanel, true);
 
     let src = this.treeIFrame.getAttribute("src");
     if (src != INSPECTOR_URI) {
       this.treeIFrame.setAttribute("src", INSPECTOR_URI);
     } else {
       this.treeIFrame.contentWindow.location.reload();
     }
   },
 
   /**
    * Close the TreePanel.
    */
   close: function TP_close()
   {
-    if (this.openInDock) {
-      this.IUI.toolbar.removeAttribute("treepanel-open");
+    this._open = false;
 
-      let treeBox = this.container;
-      Services.prefs.setIntPref("devtools.inspector.htmlHeight", treeBox.height);
-      let treeBoxParent = treeBox.parentNode;
-      treeBoxParent.removeChild(treeBox);
-    } else {
-      this.container.hidePopup();
+    // Stop caring about the tree iframe load if it's in progress.
+    if (this._boundLoadedInitializeTreePanel) {
+      this.treeIFrame.removeEventListener("load",
+        this._boundLoadedInitializeTreePanel, true);
+      delete this._boundLoadedInitializeTreePanel;
     }
 
+    this.button.removeAttribute("checked");
+    let treeBox = this.container;
+    Services.prefs.setIntPref("devtools.inspector.htmlHeight", treeBox.height);
+    let treeBoxParent = treeBox.parentNode;
+    treeBoxParent.removeChild(this.splitter);
+    treeBoxParent.removeChild(treeBox);
+
     if (this.treePanelDiv) {
       this.treePanelDiv.ownerPanel = null;
       let parent = this.treePanelDiv.parentNode;
       parent.removeChild(this.treePanelDiv);
       delete this.treePanelDiv;
       delete this.treeBrowserDocument;
     }
 
@@ -288,20 +223,25 @@ TreePanel.prototype = {
   },
 
   /**
    * Is the TreePanel open?
    * @returns boolean
    */
   isOpen: function TP_isOpen()
   {
-    if (this.openInDock)
-      return this.treeLoaded && this.container;
+    return this._open;
+  },
 
-    return this.treeLoaded && this.container.state == "open";
+  /**
+   * Toggle the TreePanel.
+   */
+  toggle: function TP_toggle()
+  {
+    this.isOpen() ? this.close() : this.open();
   },
 
   /**
    * Create the ObjectBox for the given object.
    * @param object nsIDOMNode
    * @param isRoot boolean - Is this the root object?
    * @returns InsideOutBox
    */
@@ -665,16 +605,30 @@ TreePanel.prototype = {
         else
           return child.repObject;
       }
     }
     return null;
   },
 
   /**
+   * Remove a node box from the tree view.
+   * @param aElement
+   *        The DOM node to remove from the HTML IOBox.
+   */
+  deleteChildBox: function TP_deleteChildBox(aElement)
+  {
+    let childBox = this.ioBox.findObjectBox(aElement);
+    if (!childBox) {
+      return;
+    }
+    childBox.parentNode.removeChild(childBox);
+  },
+
+  /**
    * Destructor function. Cleanup.
    */
   destroy: function TP_destroy()
   {
     if (this.isOpen()) {
       this.close();
     }
 
@@ -700,21 +654,16 @@ TreePanel.prototype = {
       parent.removeChild(this.treeIFrame);
       delete this.treeIFrame;
     }
 
     if (this.ioBox) {
       this.ioBox.destroy();
       delete this.ioBox;
     }
-
-    if (!this.openInDock) {
-      this.container.removeEventListener("popuphiding", this._boundClose, false);
-      delete this._boundClose;
-    }
   }
 };
 
 
 /**
  * DOMHelpers
  * Makes DOM traversal easier. Goes through iframes.
  *
--- a/browser/devtools/highlighter/highlighter.jsm
+++ b/browser/devtools/highlighter/highlighter.jsm
@@ -38,32 +38,39 @@
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 const Cu = Components.utils;
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+
 Cu.import("resource:///modules/devtools/LayoutHelpers.jsm");
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 var EXPORTED_SYMBOLS = ["Highlighter"];
 
 const INSPECTOR_INVISIBLE_ELEMENTS = {
   "head": true,
   "base": true,
   "basefont": true,
   "isindex": true,
   "link": true,
   "meta": true,
   "script": true,
   "style": true,
   "title": true,
 };
 
+const PSEUDO_CLASSES = [":hover", ":active", ":focus"];
+  // add ":visited" and ":link" after bug 713106 is fixed
+
 /**
  * A highlighter mechanism.
  *
  * The highlighter is built dynamically into the browser element.
  * The caller is in charge of destroying the highlighter (ie, the highlighter
  * won't be destroyed if a new tab is selected for example).
  *
  * API:
@@ -104,16 +111,18 @@ const INSPECTOR_INVISIBLE_ELEMENTS = {
  *
  * Events:
  *
  *   "closed" - Highlighter is closing
  *   "nodeselected" - A new node has been selected
  *   "highlighting" - Highlighter is highlighting
  *   "locked" - The selected node has been locked
  *   "unlocked" - The selected ndoe has been unlocked
+ *   "pseudoclasstoggled" - A pseudo-class lock has changed on the selected node
+
  *
  * Structure:
  *
  *   <stack id="highlighter-container">
  *     <vbox id="highlighter-veil-container">...</vbox>
  *     <box id="highlighter-controls>...</vbox>
  *   </stack>
  *
@@ -234,16 +243,27 @@ Highlighter.prototype = {
     this.invalidateSize(!!aScroll);
 
     if (oldNode !== this.node) {
       this.emitEvent("nodeselected");
     }
   },
 
   /**
+   * Notify that a pseudo-class lock was toggled on the highlighted element
+   *
+   * @param aPseudo - The pseudo-class to toggle, e.g. ":hover".
+   */
+  pseudoClassLockToggled: function Highlighter_pseudoClassLockToggled(aPseudo)
+  {  
+    this.emitEvent("pseudoclasstoggled", [aPseudo]);
+    this.updateInfobar();
+  },
+
+  /**
    * Update the highlighter size and position.
    */
   invalidateSize: function Highlighter_invalidateSize(aScroll)
   {
     let rect = null;
 
     if (this.node && this.isNodeHighlightable(this.node)) {
 
@@ -441,40 +461,91 @@ Highlighter.prototype = {
     let tagNameLabel = this.chromeDoc.createElementNS("http://www.w3.org/1999/xhtml", "span");
     tagNameLabel.id = "highlighter-nodeinfobar-tagname";
 
     let idLabel = this.chromeDoc.createElementNS("http://www.w3.org/1999/xhtml", "span");
     idLabel.id = "highlighter-nodeinfobar-id";
 
     let classesBox = this.chromeDoc.createElementNS("http://www.w3.org/1999/xhtml", "span");
     classesBox.id = "highlighter-nodeinfobar-classes";
+    
+    let pseudoClassesBox = this.chromeDoc.createElementNS("http://www.w3.org/1999/xhtml", "span");
+    pseudoClassesBox.id = "highlighter-nodeinfobar-pseudo-classes";
+    
     // Add some content to force a better boundingClientRect down below.
-    classesBox.textContent = "&nbsp;";
+    pseudoClassesBox.textContent = "&nbsp;";
 
     nodeInfobar.appendChild(tagNameLabel);
     nodeInfobar.appendChild(idLabel);
     nodeInfobar.appendChild(classesBox);
+    nodeInfobar.appendChild(pseudoClassesBox);
     container.appendChild(arrowBoxTop);
     container.appendChild(nodeInfobar);
     container.appendChild(arrowBoxBottom);
 
     aParent.appendChild(container);
 
+    nodeInfobar.onclick = (function _onInfobarRightClick(aEvent) {
+      if (aEvent.button == 2) {
+        this.openPseudoClassMenu();
+      }
+    }).bind(this);
+
     let barHeight = container.getBoundingClientRect().height;
 
     this.nodeInfo = {
       tagNameLabel: tagNameLabel,
       idLabel: idLabel,
       classesBox: classesBox,
+      pseudoClassesBox: pseudoClassesBox,
       container: container,
       barHeight: barHeight,
     };
   },
 
   /**
+   * Open the infobar's pseudo-class context menu.
+   */
+  openPseudoClassMenu: function Highlighter_openPseudoClassMenu()
+  {
+    let menu = this.chromeDoc.createElement("menupopup");
+    menu.id = "infobar-context-menu";
+
+    let popupSet = this.chromeDoc.getElementById("mainPopupSet");
+    popupSet.appendChild(menu);
+    
+    let fragment = this.buildPseudoClassMenu();
+    menu.appendChild(fragment);
+
+    menu.openPopup(this.nodeInfo.pseudoClassesBox, "end_before", 0, 0, true, false);
+  },  
+  
+  /**
+   * Create the menuitems for toggling the selection's pseudo-class state
+   *
+   * @returns DocumentFragment. The menuitems for toggling pseudo-classes.
+   */
+  buildPseudoClassMenu: function IUI_buildPseudoClassesMenu()
+  {
+    let fragment = this.chromeDoc.createDocumentFragment();
+    for (let i = 0; i < PSEUDO_CLASSES.length; i++) {
+      let pseudo = PSEUDO_CLASSES[i];
+      let item = this.chromeDoc.createElement("menuitem");
+      item.setAttribute("type", "checkbox");
+      item.setAttribute("label", pseudo);
+      item.addEventListener("command",
+                            this.pseudoClassLockToggled.bind(this, pseudo), false);
+      item.setAttribute("checked", DOMUtils.hasPseudoClassLock(this.node,
+                         pseudo));
+      fragment.appendChild(item);
+    }
+    return fragment;
+  },
+
+  /**
    * Highlight a rectangular region.
    *
    * @param object aRect
    *        The rectangle region to highlight.
    * @returns boolean
    *          True if the rectangle was highlighted, false otherwise.
    */
   highlightRectangle: function Highlighter_highlightRectangle(aRect)
@@ -538,16 +609,24 @@ Highlighter.prototype = {
     // ID
     this.nodeInfo.idLabel.textContent = this.node.id ? "#" + this.node.id : "";
 
     // Classes
     let classes = this.nodeInfo.classesBox;
 
     classes.textContent = this.node.classList.length ?
                             "." + Array.join(this.node.classList, ".") : "";
+
+    // Pseudo-classes
+    let pseudos = PSEUDO_CLASSES.filter(function(pseudo) {
+      return DOMUtils.hasPseudoClassLock(this.node, pseudo);
+    }, this);
+
+    let pseudoBox = this.nodeInfo.pseudoClassesBox;
+    pseudoBox.textContent = pseudos.join("");
   },
 
   /**
    * Move the Infobar to the right place in the highlighter.
    */
   moveInfobar: function Highlighter_moveInfobar()
   {
     if (this._highlightRect) {
@@ -612,18 +691,18 @@ Highlighter.prototype = {
     }
   },
 
   /**
    * Store page zoom factor.
    */
   computeZoomFactor: function Highlighter_computeZoomFactor() {
     this.zoom =
-      this.win.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
-      .getInterface(Components.interfaces.nsIDOMWindowUtils)
+      this.win.QueryInterface(Ci.nsIInterfaceRequestor)
+      .getInterface(Ci.nsIDOMWindowUtils)
       .screenPixelsPerCSSPixel;
   },
 
   /////////////////////////////////////////////////////////////////////////
   //// Event Emitter Mechanism
 
   addListener: function Highlighter_addListener(aEvent, aListener)
   {
@@ -800,8 +879,11 @@ Highlighter.prototype = {
     if (element && element != this.node) {
       this.highlight(element);
     }
   },
 };
 
 ///////////////////////////////////////////////////////////////////////////
 
+XPCOMUtils.defineLazyGetter(this, "DOMUtils", function () {
+  return Cc["@mozilla.org/inspector/dom-utils;1"].getService(Ci.inIDOMUtils)
+});
--- a/browser/devtools/highlighter/inspector.jsm
+++ b/browser/devtools/highlighter/inspector.jsm
@@ -37,16 +37,17 @@
  * use your version of this file under the terms of the MPL, indicate your
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
+const Cc = Components.classes;
 const Cu = Components.utils;
 const Ci = Components.interfaces;
 const Cr = Components.results;
 
 var EXPORTED_SYMBOLS = ["InspectorUI"];
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
@@ -77,16 +78,18 @@ const INSPECTOR_NOTIFICATIONS = {
   RULEVIEWREADY: "inspector-ruleview-ready",
 
   // Event notifications for the attribute-value editor
   EDITOR_OPENED: "inspector-editor-opened",
   EDITOR_CLOSED: "inspector-editor-closed",
   EDITOR_SAVED: "inspector-editor-saved",
 };
 
+const PSEUDO_CLASSES = [":hover", ":active", ":focus"];
+
 ///////////////////////////////////////////////////////////////////////////
 //// InspectorUI
 
 /**
  * Main controller class for the Inspector.
  *
  * @constructor
  * @param nsIDOMWindow aWindow
@@ -103,17 +106,16 @@ function InspectorUI(aWindow)
   this.INSPECTOR_NOTIFICATIONS = INSPECTOR_NOTIFICATIONS;
 }
 
 InspectorUI.prototype = {
   browser: null,
   tools: null,
   toolEvents: null,
   inspecting: false,
-  treePanelEnabled: true,
   ruleViewEnabled: true,
   isDirty: false,
   store: null,
 
   /**
    * Toggle the inspector interface elements on or off.
    *
    * @param aEvent
@@ -132,49 +134,87 @@ InspectorUI.prototype = {
    * Show the Sidebar.
    */
   showSidebar: function IUI_showSidebar()
   {
     this.sidebarBox.removeAttribute("hidden");
     this.sidebarSplitter.removeAttribute("hidden");
     this.stylingButton.checked = true;
 
-    // Activate the first tool in the sidebar, only if none previously-
-    // selected. We'll want to do a followup to remember selected tool-states.
+    // If no tool is already selected, show the last-used sidebar if available,
+    // otherwise just show the first.
+
     if (!Array.some(this.sidebarToolbar.children,
       function(btn) btn.hasAttribute("checked"))) {
-        let firstButtonId = this.getToolbarButtonId(this.sidebarTools[0].id);
-        this.chromeDoc.getElementById(firstButtonId).click();
+
+      let activePanel = this.sidebarTools[0];
+      let activeId = this.store.getValue(this.winID, "activeSidebar");
+      if (activeId && this.tools[activeId]) {
+        activePanel = this.tools[activeId];
+      }
+      this.activateSidebarPanel(activePanel.id);
     }
+
+    this.store.setValue(this.winID, "sidebarOpen", true);
+    Services.prefs.setBoolPref("devtools.inspector.sidebarOpen", true);
   },
 
   /**
-   * Hide the Sidebar.
+   * Tear down the sidebar.
    */
-  hideSidebar: function IUI_hideSidebar()
+  _destroySidebar: function IUI_destroySidebar()
   {
     this.sidebarBox.setAttribute("hidden", "true");
     this.sidebarSplitter.setAttribute("hidden", "true");
     this.stylingButton.checked = false;
   },
 
   /**
+   * Hide the sidebar.
+   */
+  hideSidebar: function IUI_hideSidebar()
+  {
+    this._destroySidebar();
+    this.store.setValue(this.winID, "sidebarOpen", false);
+    Services.prefs.setBoolPref("devtools.inspector.sidebarOpen", false);
+  },
+
+  /**
    * Show or hide the sidebar. Called from the Styling button on the
    * highlighter toolbar.
    */
   toggleSidebar: function IUI_toggleSidebar()
   {
     if (!this.isSidebarOpen) {
       this.showSidebar();
     } else {
       this.hideSidebar();
     }
   },
 
   /**
+   * Activate a sidebar panel by id.
+   */
+  activateSidebarPanel: function IUI_activateSidebarPanel(aID)
+  {
+    let buttonId = this.getToolbarButtonId(aID);
+    this.chromeDoc.getElementById(buttonId).click();
+  },
+
+  get activeSidebarPanel()
+  {
+    for each (let tool in this.sidebarTools) {
+      if (this.sidebarDeck.selectedPanel == this.getToolIframe(tool)) {
+        return tool.id;
+      }
+    }
+    return null;
+  },
+
+  /**
    * Getter to test if the Sidebar is open or not.
    */
   get isSidebarOpen()
   {
     return this.stylingButton.checked &&
           !this.sidebarBox.hidden &&
           !this.sidebarSplitter.hidden;
   },
@@ -188,16 +228,32 @@ InspectorUI.prototype = {
     if (this.inspecting) {
       this.stopInspecting();
     } else {
       this.startInspecting();
     }
   },
 
   /**
+   * Toggle the TreePanel.
+   */
+  toggleHTMLPanel: function TP_toggle()
+  {
+    if (this.treePanel.isOpen()) {
+      this.treePanel.close();
+      Services.prefs.setBoolPref("devtools.inspector.htmlPanelOpen", false);
+      this.store.setValue(this.winID, "htmlPanelOpen", false);
+    } else {
+      this.treePanel.open();
+      Services.prefs.setBoolPref("devtools.inspector.htmlPanelOpen", true);
+      this.store.setValue(this.winID, "htmlPanelOpen", true);
+    }
+  },
+
+  /**
    * Is the inspector UI open? Simply check if the toolbar is visible or not.
    *
    * @returns boolean
    */
   get isInspectorOpen()
   {
     return this.toolbar && !this.toolbar.hidden && this.highlighter;
   },
@@ -255,19 +311,17 @@ InspectorUI.prototype = {
     this.toolbar = this.chromeDoc.getElementById("inspector-toolbar");
     this.inspectMenuitem = this.chromeDoc.getElementById("Tools:Inspect");
     this.inspectToolbutton =
       this.chromeDoc.getElementById("inspector-inspect-toolbutton");
 
     this.initTools();
     this.chromeWin.Tilt.setup();
 
-    if (this.treePanelEnabled) {
-      this.treePanel = new TreePanel(this.chromeWin, this);
-    }
+    this.treePanel = new TreePanel(this.chromeWin, this);
 
     if (Services.prefs.getBoolPref("devtools.ruleview.enabled") &&
         !this.toolRegistered("ruleview")) {
       this.registerRuleView();
     }
 
     if (Services.prefs.getBoolPref("devtools.styleinspector.enabled") &&
         !this.toolRegistered("styleinspector")) {
@@ -305,16 +359,17 @@ InspectorUI.prototype = {
       label: this.strings.GetStringFromName("ruleView.label"),
       tooltiptext: this.strings.GetStringFromName("ruleView.tooltiptext"),
       accesskey: this.strings.GetStringFromName("ruleView.accesskey"),
       context: this,
       get isOpen() isOpen(),
       show: this.openRuleView,
       hide: this.closeRuleView,
       onSelect: this.selectInRuleView,
+      onChanged: this.changeInRuleView,
       panel: null,
       unregister: this.destroyRuleView,
       sidebar: true,
     };
 
     this.registerTool(this.ruleViewObject);
   },
 
@@ -344,16 +399,26 @@ InspectorUI.prototype = {
       }
       this.isDirty = this.store.getValue(this.winID, "isDirty");
     } else {
       // First time inspecting, set state to no selection + live inspection.
       this.store.addStore(this.winID);
       this.store.setValue(this.winID, "selectedNode", null);
       this.store.setValue(this.winID, "inspecting", true);
       this.store.setValue(this.winID, "isDirty", this.isDirty);
+
+      this.store.setValue(this.winID, "htmlPanelOpen",
+        Services.prefs.getBoolPref("devtools.inspector.htmlPanelOpen"));
+
+      this.store.setValue(this.winID, "sidebarOpen",
+        Services.prefs.getBoolPref("devtools.inspector.sidebarOpen"));
+
+      this.store.setValue(this.winID, "activeSidebar",
+        Services.prefs.getCharPref("devtools.inspector.activeSidebar"));
+
       this.win.addEventListener("pagehide", this, true);
     }
   },
 
   /**
    * Browse nodes according to the breadcrumbs layout, only for some specific
    * elements of the UI.
    */
@@ -395,16 +460,18 @@ InspectorUI.prototype = {
    */
   closeInspectorUI: function IUI_closeInspectorUI(aKeepStore)
   {
     // if currently editing an attribute value, closing the
     // highlighter/HTML panel dismisses the editor
     if (this.treePanel && this.treePanel.editingContext)
       this.treePanel.closeEditor();
 
+    this.treePanel.destroy();
+
     if (this.closing || !this.win || !this.browser) {
       return;
     }
 
     let winId = new String(this.winID); // retain this to notify observers.
 
     this.closing = true;
     this.toolbar.hidden = true;
@@ -412,16 +479,17 @@ InspectorUI.prototype = {
     this.removeNavigationKeys();
 
     this.progressListener.destroy();
     delete this.progressListener;
 
     if (!aKeepStore) {
       this.store.deleteStore(this.winID);
       this.win.removeEventListener("pagehide", this, true);
+      this.clearPseudoClassLocks();
     } else {
       // Update the store before closing.
       if (this.selection) {
         this.store.setValue(this.winID, "selectedNode",
           this.selection);
       }
       this.store.setValue(this.winID, "inspecting", this.inspecting);
       this.store.setValue(this.winID, "isDirty", this.isDirty);
@@ -430,23 +498,22 @@ InspectorUI.prototype = {
     if (this.store.isEmpty()) {
       this.tabbrowser.tabContainer.removeEventListener("TabSelect", this, false);
     }
 
     this.chromeWin.removeEventListener("keypress", this, false);
 
     this.stopInspecting();
 
-    this.saveToolState(this.winID);
     this.toolsDo(function IUI_toolsHide(aTool) {
       this.unregisterTool(aTool);
     }.bind(this));
 
     // close the sidebar
-    this.hideSidebar();
+    this._destroySidebar();
 
     if (this.highlighter) {
       this.highlighter.destroy();
       this.highlighter = null;
     }
 
     if (this.breadcrumbs) {
       this.breadcrumbs.destroy();
@@ -498,65 +565,108 @@ InspectorUI.prototype = {
       return;
     }
 
     this.inspectToolbutton.checked = false;
 
     this.inspecting = false;
     this.toolsDim(false);
     if (this.highlighter.getNode()) {
-      this.select(this.highlighter.getNode(), true, true, !aPreventScroll);
+      this.select(this.highlighter.getNode(), true, !aPreventScroll);
     } else {
       this.select(null, true, true);
     }
     this.highlighter.lock();
   },
 
   /**
-   * Select an object in the tree view.
+   * Select an object in the inspector.
    * @param aNode
    *        node to inspect
    * @param forceUpdate
    *        force an update?
    * @param aScroll boolean
    *        scroll the tree panel?
+   * @param aFrom [optional] string
+   *        which part of the UI the selection occured from
    */
-  select: function IUI_select(aNode, forceUpdate, aScroll)
+  select: function IUI_select(aNode, forceUpdate, aScroll, aFrom)
   {
     // if currently editing an attribute value, using the
     // highlighter dismisses the editor
     if (this.treePanel && this.treePanel.editingContext)
       this.treePanel.closeEditor();
 
     if (!aNode)
       aNode = this.defaultSelection;
 
     if (forceUpdate || aNode != this.selection) {
+      if (aFrom != "breadcrumbs") {
+        this.clearPseudoClassLocks();
+      }
+      
       this.selection = aNode;
       if (!this.inspecting) {
         this.highlighter.highlight(this.selection);
       }
     }
 
     this.breadcrumbs.update();
     this.chromeWin.Tilt.update(aNode);
+    this.treePanel.select(aNode, aScroll);
 
     this.toolsSelect(aScroll);
   },
+  
+  /**
+   * Toggle the pseudo-class lock on the currently inspected element. If the
+   * pseudo-class is :hover or :active, that pseudo-class will also be toggled
+   * on every ancestor of the element, mirroring real :hover and :active
+   * behavior.
+   * 
+   * @param aPseudo the pseudo-class lock to toggle, e.g. ":hover"
+   */
+  togglePseudoClassLock: function IUI_togglePseudoClassLock(aPseudo)
+  {
+    if (DOMUtils.hasPseudoClassLock(this.selection, aPseudo)) {
+      this.breadcrumbs.nodeHierarchy.forEach(function(crumb) {
+        DOMUtils.removePseudoClassLock(crumb.node, aPseudo);
+      });
+    } else {
+      let hierarchical = aPseudo == ":hover" || aPseudo == ":active";
+      let node = this.selection;
+      do {
+        DOMUtils.addPseudoClassLock(node, aPseudo);
+        node = node.parentNode;
+      } while (hierarchical && node.parentNode)
+    }
+    this.nodeChanged();
+  },
+
+  /**
+   * Clear all pseudo-class locks applied to elements in the node hierarchy
+   */
+  clearPseudoClassLocks: function IUI_clearPseudoClassLocks()
+  {
+    this.breadcrumbs.nodeHierarchy.forEach(function(crumb) {
+      DOMUtils.clearPseudoClassLocks(crumb.node);
+    });
+  },
 
   /**
    * Called when the highlighted node is changed by a tool.
    *
    * @param object aUpdater
    *        The tool that triggered the update (if any), that tool's
    *        onChanged will not be called.
    */
   nodeChanged: function IUI_nodeChanged(aUpdater)
   {
     this.highlighter.invalidateSize();
+    this.breadcrumbs.updateSelectors();
     this.toolsOnChanged(aUpdater);
   },
 
   /////////////////////////////////////////////////////////////////////////
   //// Event Handling
 
   highlighterReady: function IUI_highlighterReady()
   {
@@ -572,27 +682,42 @@ InspectorUI.prototype = {
     this.highlighter.addListener("unlocked", function() {
       self.startInspecting();
     });
 
     this.highlighter.addListener("nodeselected", function() {
       self.select(self.highlighter.getNode(), false, false);
     });
 
+    this.highlighter.addListener("pseudoclasstoggled", function(aPseudo) {
+      self.togglePseudoClassLock(aPseudo);
+    });
+
     if (this.store.getValue(this.winID, "inspecting")) {
       this.startInspecting();
+      this.highlighter.unlock();
+    } else {
+      this.highlighter.lock();
     }
 
-    this.restoreToolState(this.winID);
+    Services.obs.notifyObservers(null, INSPECTOR_NOTIFICATIONS.STATE_RESTORED, null);
 
     this.win.focus();
+    this.highlighter.highlight();
+
+    if (this.store.getValue(this.winID, "htmlPanelOpen")) {
+      this.treePanel.open();
+    }
+
+    if (this.store.getValue(this.winID, "sidebarOpen")) {
+      this.showSidebar();
+    }
+
     Services.obs.notifyObservers({wrappedJSObject: this},
                                  INSPECTOR_NOTIFICATIONS.OPENED, null);
-
-    this.highlighter.highlight();
   },
 
   /**
    * Main callback handler for events.
    *
    * @param event
    *        The event to be handled.
    */
@@ -709,16 +834,56 @@ InspectorUI.prototype = {
           this.highlighter.highlight(node, true);
         }
         event.preventDefault();
         event.stopPropagation();
         break;
     }
   },
 
+  /**
+   * Copy the innerHTML of the selected Node to the clipboard. Called via the
+   * Inspector:CopyInner command.
+   */
+  copyInnerHTML: function IUI_copyInnerHTML()
+  {
+    let clipboard = Cc["@mozilla.org/widget/clipboardhelper;1"].
+                    getService(Ci.nsIClipboardHelper);
+    clipboard.copyString(this.selection.innerHTML);
+  },
+
+  /**
+   * Copy the outerHTML of the selected Node to the clipboard. Called via the
+   * Inspector:CopyOuter command.
+   */
+  copyOuterHTML: function IUI_copyOuterHTML()
+  {
+    let clipboard = Cc["@mozilla.org/widget/clipboardhelper;1"].
+                    getService(Ci.nsIClipboardHelper);
+    clipboard.copyString(this.selection.outerHTML);
+  },
+
+  /**
+   * Delete the selected node. Called via the Inspector:DeleteNode command.
+   */
+  deleteNode: function IUI_deleteNode()
+  {
+    let selection = this.selection;
+    let parent = this.selection.parentNode;
+
+    // remove the node from the treepanel
+    this.treePanel.deleteChildBox(selection);
+
+    // remove the node from content
+    parent.removeChild(selection);
+    this.breadcrumbs.invalidateHierarchy();
+
+    // select the parent node in the highlighter, treepanel, breadcrumbs
+    this.inspectNode(parent);
+  },
 
   /////////////////////////////////////////////////////////////////////////
   //// CssRuleView methods
 
   /**
    * Is the cssRuleView open?
    */
   isRuleViewOpen: function IUI_isRuleViewOpen()
@@ -791,16 +956,25 @@ InspectorUI.prototype = {
    * Update the selected node in the Css Rule View.
    * @param {nsIDOMnode} the selected node.
    */
   selectInRuleView: function IUI_selectInRuleView(aNode)
   {
     if (this.ruleView)
       this.ruleView.highlight(aNode);
   },
+  
+  /**
+   * Update the rules for the current node in the Css Rule View.
+   */
+  changeInRuleView: function IUI_selectInRuleView()
+  {
+    if (this.ruleView)
+      this.ruleView.nodeChanged();
+  },
 
   ruleViewChanged: function IUI_ruleViewChanged()
   {
     this.isDirty = true;
     this.nodeChanged(this.ruleViewObject);
   },
 
   /**
@@ -1082,16 +1256,18 @@ InspectorUI.prototype = {
    * Show the specified tool.
    * @param aTool Object (see comment for IUI_registerTool)
    */
   toolShow: function IUI_toolShow(aTool)
   {
     let btn = this.chromeDoc.getElementById(this.getToolbarButtonId(aTool.id));
     btn.setAttribute("checked", "true");
     if (aTool.sidebar) {
+      Services.prefs.setCharPref("devtools.inspector.activeSidebar", aTool.id);
+      this.store.setValue(this.winID, "activeSidebar", aTool.id);
       this.sidebarDeck.selectedPanel = this.getToolIframe(aTool);
       this.sidebarTools.forEach(function(other) {
         if (other != aTool)
           this.chromeDoc.getElementById(
             this.getToolbarButtonId(other.id)).removeAttribute("checked");
       }.bind(this));
     }
 
@@ -1176,67 +1352,16 @@ InspectorUI.prototype = {
     // the iframe.
     if (aRegObj.unregister)
       aRegObj.unregister.call(aRegObj.context);
 
     delete this.tools[aRegObj.id];
   },
 
   /**
-   * Save a list of open tools to the inspector store.
-   *
-   * @param aWinID The ID of the window used to save the associated tools
-   */
-  saveToolState: function IUI_saveToolState(aWinID)
-  {
-    let openTools = {};
-    this.toolsDo(function IUI_toolsSetId(aTool) {
-      if (aTool.isOpen) {
-        openTools[aTool.id] = true;
-      }
-    });
-    this.store.setValue(aWinID, "openTools", openTools);
-  },
-
-  /**
-   * Restore tools previously save using saveToolState().
-   *
-   * @param aWinID The ID of the window to which the associated tools are to be
-   *               restored.
-   */
-  restoreToolState: function IUI_restoreToolState(aWinID)
-  {
-    let openTools = this.store.getValue(aWinID, "openTools");
-    let activeSidebarTool;
-    if (openTools) {
-      this.toolsDo(function IUI_toolsOnShow(aTool) {
-        if (aTool.id in openTools) {
-          if (aTool.sidebar && !this.isSidebarOpen) {
-            this.showSidebar();
-            activeSidebarTool = aTool;
-          }
-          this.toolShow(aTool);
-        }
-      }.bind(this));
-      this.sidebarTools.forEach(function(tool) {
-        if (tool != activeSidebarTool)
-          this.chromeDoc.getElementById(
-            this.getToolbarButtonId(tool.id)).removeAttribute("checked");
-      }.bind(this));
-    }
-    if (this.store.getValue(this.winID, "inspecting")) {
-      this.highlighter.unlock();
-    } else {
-      this.highlighter.lock();
-    }
-
-    Services.obs.notifyObservers(null, INSPECTOR_NOTIFICATIONS.STATE_RESTORED, null);
-  },
-
-  /**
    * For each tool in the tools collection select the current node that is
    * selected in the highlighter
    * @param aScroll boolean
    *        Do you want to scroll the treepanel?
    */
   toolsSelect: function IUI_toolsSelect(aScroll)
   {
     let selection = this.selection;
@@ -1249,33 +1374,33 @@ InspectorUI.prototype = {
 
   /**
    * Dim or undim each tool in the tools collection
    * @param aState true = dim, false = undim
    */
   toolsDim: function IUI_toolsDim(aState)
   {
     this.toolsDo(function IUI_toolsDim(aTool) {
-      if (aTool.isOpen && "dim" in aTool) {
+      if ("dim" in aTool) {
         aTool.dim.call(aTool.context, aState);
       }
     });
   },
 
   /**
    * Notify registered tools of changes to the highlighted element.
    *
    * @param object aUpdater
    *        The tool that triggered the update (if any), that tool's
    *        onChanged will not be called.
    */
   toolsOnChanged: function IUI_toolsChanged(aUpdater)
   {
     this.toolsDo(function IUI_toolsOnChanged(aTool) {
-      if (aTool.isOpen && ("onChanged" in aTool) && aTool != aUpdater) {
+      if (("onChanged" in aTool) && aTool != aUpdater) {
         aTool.onChanged.call(aTool.context);
       }
     });
   },
 
   /**
    * Loop through all registered tools and pass each into the provided function
    * @param aFunction The function to which each tool is to be passed
@@ -1659,16 +1784,23 @@ HTMLBreadcrumbs.prototype = {
   {
     let text = aNode.tagName.toLowerCase();
     if (aNode.id) {
       text += "#" + aNode.id;
     }
     for (let i = 0; i < aNode.classList.length; i++) {
       text += "." + aNode.classList[i];
     }
+    for (let i = 0; i < PSEUDO_CLASSES.length; i++) {
+      let pseudo = PSEUDO_CLASSES[i];
+      if (DOMUtils.hasPseudoClassLock(aNode, pseudo)) {
+        text += pseudo;  
+      }      
+    }
+
     return text;
   },
 
 
   /**
    * Build <label>s that represent the node:
    *   <label class="inspector-breadcrumbs-tag">tagName</label>
    *   <label class="inspector-breadcrumbs-id">#id</label>
@@ -1684,29 +1816,38 @@ HTMLBreadcrumbs.prototype = {
     let tagLabel = this.IUI.chromeDoc.createElement("label");
     tagLabel.className = "inspector-breadcrumbs-tag plain";
 
     let idLabel = this.IUI.chromeDoc.createElement("label");
     idLabel.className = "inspector-breadcrumbs-id plain";
 
     let classesLabel = this.IUI.chromeDoc.createElement("label");
     classesLabel.className = "inspector-breadcrumbs-classes plain";
+    
+    let pseudosLabel = this.IUI.chromeDoc.createElement("label");
+    pseudosLabel.className = "inspector-breadcrumbs-pseudo-classes plain";
 
     tagLabel.textContent = aNode.tagName.toLowerCase();
     idLabel.textContent = aNode.id ? ("#" + aNode.id) : "";
 
     let classesText = "";
     for (let i = 0; i < aNode.classList.length; i++) {
       classesText += "." + aNode.classList[i];
     }
     classesLabel.textContent = classesText;
 
+    let pseudos = PSEUDO_CLASSES.filter(function(pseudo) {
+      return DOMUtils.hasPseudoClassLock(aNode, pseudo);
+    }, this);
+    pseudosLabel.textContent = pseudos.join("");
+
     fragment.appendChild(tagLabel);
     fragment.appendChild(idLabel);
     fragment.appendChild(classesLabel);
+    fragment.appendChild(pseudosLabel);
 
     return fragment;
   },
 
   /**
    * Open the sibling menu.
    *
    * @param aButton the button representing the node.
@@ -1736,17 +1877,17 @@ HTMLBreadcrumbs.prototype = {
           item.setAttribute("checked", "true");
         }
 
         item.setAttribute("type", "radio");
         item.setAttribute("label", this.prettyPrintNodeAsText(nodes[i]));
 
         item.onmouseup = (function(aNode) {
           return function() {
-            inspector.select(aNode, true, true);
+            inspector.select(aNode, true, true, "breadcrumbs");
           }
         })(nodes[i]);
 
         fragment.appendChild(item);
       }
     }
     this.menu.appendChild(fragment);
     this.menu.openPopup(aButton, "before_start", 0, 0, true, false);
@@ -1890,17 +2031,17 @@ HTMLBreadcrumbs.prototype = {
     let inspector = this.IUI;
     button.appendChild(this.prettyPrintNodeAsXUL(aNode));
     button.className = "inspector-breadcrumbs-button";
 
     button.setAttribute("tooltiptext", this.prettyPrintNodeAsText(aNode));
 
     button.onBreadcrumbsClick = function onBreadcrumbsClick() {
       inspector.stopInspecting();
-      inspector.select(aNode, true, true);
+      inspector.select(aNode, true, true, "breadcrumbs");
     };
 
     button.onclick = (function _onBreadcrumbsRightClick(aEvent) {
       if (aEvent.button == 2) {
         this.openSiblingMenu(button, aNode);
       }
     }).bind(this);
 
@@ -2005,16 +2146,30 @@ HTMLBreadcrumbs.prototype = {
   scroll: function BC_scroll()
   {
     // FIXME bug 684352: make sure its immediate neighbors are visible too.
 
     let scrollbox = this.container;
     let element = this.nodeHierarchy[this.currentIndex].button;
     scrollbox.ensureElementIsVisible(element);
   },
+  
+  updateSelectors: function BC_updateSelectors()
+  {
+    for (let i = this.nodeHierarchy.length - 1; i >= 0; i--) {
+      let crumb = this.nodeHierarchy[i];
+      let button = crumb.button;
+
+      while(button.hasChildNodes()) {
+        button.removeChild(button.firstChild);
+      }
+      button.appendChild(this.prettyPrintNodeAsXUL(crumb.node));
+      button.setAttribute("tooltiptext", this.prettyPrintNodeAsText(crumb.node));
+    }
+  },
 
   /**
    * Update the breadcrumbs display when a new node is selected.
    */
   update: function BC_update()
   {
     this.menu.hidePopup();
 
@@ -2046,16 +2201,18 @@ HTMLBreadcrumbs.prototype = {
       idx = this.indexOf(selection);
       this.setCursor(idx);
     }
     // Add the first child of the very last node of the breadcrumbs if possible.
     this.ensureFirstChild();
 
     // Make sure the selected node and its neighbours are visible.
     this.scroll();
+
+    this.updateSelectors();
   },
 
 }
 
 /////////////////////////////////////////////////////////////////////////
 //// Initializers
 
 XPCOMUtils.defineLazyGetter(InspectorUI.prototype, "strings",
@@ -2065,8 +2222,11 @@ XPCOMUtils.defineLazyGetter(InspectorUI.
   });
 
 XPCOMUtils.defineLazyGetter(this, "StyleInspector", function () {
   var obj = {};
   Cu.import("resource:///modules/devtools/StyleInspector.jsm", obj);
   return obj.StyleInspector;
 });
 
+XPCOMUtils.defineLazyGetter(this, "DOMUtils", function () {
+  return Cc["@mozilla.org/inspector/dom-utils;1"].getService(Ci.inIDOMUtils);
+});
--- a/browser/devtools/highlighter/test/Makefile.in
+++ b/browser/devtools/highlighter/test/Makefile.in
@@ -65,16 +65,19 @@ include $(topsrcdir)/config/rules.mk
 		browser_inspector_keybindings.js \
 		browser_inspector_breadcrumbs.html \
 		browser_inspector_breadcrumbs.js \
 		browser_inspector_bug_699308_iframe_navigation.js \
 		browser_inspector_changes.js \
 		browser_inspector_ruleviewstore.js \
 		browser_inspector_duplicate_ruleview.js \
 		browser_inspector_invalidate.js \
+		browser_inspector_sidebarstate.js \
+		browser_inspector_treePanel_menu.js \
+		browser_inspector_pseudoclass_lock.js \
 		head.js \
 		$(NULL)
 
 # Disabled due to constant failures
 # 		browser_inspector_treePanel_click.js \
 
 libs::	$(_BROWSER_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
--- a/browser/devtools/highlighter/test/browser_inspector_editor.js
+++ b/browser/devtools/highlighter/test/browser_inspector_editor.js
@@ -29,17 +29,17 @@ function setupEditorTests()
   Services.obs.addObserver(setupHTMLPanel, InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false);
   InspectorUI.toggleInspectorUI();
 }
 
 function setupHTMLPanel()
 {
   Services.obs.removeObserver(setupHTMLPanel, InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED);
   Services.obs.addObserver(runEditorTests, InspectorUI.INSPECTOR_NOTIFICATIONS.TREEPANELREADY, false);
-  InspectorUI.toolShow(InspectorUI.treePanel.registrationObject);
+  InspectorUI.toggleHTMLPanel();
 }
 
 function runEditorTests()
 {
   Services.obs.removeObserver(runEditorTests, InspectorUI.INSPECTOR_NOTIFICATIONS.TREEPANELREADY);
   InspectorUI.stopInspecting();
   InspectorUI.inspectNode(doc.body, true);
 
new file mode 100644
--- /dev/null
+++ b/browser/devtools/highlighter/test/browser_inspector_pseudoclass_lock.js
@@ -0,0 +1,154 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+let DOMUtils = Cc["@mozilla.org/inspector/dom-utils;1"].getService(Ci.inIDOMUtils);
+
+let doc;
+let div;
+
+let pseudo = ":hover";
+
+function test()
+{
+  waitForExplicitFinish();
+  ignoreAllUncaughtExceptions();
+  gBrowser.selectedTab = gBrowser.addTab();
+  gBrowser.selectedBrowser.addEventListener("load", function() {
+    gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true);
+    doc = content.document;
+    waitForFocus(createDocument, content);
+  }, true);
+
+  content.location = "data:text/html,pseudo-class lock tests";
+}
+
+function createDocument()
+{  
+  div = doc.createElement("div");
+  div.textContent = "test div";
+
+  let head = doc.getElementsByTagName('head')[0];
+  let style = doc.createElement('style');
+  let rules = doc.createTextNode('div { color: red; } div:hover { color: blue; }');
+
+  style.appendChild(rules);
+  head.appendChild(style);
+  doc.body.appendChild(div);
+  
+  setupTests();
+}
+
+function setupTests()
+{
+  Services.obs.addObserver(selectNode,
+    InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false);
+  InspectorUI.openInspectorUI();
+}
+
+function selectNode()
+{
+  Services.obs.removeObserver(selectNode,
+    InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED);
+
+  executeSoon(function() {
+    InspectorUI.highlighter.addListener("nodeselected", openRuleView);
+    InspectorUI.inspectNode(div);
+  });
+}
+
+function openRuleView()
+{
+  Services.obs.addObserver(performTests,
+    InspectorUI.INSPECTOR_NOTIFICATIONS.RULEVIEWREADY, false);
+
+  InspectorUI.showSidebar();
+  InspectorUI.openRuleView();
+}
+
+function performTests()
+{
+  Services.obs.removeObserver(performTests,
+    InspectorUI.INSPECTOR_NOTIFICATIONS.RULEVIEWREADY);
+
+  InspectorUI.highlighter.removeListener("nodeselected", performTests);
+
+  // toggle the class
+  InspectorUI.highlighter.pseudoClassLockToggled(pseudo);
+
+  testAdded();
+
+  // toggle the lock off
+  InspectorUI.highlighter.pseudoClassLockToggled(pseudo);
+
+  testRemoved();
+  testRemovedFromUI();
+
+  // toggle it back on
+  InspectorUI.highlighter.pseudoClassLockToggled(pseudo);  
+
+  // close the inspector
+  Services.obs.addObserver(testInspectorClosed,
+    InspectorUI.INSPECTOR_NOTIFICATIONS.CLOSED, false);
+  InspectorUI.closeInspectorUI();
+}
+
+function testAdded()
+{
+  // lock is applied to it and ancestors
+  let node = div;
+  do {
+    is(DOMUtils.hasPseudoClassLock(node, pseudo), true,
+       "pseudo-class lock has been applied");
+    node = node.parentNode;
+  } while (node.parentNode)
+
+  // infobar selector contains pseudo-class
+  let pseudoClassesBox = document.getElementById("highlighter-nodeinfobar-pseudo-classes");
+  is(pseudoClassesBox.textContent, pseudo, "pseudo-class in infobar selector");
+  
+  // ruleview contains pseudo-class rule
+  is(InspectorUI.ruleView.element.children.length, 3,
+     "rule view is showing 3 rules for pseudo-class locked div");
+     
+  is(InspectorUI.ruleView.element.children[1]._ruleEditor.rule.selectorText,
+     "div:hover", "rule view is showing " + pseudo + " rule");
+}
+
+function testRemoved()
+{
+  // lock removed from node and ancestors  
+  let node = div;
+  do {
+    is(DOMUtils.hasPseudoClassLock(node, pseudo), false,
+       "pseudo-class lock has been removed");
+    node = node.parentNode;
+  } while (node.parentNode)
+}
+
+function testRemovedFromUI()
+{
+  // infobar selector doesn't contain pseudo-class
+  let pseudoClassesBox = document.getElementById("highlighter-nodeinfobar-pseudo-classes");
+  is(pseudoClassesBox.textContent, "", "pseudo-class removed from infobar selector");    
+
+  // ruleview no longer contains pseudo-class rule
+  is(InspectorUI.ruleView.element.children.length, 2,
+     "rule view is showing 2 rules after removing lock");    
+}
+
+function testInspectorClosed()
+{
+  Services.obs.removeObserver(testInspectorClosed,
+    InspectorUI.INSPECTOR_NOTIFICATIONS.CLOSED);
+
+  testRemoved();
+
+  finishUp();  
+}
+
+function finishUp()
+{
+  doc = div = null;
+  gBrowser.removeCurrentTab();
+  finish();
+}
--- a/browser/devtools/highlighter/test/browser_inspector_registertools.js
+++ b/browser/devtools/highlighter/test/browser_inspector_registertools.js
@@ -143,55 +143,16 @@ function startToolTests(evt)
   InspectorUI.toolShow(tool1);
   InspectorUI.toolShow(tool3);
 
   info("Checking panel states 4");
   ok(tool1.isOpen, "Panel 1 is open");
   ok(!tool2.isOpen, "Panel 2 is closed");
   ok(tool3.isOpen, "Panel 3 is open");
 
-  gBrowser.selectedTab = gBrowser.addTab();
-  gBrowser.selectedBrowser.addEventListener("load", function() {
-    gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true);
-    waitForFocus(testSecondTab, content);
-  }, true);
-
-  content.location = "data:text/html,registertool new tab test for inspector";
-}
-
-function testSecondTab()
-{
-  info("Opened second tab");
-  info("Checking panel states 5");
-
-  let tools = InspectorUI.tools;
-  ok(!(tool1 in tools), "Panel 1 not in tools");
-  ok(!(tool2 in tools), "Panel 2 not in tools");
-  ok(!(tool3 in tools), "Panel 3 not in tools");
-
-  info("Closing current tab");
-  Services.obs.addObserver(testOriginalTab, InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false);
-  gBrowser.removeCurrentTab();
-}
-
-function testOriginalTab()
-{
-  Services.obs.removeObserver(testOriginalTab, InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED);
-  info("Checking panel states 6");
-
-  info("Tools: " + InspectorUI.tools);
-  // reacquaint ourselves with our tools
-  tool1 = InspectorUI.tools["tool_1"];
-  tool2 = InspectorUI.tools["tool_2"];
-  tool3 = InspectorUI.tools["tool_3"];
-
-  ok(tool1.isOpen, "Panel 1 is open after reactivation");
-  ok(!tool2.isOpen, "Panel 2 is closed after reactivation");
-  ok(tool3.isOpen, "Panel 3 is open after reactivation");
-
   Services.obs.addObserver(unregisterTools, InspectorUI.INSPECTOR_NOTIFICATIONS.CLOSED, false);
   InspectorUI.closeInspectorUI(true);
 }
 
 function unregisterTools()
 {
   Services.obs.removeObserver(unregisterTools, InspectorUI.INSPECTOR_NOTIFICATIONS.CLOSED);
   let tools = InspectorUI.tools;
--- a/browser/devtools/highlighter/test/browser_inspector_ruleviewstore.js
+++ b/browser/devtools/highlighter/test/browser_inspector_ruleviewstore.js
@@ -123,18 +123,18 @@ function inspectorFocusTab1()
 }
 
 function ruleViewOpened2()
 {
   let prop = InspectorUI.ruleView._elementStyle.rules[0].textProps[0];
   is(prop.name, "background-color", "First prop is the background color prop.");
   ok(!prop.enabled, "First prop should be disabled.");
 
+  InspectorUI.closeInspectorUI();
   gBrowser.removeCurrentTab();
-  InspectorUI.closeInspectorUI();
   finish();
 }
 
 function test()
 {
   waitForExplicitFinish();
 
   tab1 = gBrowser.addTab();
new file mode 100644
--- /dev/null
+++ b/browser/devtools/highlighter/test/browser_inspector_sidebarstate.js
@@ -0,0 +1,74 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+let doc;
+
+function createDocument()
+{
+  doc.body.innerHTML = '<h1>Sidebar state test</h1>';
+  doc.title = "Sidebar State Test";
+
+  // Open the sidebar and wait for the default view (the rule view) to show.
+  Services.obs.addObserver(inspectorRuleViewOpened,
+    InspectorUI.INSPECTOR_NOTIFICATIONS.RULEVIEWREADY, false);
+
+  InspectorUI.openInspectorUI();
+  InspectorUI.showSidebar();
+}
+
+function inspectorRuleViewOpened()
+{
+  Services.obs.removeObserver(inspectorRuleViewOpened,
+    InspectorUI.INSPECTOR_NOTIFICATIONS.RULEVIEWREADY);
+  is(InspectorUI.activeSidebarPanel, "ruleview", "Rule View is selected by default");
+
+  // Select the computed view and turn off the inspector.
+  InspectorUI.activateSidebarPanel("styleinspector");
+
+  Services.obs.addObserver(inspectorClosed,
+    InspectorUI.INSPECTOR_NOTIFICATIONS.CLOSED, false);
+  InspectorUI.closeInspectorUI();
+}
+
+function inspectorClosed()
+{
+  // Reopen the inspector, expect the computed view to be loaded.
+  Services.obs.removeObserver(inspectorClosed,
+    InspectorUI.INSPECTOR_NOTIFICATIONS.CLOSED);
+
+  Services.obs.addObserver(computedViewPopulated,
+    "StyleInspector-populated", false);
+
+  InspectorUI.openInspectorUI();
+}
+
+function computedViewPopulated()
+{
+  Services.obs.removeObserver(computedViewPopulated,
+    "StyleInspector-populated");
+  is(InspectorUI.activeSidebarPanel, "styleinspector", "Computed view is selected by default.");
+
+  finishTest();
+}
+
+
+function finishTest()
+{
+  InspectorUI.closeInspectorUI();
+  gBrowser.removeCurrentTab();
+  finish();
+}
+
+function test()
+{
+  waitForExplicitFinish();
+  gBrowser.selectedTab = gBrowser.addTab();
+  gBrowser.selectedBrowser.addEventListener("load", function() {
+    gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true);
+    doc = content.document;
+    waitForFocus(createDocument, content);
+  }, true);
+
+  content.location = "data:text/html,basic tests for inspector";
+}
+
--- a/browser/devtools/highlighter/test/browser_inspector_tab_switch.js
+++ b/browser/devtools/highlighter/test/browser_inspector_tab_switch.js
@@ -91,16 +91,17 @@ function inspectorTabOpen2()
   ok(!InspectorUI.treePanel, "Inspector Tree Panel is closed");
   ok(!InspectorUI.isSidebarOpen, "Inspector Sidebar is not open");
   is(InspectorUI.store.length, 1, "Inspector.store.length = 1");
 
   // Activate the inspector again.
   executeSoon(function() {
     Services.obs.addObserver(inspectorUIOpen2,
       InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false);
+    clearUserPrefs();
     InspectorUI.openInspectorUI();
   });
 }
 
 function inspectorUIOpen2()
 {
   Services.obs.removeObserver(inspectorUIOpen2,
     InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false);
@@ -131,34 +132,34 @@ function inspectorFocusTab1()
   ok(InspectorUI.inspecting, "Inspector is highlighting");
   ok(!InspectorUI.treePanel.isOpen(), "Inspector Tree Panel is not open");
   is(InspectorUI.store.length, 2, "Inspector.store.length = 2");
   is(InspectorUI.selection, div, "selection matches the div element");
 
   Services.obs.addObserver(inspectorOpenTreePanelTab1,
     InspectorUI.INSPECTOR_NOTIFICATIONS.TREEPANELREADY, false);
 
-  InspectorUI.treePanel.open();
+  InspectorUI.toggleHTMLPanel();
 }
 
 function inspectorOpenTreePanelTab1()
 {
   Services.obs.removeObserver(inspectorOpenTreePanelTab1,
     InspectorUI.INSPECTOR_NOTIFICATIONS.TREEPANELREADY);
 
   ok(InspectorUI.inspecting, "Inspector is highlighting");
   ok(InspectorUI.treePanel.isOpen(), "Inspector Tree Panel is open");
   is(InspectorUI.store.length, 2, "Inspector.store.length = 2");
   is(InspectorUI.selection, div, "selection matches the div element");
 
   Services.obs.addObserver(inspectorSidebarStyleView1, "StyleInspector-opened", false);
 
   executeSoon(function() {
     InspectorUI.showSidebar();
-    InspectorUI.toolShow(InspectorUI.stylePanel.registrationObject);
+    InspectorUI.activateSidebarPanel("styleinspector");
   });
 }
 
 function inspectorSidebarStyleView1()
 {
   Services.obs.removeObserver(inspectorSidebarStyleView1, "StyleInspector-opened");
   ok(InspectorUI.isSidebarOpen, "Inspector Sidebar is open");
   ok(InspectorUI.stylePanel, "Inspector Has a Style Panel Instance");
new file mode 100644
--- /dev/null
+++ b/browser/devtools/highlighter/test/browser_inspector_treePanel_menu.js
@@ -0,0 +1,97 @@
+/* Any copyright is dedicated to the Public Domain.
+http://creativecommons.org/publicdomain/zero/1.0/ */
+
+
+function test() {
+
+  waitForExplicitFinish();
+
+  let doc;
+  let node1;
+  let div;
+
+  function createDocument() {
+    div = doc.createElement("div");
+    let h1 = doc.createElement("h1");
+    let p1 = doc.createElement("p");
+    let p2 = doc.createElement("p");
+    doc.title = "Inspector Tree Menu Test";
+    h1.textContent = "Inspector Tree Menu Test";
+    p1.textContent = "This is some example text";
+    div.appendChild(h1);
+    div.appendChild(p1);
+    doc.body.appendChild(div);
+    node1 = p1;
+    setupTest();
+  }
+
+  gBrowser.selectedTab = gBrowser.addTab();
+  gBrowser.selectedBrowser.addEventListener("load", function onload() {
+    gBrowser.selectedBrowser.removeEventListener("load", onload, true);
+    doc = content.document;
+    waitForFocus(createDocument, content);
+  }, true);
+
+  content.location = content.location = "data:text/html,basic tests for inspector";;
+
+  function setupTest() {
+    Services.obs.addObserver(runTests, InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false);
+    InspectorUI.toggleInspectorUI();
+  }
+
+  function runTests() {
+    Services.obs.removeObserver(runTests, InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED);
+    Services.obs.addObserver(testCopyInnerMenu, InspectorUI.INSPECTOR_NOTIFICATIONS.TREEPANELREADY, false);
+    InspectorUI.stopInspecting();
+    InspectorUI.inspectNode(node1, true);
+    InspectorUI.treePanel.open();
+  }
+
+  function testCopyInnerMenu() {
+    let copyInner = document.getElementById("inspectorHTMLCopyInner");
+    ok(copyInner, "the popup menu has a copy inner html menu item");
+
+    waitForClipboard("This is some example text",
+                     function() { copyInner.doCommand(); },
+                     testCopyOuterMenu, testCopyOuterMenu);
+  }
+
+  function testCopyOuterMenu() {
+    let copyOuter = document.getElementById("inspectorHTMLCopyOuter");
+    ok(copyOuter, "the popup menu has a copy outer html menu item");
+
+    waitForClipboard("<p>This is some example text</p>",
+                     function() { copyOuter.doCommand(); },
+                     testDeleteNode, testDeleteNode);
+  }
+
+  function testDeleteNode() {
+    let deleteNode = document.getElementById("inspectorHTMLDelete");
+    ok(deleteNode, "the popup menu has a delete menu item");
+
+    InspectorUI.highlighter.addListener("nodeselected", deleteTest);
+
+    let commandEvent = document.createEvent("XULCommandEvent");
+    commandEvent.initCommandEvent("command", true, true, window, 0, false, false,
+                                  false, false, null);
+    deleteNode.dispatchEvent(commandEvent);
+  }
+
+  function deleteTest() {
+    InspectorUI.highlighter.removeListener("nodeSelected", deleteTest);
+    Services.obs.addObserver(finishUp, InspectorUI.INSPECTOR_NOTIFICATIONS.CLOSED, false);
+    is(InspectorUI.selection, div, "parent node selected");
+    let p = doc.querySelector("P");
+    is(p, null, "node deleted");
+    executeSoon(function() {
+      InspectorUI.closeInspectorUI();
+    });
+  }
+
+  function finishUp() {
+    Services.obs.removeObserver(finishUp, InspectorUI.INSPECTOR_NOTIFICATIONS.CLOSED);
+    doc = node1 = div = null;
+    gBrowser.removeCurrentTab();
+    finish();
+  }
+}
--- a/browser/devtools/highlighter/test/head.js
+++ b/browser/devtools/highlighter/test/head.js
@@ -36,16 +36,26 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 const Cu = Components.utils;
 let tempScope = {};
 Cu.import("resource:///modules/devtools/LayoutHelpers.jsm", tempScope);
 let LayoutHelpers = tempScope.LayoutHelpers;
 
+// Clear preferences that may be set during the course of tests.
+function clearUserPrefs()
+{
+  Services.prefs.clearUserPref("devtools.inspector.htmlPanelOpen");
+  Services.prefs.clearUserPref("devtools.inspector.sidebarOpen");
+  Services.prefs.clearUserPref("devtools.inspector.activeSidebar");
+}
+
+registerCleanupFunction(clearUserPrefs);
+
 function isHighlighting()
 {
   let veil = InspectorUI.highlighter.veilTransparentBox;
   return !(veil.style.visibility == "hidden");
 }
 
 function getHighlitNode()
 {
@@ -73,8 +83,9 @@ function getHighlitNode()
 
 function midPoint(aPointA, aPointB)
 {
   let pointC = { };
   pointC.x = (aPointB.x - aPointA.x) / 2 + aPointA.x;
   pointC.y = (aPointB.y - aPointA.y) / 2 + aPointA.y;
   return pointC;
 }
+
--- a/browser/devtools/scratchpad/scratchpad.js
+++ b/browser/devtools/scratchpad/scratchpad.js
@@ -436,28 +436,29 @@ var Scratchpad = {
     if (!error) {
       this.writeAsComment(result);
     } else {
       this.writeAsErrorComment(error);
     }
   },
 
   /**
-   * Write out a value at the current insertion point as a block comment
+   * Write out a value at the next line from the current insertion point.
+   * The comment block will always be preceded by a newline character.
    * @param object aValue
    *        The Object to write out as a string
    */
   writeAsComment: function SP_writeAsComment(aValue)
   {
     let selection = this.getSelectionRange();
     let insertionPoint = selection.start != selection.end ?
                          selection.end : // after selected text
                          this.editor.getCharCount(); // after text end
                          
-    let newComment = "/*\n" + aValue + "\n*/";
+    let newComment = "\n/*\n" + aValue + "\n*/";
     
     this.setText(newComment, insertionPoint, insertionPoint);
 
     // Select the new comment.
     this.selectRange(insertionPoint, insertionPoint + newComment.length);
   },
 
   /**
--- a/browser/devtools/scratchpad/test/browser_scratchpad_bug690552_display_outputs_errors.js
+++ b/browser/devtools/scratchpad/test/browser_scratchpad_bug690552_display_outputs_errors.js
@@ -16,17 +16,17 @@ function test()
       "comments for 'display' and not sent to the console in Scratchpad";
 }
 
 function runTests()
 {
   var scratchpad = gScratchpadWindow.Scratchpad;
 
   var message = "\"Hello World!\""
-  var openComment = "/*\n";
+  var openComment = "\n/*\n";
   var closeComment = "\n*/";
   var error = "throw new Error(\"Ouch!\")";
   let messageArray = {};
   let count = {};
 
   scratchpad.setText(message);
   scratchpad.display();
   is(scratchpad.getText(),
--- a/browser/devtools/scratchpad/test/browser_scratchpad_bug_679467_falsy.js
+++ b/browser/devtools/scratchpad/test/browser_scratchpad_bug_679467_falsy.js
@@ -25,30 +25,30 @@ function testFalsy()
 
   finish();
 }
 
 function verifyFalsies(sp)
 {
   sp.setText("undefined");
   sp.display();
-  is(sp.selectedText, "/*\nundefined\n*/", "'undefined' is displayed");
+  is(sp.selectedText, "\n/*\nundefined\n*/", "'undefined' is displayed");
 
   sp.setText("false");
   sp.display();
-  is(sp.selectedText, "/*\nfalse\n*/", "'false' is displayed");
+  is(sp.selectedText, "\n/*\nfalse\n*/", "'false' is displayed");
 
   sp.setText("0");
   sp.display();
-  is(sp.selectedText, "/*\n0\n*/", "'0' is displayed");
+  is(sp.selectedText, "\n/*\n0\n*/", "'0' is displayed");
 
   sp.setText("null");
   sp.display();
-  is(sp.selectedText, "/*\nnull\n*/", "'null' is displayed");
+  is(sp.selectedText, "\n/*\nnull\n*/", "'null' is displayed");
 
   sp.setText("NaN");
   sp.display();
-  is(sp.selectedText, "/*\nNaN\n*/", "'NaN' is displayed");
+  is(sp.selectedText, "\n/*\nNaN\n*/", "'NaN' is displayed");
 
   sp.setText("''");
   sp.display();
-  is(sp.selectedText, "/*\n\n*/", "empty string is displayed");
+  is(sp.selectedText, "\n/*\n\n*/", "empty string is displayed");
 }
--- a/browser/devtools/scratchpad/test/browser_scratchpad_execute_print.js
+++ b/browser/devtools/scratchpad/test/browser_scratchpad_execute_print.js
@@ -35,23 +35,23 @@ function runTests()
   is(content.wrappedJSObject.foobarBug636725, 2,
      "run() updated window.foobarBug636725");
 
   sp.display();
 
   is(content.wrappedJSObject.foobarBug636725, 3,
      "display() updated window.foobarBug636725");
 
-  is(sp.getText(), "++window.foobarBug636725/*\n3\n*/",
+  is(sp.getText(), "++window.foobarBug636725\n/*\n3\n*/",
      "display() shows evaluation result in the textbox");
 
-  is(sp.selectedText, "/*\n3\n*/", "selectedText is correct");
+  is(sp.selectedText, "\n/*\n3\n*/", "selectedText is correct");
   let selection = sp.getSelectionRange();
   is(selection.start, 24, "selection.start is correct");
-  is(selection.end, 31, "selection.end is correct");
+  is(selection.end, 32, "selection.end is correct");
 
   // Test selection run() and display().
 
   sp.setText("window.foobarBug636725 = 'a';\n" +
              "window.foobarBug636725 = 'b';");
 
   sp.selectRange(1, 2);
 
@@ -89,26 +89,26 @@ function runTests()
   sp.selectRange(0, 22);
 
   sp.display();
 
   is(content.wrappedJSObject.foobarBug636725, "a",
      "display() worked for the selected range");
 
   is(sp.getText(), "window.foobarBug636725" +
-                   "/*\na\n*/" +
+                   "\n/*\na\n*/" +
                    " = 'c';\n" +
                    "window.foobarBug636725 = 'b';",
      "display() shows evaluation result in the textbox");
 
-  is(sp.selectedText, "/*\na\n*/", "selectedText is correct");
+  is(sp.selectedText, "\n/*\na\n*/", "selectedText is correct");
 
   selection = sp.getSelectionRange();
   is(selection.start, 22, "selection.start is correct");
-  is(selection.end, 29, "selection.end is correct");
+  is(selection.end, 30, "selection.end is correct");
 
   sp.deselect();
 
   ok(!sp.selectedText, "selectedText is empty");
 
   selection = sp.getSelectionRange();
   is(selection.start, selection.end, "deselect() works");
 
--- a/browser/devtools/styleinspector/CssRuleView.jsm
+++ b/browser/devtools/styleinspector/CssRuleView.jsm
@@ -111,17 +111,17 @@ function ElementStyle(aElement, aStore)
 
   let doc = aElement.ownerDocument;
 
   // To figure out how shorthand properties are interpreted by the
   // engine, we will set properties on a dummy element and observe
   // how their .style attribute reflects them as computed values.
   this.dummyElement = doc.createElementNS(this.element.namespaceURI,
                                           this.element.tagName);
-  this._populate();
+  this.populate();
 }
 // We're exporting _ElementStyle for unit tests.
 var _ElementStyle = ElementStyle;
 
 ElementStyle.prototype = {
 
   // The element we're looking at.
   element: null,
@@ -142,17 +142,17 @@ ElementStyle.prototype = {
       this.onChanged();
     }
   },
 
   /**
    * Refresh the list of rules to be displayed for the active element.
    * Upon completion, this.rules[] will hold a list of Rule objects.
    */
-  _populate: function ElementStyle_populate()
+  populate: function ElementStyle_populate()
   {
     this.rules = [];
 
     let element = this.element;
     do {
       this._addElementRules(element);
     } while ((element = element.parentNode) &&
              element.nodeType === Ci.nsIDOMNode.ELEMENT_NODE);
@@ -708,25 +708,43 @@ CssRuleView.prototype = {
 
     this._elementStyle = new ElementStyle(aElement, this.store);
     this._elementStyle.onChanged = function() {
       this._changed();
     }.bind(this);
 
     this._createEditors();
   },
+  
+  /**
+   * Update the rules for the currently highlighted element.
+   */
+  nodeChanged: function CssRuleView_nodeChanged()
+  {
+    this._clearRules();
+    this._elementStyle.populate();
+    this._createEditors();
+  },  
+
+  /**
+   * Clear the rules.
+   */
+  _clearRules: function CssRuleView_clearRules()
+  {
+    while (this.element.hasChildNodes()) {
+      this.element.removeChild(this.element.lastChild);
+    }
+  },
 
   /**
    * Clear the rule view.
    */
   clear: function CssRuleView_clear()
   {
-    while (this.element.hasChildNodes()) {
-      this.element.removeChild(this.element.lastChild);
-    }
+    this._clearRules();
     this._viewedElement = null;
     this._elementStyle = null;
   },
 
   /**
    * Called when the user has made changes to the ElementStyle.
    * Emits an event that clients can listen to.
    */
--- a/browser/devtools/styleinspector/StyleInspector.jsm
+++ b/browser/devtools/styleinspector/StyleInspector.jsm
@@ -115,16 +115,21 @@ StyleInspector.prototype = {
           this.iframe.getAttribute("src") ==
           "chrome://browser/content/devtools/csshtmltree.xul") {
         let selectedNode = this.selectedNode || null;
         this.cssHtmlTree = new CssHtmlTree(this);
         this.cssLogic.highlight(selectedNode);
         this.cssHtmlTree.highlight(selectedNode);
         this.iframe.removeEventListener("load", boundIframeOnLoad, true);
         this.iframeReady = true;
+
+        // Now that we've loaded, select any node we were previously asked
+        // to show.
+        this.selectNode(this.selectedNode);
+
         Services.obs.notifyObservers(null, "StyleInspector-opened", null);
       }
     }.bind(this);
 
     this.iframe = this.IUI.getToolIframe(this.registrationObject);
 
     this.iframe.addEventListener("load", boundIframeOnLoad, true);
   },
@@ -210,21 +215,26 @@ StyleInspector.prototype = {
   },
 
   /**
    * Check if the style inspector is open.
    * @returns boolean
    */
   isOpen: function SI_isOpen()
   {
-    return this.openDocked ? this.iframeReady && this.IUI.isSidebarOpen &&
+    return this.openDocked ? this.IUI.isSidebarOpen &&
             (this.IUI.sidebarDeck.selectedPanel == this.iframe) :
            this.panel && this.panel.state && this.panel.state == "open";
   },
 
+  isLoaded: function SI_isLoaded()
+  {
+    return this.openDocked ? this.iframeReady : this.iframeReady && this.panelReady;
+  },
+
   /**
    * Select from Path (via CssHtmlTree_pathClick)
    * @param aNode The node to inspect.
    */
   selectFromPath: function SI_selectFromPath(aNode)
   {
     if (this.IUI && this.IUI.selection) {
       if (aNode != this.IUI.selection) {
@@ -237,28 +247,28 @@ StyleInspector.prototype = {
 
   /**
    * Select a node to inspect in the Style Inspector panel
    * @param aNode The node to inspect.
    */
   selectNode: function SI_selectNode(aNode)
   {
     this.selectedNode = aNode;
-    if (this.isOpen() && !this.dimmed) {
+    if (this.isLoaded() && !this.dimmed) {
       this.cssLogic.highlight(aNode);
       this.cssHtmlTree.highlight(aNode);
     }
   },
 
   /**
    * Update the display for the currently-selected node.
    */
   updateNode: function SI_updateNode()
   {
-    if (this.isOpen() && !this.dimmed) {
+    if (this.isLoaded() && !this.dimmed) {
       this.cssLogic.highlight(this.selectedNode);
       this.cssHtmlTree.refreshPanel();
     }
   },
 
   /**
    * Dim or undim a panel by setting or removing a dimmed attribute.
    * @param aState
--- a/browser/devtools/tilt/Tilt.jsm
+++ b/browser/devtools/tilt/Tilt.jsm
@@ -122,76 +122,81 @@ Tilt.prototype = {
       return;
     }
 
     // create a visualizer instance for the current tab
     this.visualizers[id] = new TiltVisualizer({
       chromeWindow: this.chromeWindow,
       contentWindow: this.chromeWindow.gBrowser.selectedBrowser.contentWindow,
       parentNode: this.chromeWindow.gBrowser.selectedBrowser.parentNode,
-      requestAnimationFrame: this.chromeWindow.mozRequestAnimationFrame,
       notifications: this.NOTIFICATIONS
     });
 
     // make sure the visualizer object was initialized properly
     if (!this.visualizers[id].isInitialized()) {
       this.destroy(id);
       return;
     }
 
     Services.obs.notifyObservers(null, TILT_NOTIFICATIONS.INITIALIZING, null);
   },
 
   /**
-   * Destroys a specific instance of the visualizer.
+   * Starts destroying a specific instance of the visualizer.
    *
    * @param {String} aId
    *                 the identifier of the instance in the visualizers array
    * @param {Boolean} aAnimateFlag
    *                  optional, set to true to display a destruction transition
    */
   destroy: function T_destroy(aId, aAnimateFlag)
   {
-    // if the visualizer is already destroyed, don't do anything
-    if (!this.visualizers[aId]) {
+    // if the visualizer is destroyed or destroying, don't do anything
+    if (!this.visualizers[aId] || this._isDestroying) {
+      return;
+    }
+    this._isDestroying = true;
+
+    let controller = this.visualizers[aId].controller;
+    let presenter = this.visualizers[aId].presenter;
+
+    let content = presenter.contentWindow;
+    let pageXOffset = content.pageXOffset * presenter.transforms.zoom;
+    let pageYOffset = content.pageYOffset * presenter.transforms.zoom;
+    TiltUtils.setDocumentZoom(this.chromeWindow, presenter.transforms.zoom);
+
+    // if we're not doing any outro animation, just finish destruction directly
+    if (!aAnimateFlag) {
+      this._finish(aId);
       return;
     }
 
-    if (!this.isDestroying) {
-      this.isDestroying = true;
+    // otherwise, trigger the outro animation and notify necessary observers
+    Services.obs.notifyObservers(null, TILT_NOTIFICATIONS.DESTROYING, null);
 
-      let finalize = function T_finalize(aId) {
-        this.visualizers[aId].removeOverlay();
-        this.visualizers[aId].cleanup();
-        this.visualizers[aId] = null;
-
-        this.isDestroying = false;
-        this.chromeWindow.gBrowser.selectedBrowser.focus();
-        Services.obs.notifyObservers(null, TILT_NOTIFICATIONS.DESTROYED, null);
-      };
+    controller.removeEventListeners();
+    controller.arcball.reset([-pageXOffset, -pageYOffset]);
+    presenter.executeDestruction(this._finish.bind(this, aId));
+  },
 
-      if (!aAnimateFlag) {
-        finalize.call(this, aId);
-        return;
-      }
-
-      let controller = this.visualizers[aId].controller;
-      let presenter = this.visualizers[aId].presenter;
+  /**
+   * Finishes detroying a specific instance of the visualizer.
+   *
+   * @param {String} aId
+   *                 the identifier of the instance in the visualizers array
+   */
+  _finish: function T__finish(aId)
+  {
+    this.visualizers[aId].removeOverlay();
+    this.visualizers[aId].cleanup();
+    this.visualizers[aId] = null;
 
-      let content = presenter.contentWindow;
-      let pageXOffset = content.pageXOffset * presenter.transforms.zoom;
-      let pageYOffset = content.pageYOffset * presenter.transforms.zoom;
-
-      Services.obs.notifyObservers(null, TILT_NOTIFICATIONS.DESTROYING, null);
-      TiltUtils.setDocumentZoom(this.chromeWindow, presenter.transforms.zoom);
-
-      controller.removeEventListeners();
-      controller.arcball.reset([-pageXOffset, -pageYOffset]);
-      presenter.executeDestruction(finalize.bind(this, aId));
-    }
+    this._isDestroying = false;
+    this.chromeWindow.gBrowser.selectedBrowser.focus();
+    Services.obs.notifyObservers(null, TILT_NOTIFICATIONS.DESTROYED, null);
   },
 
   /**
    * Handles any supplementary post-initialization work, done immediately
    * after a TILT_NOTIFICATIONS.INITIALIZING notification.
    */
   _whenInitializing: function T__whenInitializing()
   {
@@ -281,26 +286,28 @@ Tilt.prototype = {
       this.chromeWindow.InspectorUI.INSPECTOR_NOTIFICATIONS.DESTROYED, false);
 
     this.chromeWindow.gBrowser.tabContainer.addEventListener("TabSelect",
       this._onTabSelect.bind(this), false);
 
 
     // FIXME: this shouldn't be done here, see bug #705131
     let onOpened = function() {
-      if (this.currentInstance) {
-        this.chromeWindow.InspectorUI.stopInspecting();
-        this.inspectButton.disabled = true;
-        this.highlighterContainer.style.display = "none";
+      if (this.inspector && this.highlighter && this.currentInstance) {
+        this.inspector.stopInspecting();
+        this.inspector.inspectToolbutton.disabled = true;
+        this.highlighter.hide();
       }
     }.bind(this);
 
     let onClosed = function() {
-      this.inspectButton.disabled = false;
-      this.highlighterContainer.style.display = "";
+      if (this.inspector && this.highlighter) {
+        this.inspector.inspectToolbutton.disabled = false;
+        this.highlighter.show();
+      }
     }.bind(this);
 
     Services.obs.addObserver(onOpened,
       this.chromeWindow.InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false);
     Services.obs.addObserver(onClosed,
       this.chromeWindow.InspectorUI.INSPECTOR_NOTIFICATIONS.CLOSED, false);
     Services.obs.addObserver(onOpened,
       TILT_NOTIFICATIONS.INITIALIZING, false);
@@ -333,36 +340,31 @@ Tilt.prototype = {
    * Gets the visualizer instance for the current tab.
    */
   get currentInstance()
   {
     return this.visualizers[this.currentWindowId];
   },
 
   /**
+   * Gets the current InspectorUI instance.
+   */
+  get inspector()
+  {
+    return this.chromeWindow.InspectorUI;
+  },
+
+  /**
+   * Gets the current Highlighter instance from the InspectorUI.
+   */
+  get highlighter()
+  {
+    return this.inspector.highlighter;
+  },
+
+  /**
    * Gets the Tilt button in the Inspector toolbar.
    */
   get tiltButton()
   {
-    return this.chromeWindow.document.getElementById(
-      "inspector-3D-button");
-  },
-
-  /**
-   * Gets the Inspect button in the Inspector toolbar.
-   * FIXME: this shouldn't be needed here, remove after bug #705131
-   */
-  get inspectButton()
-  {
-    return this.chromeWindow.document.getElementById(
-      "inspector-inspect-toolbutton");
-  },
-
-  /**
-   * Gets the Highlighter contaniner stack.
-   * FIXME: this shouldn't be needed here, remove after bug #705131
-   */
-  get highlighterContainer()
-  {
-    return this.chromeWindow.document.getElementById(
-      "highlighter-container");
+    return this.chromeWindow.document.getElementById("inspector-3D-button");
   }
 };
--- a/browser/devtools/tilt/TiltGL.jsm
+++ b/browser/devtools/tilt/TiltGL.jsm
@@ -87,16 +87,18 @@ TiltGL.Renderer = function TGL_Renderer(
   this.context.clearColor(0, 0, 0, 0);
   this.context.clearDepth(1);
 
   /**
    * Variables representing the current framebuffer width and height.
    */
   this.width = aCanvas.width;
   this.height = aCanvas.height;
+  this.initialWidth = this.width;
+  this.initialHeight = this.height;
 
   /**
    * The current model view matrix.
    */
   this.mvMatrix = mat4.identity(mat4.create());
 
   /**
    * The current projection matrix.
@@ -859,32 +861,40 @@ TiltGL.Program.prototype = {
     let utils = TiltGL.ProgramUtils;
 
     // check if the program wasn't already active
     if (utils._activeProgram !== id) {
       utils._activeProgram = id;
 
       // use the the program if it wasn't already set
       this._context.useProgram(this._ref);
-
-      // check if the required vertex attributes aren't already set
-      if (utils._enabledAttributes < this._attributes.length) {
-        utils._enabledAttributes = this._attributes.length;
+      this.cleanupVertexAttrib();
 
-        // enable any necessary vertex attributes using the cache
-        for (let i in this._attributes) {
-          if (this._attributes.hasOwnProperty(i)) {
-            this._context.enableVertexAttribArray(this._attributes[i]);
-          }
-        }
+      // enable any necessary vertex attributes using the cache
+      for each (let attribute in this._attributes) {
+        this._context.enableVertexAttribArray(attribute);
+        utils._enabledAttributes.push(attribute);
       }
     }
   },
 
   /**
+   * Disables all currently enabled vertex attribute arrays.
+   */
+  cleanupVertexAttrib: function TGLP_cleanupVertexAttrib()
+  {
+    let utils = TiltGL.ProgramUtils;
+
+    for each (let attribute in utils._enabledAttributes) {
+      this._context.disableVertexAttribArray(attribute);
+    }
+    utils._enabledAttributes = [];
+  },
+
+  /**
    * Binds a vertex buffer as an array buffer for a specific shader attribute.
    *
    * @param {String} aAtribute
    *                 the attribute name obtained from the shader
    * @param {Float32Array} aBuffer
    *                       the buffer to be bound
    */
   bindVertexBuffer: function TGLP_bindVertexBuffer(aAtribute, aBuffer)
@@ -944,19 +954,19 @@ TiltGL.Program.prototype = {
    *                 the sampler name to bind the texture to
    * @param {TiltGL.Texture} aTexture
    *                       the texture to be bound
    */
   bindTexture: function TGLP_bindTexture(aSampler, aTexture)
   {
     let gl = this._context;
 
-    gl.uniform1i(this._uniforms[aSampler], 0);
     gl.activeTexture(gl.TEXTURE0);
     gl.bindTexture(gl.TEXTURE_2D, aTexture._ref);
+    gl.uniform1i(this._uniforms[aSampler], 0);
   },
 
   /**
    * Function called when this object is destroyed.
    */
   finalize: function TGLP_finalize()
   {
     if (this._context) {
@@ -1172,17 +1182,17 @@ TiltGL.ProgramUtils = {
   /**
    * Represents the current active shader, identified by an id.
    */
   _activeProgram: -1,
 
   /**
    * Represents the current enabled attributes.
    */
-  _enabledAttributes: -1
+  _enabledAttributes: []
 };
 
 /**
  * This constructor creates a texture from an Image.
  *
  * @param {Object} aContext
  *                 a WebGL context
  * @param {Object} aProperties
@@ -1410,17 +1420,17 @@ TiltGL.TextureUtils = {
     // generate mipmap if necessary
     if (aProperties.mipmap) {
       gl.generateMipmap(gl.TEXTURE_2D);
     }
   },
 
   /**
    * This shim renders a content window to a canvas element, but clamps the
-   * maximum width and height of the canvas to half the WebGL MAX_TEXTURE_SIZE.
+   * maximum width and height of the canvas to the WebGL MAX_TEXTURE_SIZE.
    *
    * @param {Window} aContentWindow
    *                 the content window to get a texture from
    * @param {Number} aMaxImageSize
    *                 the maximum image size to be used
    *
    * @return {Image} the new content window image
    */
@@ -1610,10 +1620,10 @@ TiltGL.create3DContext = function TGL_cr
 };
 
 /**
  * Clears the cache and sets all the variables to default.
  */
 TiltGL.clearCache = function TGL_clearCache()
 {
   TiltGL.ProgramUtils._activeProgram = -1;
-  TiltGL.ProgramUtils._enabledAttributes = -1;
+  TiltGL.ProgramUtils._enabledAttributes = [];
 };
--- a/browser/devtools/tilt/TiltUtils.jsm
+++ b/browser/devtools/tilt/TiltUtils.jsm
@@ -513,18 +513,18 @@ TiltUtils.bindObjectFunc = function TU_b
  */
 TiltUtils.destroyObject = function TU_destroyObject(aScope)
 {
   if (!aScope) {
     return;
   }
 
   // objects in Tilt usually use a function to handle internal destruction
-  if ("function" === typeof aScope.finalize) {
-    aScope.finalize();
+  if ("function" === typeof aScope._finalize) {
+    aScope._finalize();
   }
   for (let i in aScope) {
     if (aScope.hasOwnProperty(i)) {
       delete aScope[i];
     }
   }
 };
 
--- a/browser/devtools/tilt/TiltVisualizer.jsm
+++ b/browser/devtools/tilt/TiltVisualizer.jsm
@@ -50,33 +50,40 @@ const INVISIBLE_ELEMENTS = {
   "link": true,
   "meta": true,
   "option": true,
   "script": true,
   "style": true,
   "title": true
 };
 
+// a node is represented in the visualization mesh as a rectangular stack
+// of 5 quads composed of 12 vertices; we draw these as triangles using an
+// index buffer of 12 unsigned int elements, obviously one for each vertex;
+// if a webpage has enough nodes to overflow the index buffer elements size,
+// weird things may happen; thus, when necessary, we'll split into groups
+const MAX_GROUP_NODES = Math.pow(2, Uint16Array.BYTES_PER_ELEMENT * 8) / 12 - 1;
+
 const STACK_THICKNESS = 15;
 const WIREFRAME_COLOR = [0, 0, 0, 0.25];
-const INTRO_TRANSITION_DURATION = 50;
-const OUTRO_TRANSITION_DURATION = 40;
+const INTRO_TRANSITION_DURATION = 1000;
+const OUTRO_TRANSITION_DURATION = 800;
 const INITIAL_Z_TRANSLATION = 400;
 const MOVE_INTO_VIEW_ACCURACY = 50;
 
 const MOUSE_CLICK_THRESHOLD = 10;
-const MOUSE_INTRO_DELAY = 10;
+const MOUSE_INTRO_DELAY = 200;
 const ARCBALL_SENSITIVITY = 0.5;
 const ARCBALL_ROTATION_STEP = 0.15;
 const ARCBALL_TRANSLATION_STEP = 35;
 const ARCBALL_ZOOM_STEP = 0.1;
 const ARCBALL_ZOOM_MIN = -3000;
 const ARCBALL_ZOOM_MAX = 500;
-const ARCBALL_RESET_FACTOR = 0.9;
-const ARCBALL_RESET_INTERVAL = 1000 / 60;
+const ARCBALL_RESET_SPHERICAL_FACTOR = 0.1;
+const ARCBALL_RESET_LINEAR_FACTOR = 0.01;
 
 const TILT_CRAFTER = "resource:///modules/devtools/TiltWorkerCrafter.js";
 const TILT_PICKER = "resource:///modules/devtools/TiltWorkerPicker.js";
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource:///modules/devtools/TiltGL.jsm");
 Cu.import("resource:///modules/devtools/TiltMath.jsm");
 Cu.import("resource:///modules/devtools/TiltUtils.jsm");
@@ -87,17 +94,16 @@ let EXPORTED_SYMBOLS = ["TiltVisualizer"
 /**
  * Initializes the visualization presenter and controller.
  *
  * @param {Object} aProperties
  *                 an object containing the following properties:
  *        {Window} chromeWindow: a reference to the top level window
  *        {Window} contentWindow: the content window holding the visualized doc
  *       {Element} parentNode: the parent node to hold the visualization
- *      {Function} requestAnimationFrame: responsible with scheduling loops
  *        {Object} notifications: necessary notifications for Tilt
  *      {Function} onError: optional, function called if initialization failed
  *      {Function} onLoad: optional, function called if initialization worked
  */
 function TiltVisualizer(aProperties)
 {
   // make sure the properties parameter is a valid object
   aProperties = aProperties || {};
@@ -116,17 +122,16 @@ function TiltVisualizer(aProperties)
   });
 
   /**
    * Visualization logic and drawing loop.
    */
   this.presenter = new TiltVisualizer.Presenter(this.canvas,
     aProperties.chromeWindow,
     aProperties.contentWindow,
-    aProperties.requestAnimationFrame,
     aProperties.notifications,
     aProperties.onError || null,
     aProperties.onLoad || null);
 
   /**
    * Visualization mouse and keyboard controller.
    */
   this.controller = new TiltVisualizer.Controller(this.canvas, this.presenter);
@@ -179,28 +184,25 @@ TiltVisualizer.prototype = {
  * This object manages the visualization logic and drawing loop.
  *
  * @param {HTMLCanvasElement} aCanvas
  *                            the canvas element used for rendering
  * @param {Window} aChromeWindow
  *                 a reference to the top-level window
  * @param {Window} aContentWindow
  *                 the content window holding the document to be visualized
- * @param {Function} aRequestAnimationFrame
- *                   function responsible with scheduling loop frames
  * @param {Object} aNotifications
  *                 necessary notifications for Tilt
  * @param {Function} onError
  *                   function called if initialization failed
  * @param {Function} onLoad
  *                   function called if initialization worked
  */
 TiltVisualizer.Presenter = function TV_Presenter(
-  aCanvas, aChromeWindow, aContentWindow, aRequestAnimationFrame, aNotifications,
-  onError, onLoad)
+  aCanvas, aChromeWindow, aContentWindow, aNotifications, onError, onLoad)
 {
   /**
    * A canvas overlay used for drawing the visualization.
    */
   this.canvas = aCanvas;
 
   /**
    * Save a reference to the top-level window, to access InspectorUI or Tilt.
@@ -215,35 +217,36 @@ TiltVisualizer.Presenter = function TV_P
   /**
    * Shortcut for accessing notifications strings.
    */
   this.NOTIFICATIONS = aNotifications;
 
   /**
    * Create the renderer, containing useful functions for easy drawing.
    */
-  this.renderer = new TiltGL.Renderer(aCanvas, onError, onLoad);
+  this._renderer = new TiltGL.Renderer(aCanvas, onError, onLoad);
 
   /**
    * A custom shader used for drawing the visualization mesh.
    */
-  this.visualizationProgram = null;
+  this._visualizationProgram = null;
 
   /**
    * The combined mesh representing the document visualization.
    */
-  this.texture = null;
-  this.meshStacks = null;
-  this.meshWireframe = null;
-  this.traverseData = null;
+  this._texture = null;
+  this._meshData = null;
+  this._meshStacks = null;
+  this._meshWireframe = null;
+  this._traverseData = null;
 
   /**
    * A highlight quad drawn over a stacked dom node.
    */
-  this.highlight = {
+  this._highlight = {
     disabled: true,
     v0: vec3.create(),
     v1: vec3.create(),
     v2: vec3.create(),
     v3: vec3.create()
   };
 
   /**
@@ -263,396 +266,435 @@ TiltVisualizer.Presenter = function TV_P
   this._currentSelection = -1; // the selected node index
   this._initialSelection = false; // true if an initial selection was made
   this._initialMeshConfiguration = false; // true if the 3D mesh was configured
 
   /**
    * Variable specifying if the scene should be redrawn.
    * This should happen usually when the visualization is translated/rotated.
    */
-  this.redraw = true;
+  this._redraw = true;
+
+  /**
+   * Total time passed since the rendering started.
+   * If the rendering is paused, this property won't get updated.
+   */
+  this._time = 0;
 
   /**
-   * A frame counter, incremented each time the scene is redrawn.
+   * Frame delta time (the ammount of time passed for each frame).
+   * This is used to smoothly interpolate animation transfroms.
    */
-  this.frames = 0;
+  this._delta = 0;
+  this._prevFrameTime = 0;
+  this._currFrameTime = 0;
+
+
+  this._setup();
+  this._loop();
+};
+
+TiltVisualizer.Presenter.prototype = {
 
   /**
    * The initialization logic.
    */
-  let setup = function TVP_setup()
+  _setup: function TVP__setup()
   {
-    let renderer = this.renderer;
+    let renderer = this._renderer;
     let inspector = this.chromeWindow.InspectorUI;
 
     // if the renderer was destroyed, don't continue setup
     if (!renderer || !renderer.context) {
       return;
     }
 
     // create the visualization shaders and program to draw the stacks mesh
-    this.visualizationProgram = new renderer.Program({
+    this._visualizationProgram = new renderer.Program({
       vs: TiltVisualizer.MeshShader.vs,
       fs: TiltVisualizer.MeshShader.fs,
       attributes: ["vertexPosition", "vertexTexCoord", "vertexColor"],
       uniforms: ["mvMatrix", "projMatrix", "sampler"]
     });
 
     // get the document zoom to properly scale the visualization
     if (inspector.highlighter) {
       this.transforms.zoom = inspector.highlighter.zoom;
     }
 
-    this.setupTexture();
-    this.setupMeshData();
-    this.setupEventListeners();
+    // bind the owner object to the necessary functions
+    TiltUtils.bindObjectFunc(this, "^_on");
+    TiltUtils.bindObjectFunc(this, "_loop");
+
+    this._setupTexture();
+    this._setupMeshData();
+    this._setupEventListeners();
     this.canvas.focus();
-  }.bind(this);
+  },
 
   /**
    * The animation logic.
    */
-  let loop = function TVP_loop()
+  _loop: function TVP__loop()
   {
-    let renderer = this.renderer;
+    let renderer = this._renderer;
 
     // if the renderer was destroyed, don't continue rendering
     if (!renderer || !renderer.context) {
       return;
     }
 
     // prepare for the next frame of the animation loop
-    aRequestAnimationFrame(loop);
+    this.chromeWindow.mozRequestAnimationFrame(this._loop);
 
     // only redraw if we really have to
-    if (this.redraw) {
-      this.redraw = false;
-      this.drawVisualization();
+    if (this._redraw) {
+      this._redraw = false;
+      this._drawVisualization();
+    }
+
+    // update the current presenter transfroms from the controller
+    if ("function" === typeof this._controllerUpdate) {
+      this._controllerUpdate(this._time, this._delta);
     }
 
-    // call the attached ondraw function and handle all keyframe notifications
-    if ("function" === typeof this.ondraw) {
-      this.ondraw(this.frames);
-    }
+    this._handleFrameDelta();
+    this._handleKeyframeNotifications();
+  },
 
-    this.handleKeyframeNotifications();
-  }.bind(this);
-
-  setup();
-  loop();
-};
-
-TiltVisualizer.Presenter.prototype = {
+  /**
+   * Calculates the current frame delta time.
+   */
+  _handleFrameDelta: function TVP__handleFrameDelta()
+  {
+    this._prevFrameTime = this._currFrameTime;
+    this._currFrameTime = this.chromeWindow.mozAnimationStartTime;
+    this._delta = this._currFrameTime - this._prevFrameTime;
+  },
 
   /**
    * Draws the visualization mesh and highlight quad.
    */
-  drawVisualization: function TVP_drawVisualization()
+  _drawVisualization: function TVP__drawVisualization()
   {
-    let renderer = this.renderer;
+    let renderer = this._renderer;
     let transforms = this.transforms;
     let w = renderer.width;
     let h = renderer.height;
+    let ih = renderer.initialHeight;
 
     // if the mesh wasn't created yet, don't continue rendering
-    if (!this.meshStacks || !this.meshWireframe) {
+    if (!this._meshStacks || !this._meshWireframe) {
       return;
     }
 
     // clear the context to an opaque black background
     renderer.clear();
     renderer.perspective();
 
     // apply a transition transformation using an ortho and perspective matrix
     let ortho = mat4.ortho(0, w, h, 0, -1000, 1000);
 
-    if (!this.isExecutingDestruction) {
-      let f = this.frames / INTRO_TRANSITION_DURATION;
+    if (!this._isExecutingDestruction) {
+      let f = this._time / INTRO_TRANSITION_DURATION;
       renderer.lerp(renderer.projMatrix, ortho, f, 8);
     } else {
-      let f = this.frames / OUTRO_TRANSITION_DURATION;
+      let f = this._time / OUTRO_TRANSITION_DURATION;
       renderer.lerp(renderer.projMatrix, ortho, 1 - f, 8);
     }
 
     // apply the preliminary transformations to the model view
-    renderer.translate(w * 0.5, h * 0.5, -INITIAL_Z_TRANSLATION);
+    renderer.translate(w * 0.5, ih * 0.5, -INITIAL_Z_TRANSLATION);
 
     // calculate the camera matrix using the rotation and translation
     renderer.translate(transforms.translation[0], 0,
                        transforms.translation[2]);
 
     renderer.transform(quat4.toMat4(transforms.rotation));
 
     // offset the visualization mesh to center
     renderer.translate(transforms.offset[0],
                        transforms.offset[1] + transforms.translation[1], 0);
 
     renderer.scale(transforms.zoom, transforms.zoom);
 
     // draw the visualization mesh
     renderer.strokeWeight(2);
     renderer.depthTest(true);
-    this.drawMeshStacks();
-    this.drawMeshWireframe();
-    this.drawHighlight();
+    this._drawMeshStacks();
+    this._drawMeshWireframe();
+    this._drawHighlight();
 
     // make sure the initial transition is drawn until finished
-    if (this.frames < INTRO_TRANSITION_DURATION ||
-        this.frames < OUTRO_TRANSITION_DURATION) {
-      this.redraw = true;
+    if (this._time < INTRO_TRANSITION_DURATION ||
+        this._time < OUTRO_TRANSITION_DURATION) {
+      this._redraw = true;
     }
-    this.frames++;
+    this._time += this._delta;
   },
 
   /**
    * Draws the meshStacks object.
    */
-  drawMeshStacks: function TVP_drawMeshStacks()
+  _drawMeshStacks: function TVP__drawMeshStacks()
   {
-    let renderer = this.renderer;
-    let mesh = this.meshStacks;
+    let renderer = this._renderer;
+    let mesh = this._meshStacks;
 
-    let visualizationProgram = this.visualizationProgram;
-    let texture = this.texture;
+    let visualizationProgram = this._visualizationProgram;
+    let texture = this._texture;
     let mvMatrix = renderer.mvMatrix;
     let projMatrix = renderer.projMatrix;
 
     // use the necessary shader
     visualizationProgram.use();
 
-    // bind the attributes and uniforms as necessary
-    visualizationProgram.bindVertexBuffer("vertexPosition", mesh.vertices);
-    visualizationProgram.bindVertexBuffer("vertexTexCoord", mesh.texCoord);
-    visualizationProgram.bindVertexBuffer("vertexColor", mesh.color);
+    for (let i = 0, len = mesh.length; i < len; i++) {
+      let group = mesh[i];
+
+      // bind the attributes and uniforms as necessary
+      visualizationProgram.bindVertexBuffer("vertexPosition", group.vertices);
+      visualizationProgram.bindVertexBuffer("vertexTexCoord", group.texCoord);
+      visualizationProgram.bindVertexBuffer("vertexColor", group.color);
 
-    visualizationProgram.bindUniformMatrix("mvMatrix", mvMatrix);
-    visualizationProgram.bindUniformMatrix("projMatrix", projMatrix);
-    visualizationProgram.bindTexture("sampler", texture);
+      visualizationProgram.bindUniformMatrix("mvMatrix", mvMatrix);
+      visualizationProgram.bindUniformMatrix("projMatrix", projMatrix);
+      visualizationProgram.bindTexture("sampler", texture);
 
-    // draw the vertices as TRIANGLES indexed elements
-    renderer.drawIndexedVertices(renderer.context.TRIANGLES, mesh.indices);
+      // draw the vertices as TRIANGLES indexed elements
+      renderer.drawIndexedVertices(renderer.context.TRIANGLES, group.indices);
+    }
 
     // save the current model view and projection matrices
     mesh.mvMatrix = mat4.create(mvMatrix);
     mesh.projMatrix = mat4.create(projMatrix);
   },
 
   /**
    * Draws the meshWireframe object.
    */
-  drawMeshWireframe: function TVP_drawMeshWireframe()
+  _drawMeshWireframe: function TVP__drawMeshWireframe()
   {
-    let renderer = this.renderer;
-    let mesh = this.meshWireframe;
+    let renderer = this._renderer;
+    let mesh = this._meshWireframe;
 
-    // use the necessary shader
-    renderer.useColorShader(mesh.vertices, WIREFRAME_COLOR);
+    for (let i = 0, len = mesh.length; i < len; i++) {
+      let group = mesh[i];
 
-    // draw the vertices as LINES indexed elements
-    renderer.drawIndexedVertices(renderer.context.LINES, mesh.indices);
+      // use the necessary shader
+      renderer.useColorShader(group.vertices, WIREFRAME_COLOR);
+
+      // draw the vertices as LINES indexed elements
+      renderer.drawIndexedVertices(renderer.context.LINES, group.indices);
+    }
   },
 
   /**
    * Draws a highlighted quad around a currently selected node.
    */
-  drawHighlight: function TVP_drawHighlight()
+  _drawHighlight: function TVP__drawHighlight()
   {
     // check if there's anything to highlight (i.e any node is selected)
-    if (!this.highlight.disabled) {
+    if (!this._highlight.disabled) {
 
       // set the corresponding state to draw the highlight quad
-      let renderer = this.renderer;
-      let highlight = this.highlight;
+      let renderer = this._renderer;
+      let highlight = this._highlight;
 
       renderer.depthTest(false);
       renderer.fill(highlight.fill, 0.5);
       renderer.stroke(highlight.stroke);
       renderer.strokeWeight(highlight.strokeWeight);
       renderer.quad(highlight.v0, highlight.v1, highlight.v2, highlight.v3);
     }
   },
 
   /**
    * Creates or refreshes the texture applied to the visualization mesh.
    */
-  setupTexture: function TVP_setupTexture()
+  _setupTexture: function TVP__setupTexture()
   {
-    let renderer = this.renderer;
+    let renderer = this._renderer;
 
     // destroy any previously created texture
-    TiltUtils.destroyObject(this.texture);
+    TiltUtils.destroyObject(this._texture); this._texture = null;
 
     // if the renderer was destroyed, don't continue setup
     if (!renderer || !renderer.context) {
       return;
     }
 
     // get the maximum texture size
-    this.maxTextureSize =
+    this._maxTextureSize =
       renderer.context.getParameter(renderer.context.MAX_TEXTURE_SIZE);
 
     // use a simple shim to get the image representation of the document
     // this will be removed once the MOZ_window_region_texture bug #653656
     // is finished; currently just converting the document image to a texture
     // applied to the mesh
-    this.texture = new renderer.Texture({
+    this._texture = new renderer.Texture({
       source: TiltGL.TextureUtils.createContentImage(this.contentWindow,
-                                                     this.maxTextureSize),
+                                                     this._maxTextureSize),
       format: "RGB"
     });
 
-    if ("function" === typeof this.onSetupTexture) {
-      this.onSetupTexture();
-      this.onSetupTexture = null;
+    if ("function" === typeof this._onSetupTexture) {
+      this._onSetupTexture();
+      this._onSetupTexture = null;
     }
   },
 
   /**
    * Create the combined mesh representing the document visualization by
    * traversing the document & adding a stack for each node that is drawable.
    *
-   * @param {Object} aData
+   * @param {Object} aMeshData
    *                 object containing the necessary mesh verts, texcoord etc.
    */
-  setupMesh: function TVP_setupMesh(aData)
+  _setupMesh: function TVP__setupMesh(aMeshData)
   {
-    let renderer = this.renderer;
+    let renderer = this._renderer;
 
     // destroy any previously created mesh
-    TiltUtils.destroyObject(this.meshStacks);
-    TiltUtils.destroyObject(this.meshWireframe);
+    TiltUtils.destroyObject(this._meshStacks); this._meshStacks = [];
+    TiltUtils.destroyObject(this._meshWireframe); this._meshWireframe = [];
 
     // if the renderer was destroyed, don't continue setup
     if (!renderer || !renderer.context) {
       return;
     }
 
     // save the mesh data for future use
-    this.meshData = aData;
+    this._meshData = aMeshData;
+
+    // create a sub-mesh for each group in the mesh data
+    for (let i = 0, len = aMeshData.groups.length; i < len; i++) {
+      let group = aMeshData.groups[i];
 
-    // create the visualization mesh using the vertices, texture coordinates
-    // and indices computed when traversing the document object model
-    this.meshStacks = {
-      vertices: new renderer.VertexBuffer(aData.vertices, 3),
-      texCoord: new renderer.VertexBuffer(aData.texCoord, 2),
-      color: new renderer.VertexBuffer(aData.color, 3),
-      indices: new renderer.IndexBuffer(aData.stacksIndices)
-    };
+      // create the visualization mesh using the vertices, texture coordinates
+      // and indices computed when traversing the document object model
+      this._meshStacks.push({
+        vertices: new renderer.VertexBuffer(group.vertices, 3),
+        texCoord: new renderer.VertexBuffer(group.texCoord, 2),
+        color: new renderer.VertexBuffer(group.color, 3),
+        indices: new renderer.IndexBuffer(group.stacksIndices)
+      });
 
-    // additionally, create a wireframe representation to make the
-    // visualization a bit more pretty
-    this.meshWireframe = {
-      vertices: this.meshStacks.vertices,
-      indices: new renderer.IndexBuffer(aData.wireframeIndices)
-    };
+      // additionally, create a wireframe representation to make the
+      // visualization a bit more pretty
+      this._meshWireframe.push({
+        vertices: this._meshStacks[i].vertices,
+        indices: new renderer.IndexBuffer(group.wireframeIndices)
+      });
+    }
 
     // if there's no initial selection made, highlight the required node
     if (!this._initialSelection) {
       this._initialSelection = true;
       this.highlightNode(this.chromeWindow.InspectorUI.selection);
+
+      if (this._currentSelection === 0) { // if the "html" node is selected
+        this._highlight.disabled = true;
+      }
     }
 
+    // configure the required mesh transformations and background only once
     if (!this._initialMeshConfiguration) {
       this._initialMeshConfiguration = true;
 
-      let width = renderer.width;
-      let height = renderer.height;
-
       // set the necessary mesh offsets
-      this.transforms.offset[0] = -width * 0.5;
-      this.transforms.offset[1] = -height * 0.5;
+      this.transforms.offset[0] = -renderer.width * 0.5;
+      this.transforms.offset[1] = -renderer.height * 0.5;
 
       // make sure the canvas is opaque now that the initialization is finished
       this.canvas.style.background = TiltVisualizerStyle.canvas.background;
 
-      this.drawVisualization();
-      this.redraw = true;
+      this._drawVisualization();
+      this._redraw = true;
     }
 
-    if ("function" === typeof this.onSetupMesh) {
-      this.onSetupMesh();
-      this.onSetupMesh = null;
+    if ("function" === typeof this._onSetupMesh) {
+      this._onSetupMesh();
+      this._onSetupMesh = null;
     }
   },
 
   /**
-   * Computes the mesh vertices, texture coordinates etc.
+   * Computes the mesh vertices, texture coordinates etc. by groups of nodes.
    */
-  setupMeshData: function TVP_setupMeshData()
+  _setupMeshData: function TVP__setupMeshData()
   {
-    let renderer = this.renderer;
+    let renderer = this._renderer;
 
     // if the renderer was destroyed, don't continue setup
     if (!renderer || !renderer.context) {
       return;
     }
 
     // traverse the document and get the depths, coordinates and local names
-    this.traverseData = TiltUtils.DOM.traverse(this.contentWindow, {
+    this._traverseData = TiltUtils.DOM.traverse(this.contentWindow, {
       invisibleElements: INVISIBLE_ELEMENTS,
       minSize: ELEMENT_MIN_SIZE,
-      maxX: this.texture.width,
-      maxY: this.texture.height
+      maxX: this._texture.width,
+      maxY: this._texture.height
     });
 
     let worker = new ChromeWorker(TILT_CRAFTER);
 
     worker.addEventListener("message", function TVP_onMessage(event) {
-      this.setupMesh(event.data);
+      this._setupMesh(event.data);
     }.bind(this), false);
 
     // calculate necessary information regarding vertices, texture coordinates
     // etc. in a separate thread, as this process may take a while
     worker.postMessage({
+      maxGroupNodes: MAX_GROUP_NODES,
       thickness: STACK_THICKNESS,
       style: TiltVisualizerStyle.nodes,
-      texWidth: this.texture.width,
-      texHeight: this.texture.height,
-      nodesInfo: this.traverseData.info
+      texWidth: this._texture.width,
+      texHeight: this._texture.height,
+      nodesInfo: this._traverseData.info
     });
   },
 
   /**
    * Sets up event listeners necessary for the presenter.
    */
-  setupEventListeners: function TVP_setupEventListeners()
+  _setupEventListeners: function TVP__setupEventListeners()
   {
-    // bind the owner object to the necessary functions
-    TiltUtils.bindObjectFunc(this, "^on");
-
-    this.contentWindow.addEventListener("resize", this.onResize, false);
+    this.contentWindow.addEventListener("resize", this._onResize, false);
   },
 
   /**
    * Called when the content window of the current browser is resized.
    */
-  onResize: function TVP_onResize(e)
+  _onResize: function TVP_onResize(e)
   {
     let zoom = this.chromeWindow.InspectorUI.highlighter.zoom;
     let width = e.target.innerWidth * zoom;
     let height = e.target.innerHeight * zoom;
 
     // handle aspect ratio changes to update the projection matrix
-    this.renderer.width = width;
-    this.renderer.height = height;
+    this._renderer.width = width;
+    this._renderer.height = height;
 
-    this.redraw = true;
+    this._redraw = true;
   },
 
   /**
    * Highlights a specific node.
    *
    * @param {Element} aNode
    *                  the html node to be highlighted
    * @param {String} aFlags
    *                 flags specifying highlighting options
    */
   highlightNode: function TVP_highlightNode(aNode, aFlags)
   {
-    this.highlightNodeFor(this.traverseData.nodes.indexOf(aNode), aFlags);
+    this.highlightNodeFor(this._traverseData.nodes.indexOf(aNode), aFlags);
   },
 
   /**
    * Picks a stacked dom node at the x and y screen coordinates and highlights
    * the selected node in the mesh.
    *
    * @param {Number} x
    *                 the current horizontal coordinate of the mouse
@@ -700,41 +742,41 @@ TiltVisualizer.Presenter.prototype = {
     });
   },
 
   /**
    * Sets the corresponding highlight coordinates and color based on the
    * information supplied.
    *
    * @param {Number} aNodeIndex
-   *                 the index of the node in the this.traverseData array
+   *                 the index of the node in the this._traverseData array
    * @param {String} aFlags
    *                 flags specifying highlighting options
    */
   highlightNodeFor: function TVP_highlightNodeFor(aNodeIndex, aFlags)
   {
-    this.redraw = true;
+    this._redraw = true;
 
     // if the node was already selected, don't do anything
     if (this._currentSelection === aNodeIndex) {
       return;
     }
 
     // if an invalid or nonexisted node is specified, disable the highlight
     if (aNodeIndex < 0) {
       this._currentSelection = -1;
-      this.highlight.disabled = true;
+      this._highlight.disabled = true;
 
       Services.obs.notifyObservers(null, this.NOTIFICATIONS.UNHIGHLIGHTING, null);
       return;
     }
 
-    let highlight = this.highlight;
-    let info = this.traverseData.info[aNodeIndex];
-    let node = this.traverseData.nodes[aNodeIndex];
+    let highlight = this._highlight;
+    let info = this._traverseData.info[aNodeIndex];
+    let node = this._traverseData.nodes[aNodeIndex];
     let style = TiltVisualizerStyle.nodes;
 
     highlight.disabled = false;
     highlight.fill = style[info.name] || style.highlight.defaultFill;
     highlight.stroke = style.highlight.defaultStroke;
     highlight.strokeWeight = style.highlight.defaultStrokeWeight;
 
     let x = info.coord.left;
@@ -756,50 +798,53 @@ TiltVisualizer.Presenter.prototype = {
 
     // if something is highlighted, make sure it's inside the current viewport;
     // the point which should be moved into view is considered the center [x, y]
     // position along the top edge of the currently selected node
 
     if (aFlags && aFlags.indexOf("moveIntoView") !== -1)
     {
       this.controller.arcball.moveIntoView(vec3.lerp(
-        vec3.scale(this.highlight.v0, this.transforms.zoom, []),
-        vec3.scale(this.highlight.v1, this.transforms.zoom, []), 0.5));
+        vec3.scale(this._highlight.v0, this.transforms.zoom, []),
+        vec3.scale(this._highlight.v1, this.transforms.zoom, []), 0.5));
     }
 
     Services.obs.notifyObservers(null, this.NOTIFICATIONS.HIGHLIGHTING, null);
   },
 
   /**
    * Deletes a node from the visualization mesh.
    *
    * @param {Number} aNodeIndex
-   *                 the index of the node in the this.traverseData array;
+   *                 the index of the node in the this._traverseData array;
    *                 if not specified, it will default to the current selection
    */
   deleteNode: function TVP_deleteNode(aNodeIndex)
   {
     // we probably don't want to delete the html or body node.. just sayin'
     if ((aNodeIndex = aNodeIndex || this._currentSelection) < 1) {
       return;
     }
 
-    let renderer = this.renderer;
-    let meshData = this.meshData;
+    let renderer = this._renderer;
 
-    for (let i = 0, k = 36 * aNodeIndex; i < 36; i++) {
-      meshData.vertices[i + k] = 0;
+    let groupIndex = parseInt(aNodeIndex / MAX_GROUP_NODES);
+    let nodeIndex = parseInt((aNodeIndex + (groupIndex ? 1 : 0)) % MAX_GROUP_NODES);
+    let group = this._meshStacks[groupIndex];
+    let vertices = group.vertices.components;
+
+    for (let i = 0, k = 36 * nodeIndex; i < 36; i++) {
+      vertices[i + k] = 0;
     }
 
-    this.meshStacks.vertices = new renderer.VertexBuffer(meshData.vertices, 3);
-    this.highlight.disabled = true;
-    this.redraw = true;
+    group.vertices = new renderer.VertexBuffer(vertices, 3);
+    this._highlight.disabled = true;
+    this._redraw = true;
 
-    Services.obs.notifyObservers(null,
-      this.NOTIFICATIONS.NODE_REMOVED, null);
+    Services.obs.notifyObservers(null, this.NOTIFICATIONS.NODE_REMOVED, null);
   },
 
   /**
    * Picks a stacked dom node at the x and y screen coordinates and issues
    * a callback function with the found intersection.
    *
    * @param {Number} x
    *                 the current horizontal coordinate of the mouse
@@ -811,17 +856,17 @@ TiltVisualizer.Presenter.prototype = {
    *      {Function} onfail: function to be called if no intersections
    */
   pickNode: function TVP_pickNode(x, y, aProperties)
   {
     // make sure the properties parameter is a valid object
     aProperties = aProperties || {};
 
     // if the mesh wasn't created yet, don't continue picking
-    if (!this.meshStacks || !this.meshWireframe) {
+    if (!this._meshStacks || !this._meshWireframe) {
       return;
     }
 
     let worker = new ChromeWorker(TILT_PICKER);
 
     worker.addEventListener("message", function TVP_onMessage(event) {
       if (event.data) {
         if ("function" === typeof aProperties.onpick) {
@@ -830,33 +875,32 @@ TiltVisualizer.Presenter.prototype = {
       } else {
         if ("function" === typeof aProperties.onfail) {
           aProperties.onfail();
         }
       }
     }, false);
 
     let zoom = this.chromeWindow.InspectorUI.highlighter.zoom;
-    let width = this.renderer.width * zoom;
-    let height = this.renderer.height * zoom;
-    let mesh = this.meshStacks;
+    let width = this._renderer.width * zoom;
+    let height = this._renderer.height * zoom;
     x *= zoom;
     y *= zoom;
 
     // create a ray following the mouse direction from the near clipping plane
     // to the far clipping plane, to check for intersections with the mesh,
     // and do all the heavy lifting in a separate thread
     worker.postMessage({
       thickness: STACK_THICKNESS,
-      vertices: mesh.vertices.components,
+      vertices: this._meshData.allVertices,
 
       // create the ray destined for 3D picking
       ray: vec3.createRay([x, y, 0], [x, y, 1], [0, 0, width, height],
-        mesh.mvMatrix,
-        mesh.projMatrix)
+        this._meshStacks.mvMatrix,
+        this._meshStacks.projMatrix)
     });
   },
 
   /**
    * Delegate translation method, used by the controller.
    *
    * @param {Array} aTranslation
    *                the new translation on the [x, y, z] axis
@@ -869,17 +913,17 @@ TiltVisualizer.Presenter.prototype = {
     let transforms = this.transforms;
 
     // only update the translation if it's not already set
     if (transforms.translation[0] !== x ||
         transforms.translation[1] !== y ||
         transforms.translation[2] !== z) {
 
       vec3.set(aTranslation, transforms.translation);
-      this.redraw = true;
+      this._redraw = true;
     }
   },
 
   /**
    * Delegate rotation method, used by the controller.
    *
    * @param {Array} aQuaternion
    *                the rotation quaternion, as [x, y, z, w]
@@ -894,113 +938,118 @@ TiltVisualizer.Presenter.prototype = {
 
     // only update the rotation if it's not already set
     if (transforms.rotation[0] !== x ||
         transforms.rotation[1] !== y ||
         transforms.rotation[2] !== z ||
         transforms.rotation[3] !== w) {
 
       quat4.set(aQuaternion, transforms.rotation);
-      this.redraw = true;
+      this._redraw = true;
     }
   },
 
   /**
    * Handles notifications at specific frame counts.
    */
-  handleKeyframeNotifications: function TV_handleKeyframeNotifications()
+  _handleKeyframeNotifications: function TV__handleKeyframeNotifications()
   {
-    if (!TiltVisualizer.Prefs.introTransition && !this.isExecutingDestruction) {
-      this.frames = INTRO_TRANSITION_DURATION;
+    if (!TiltVisualizer.Prefs.introTransition && !this._isExecutingDestruction) {
+      this._time = INTRO_TRANSITION_DURATION;
     }
-    if (!TiltVisualizer.Prefs.outroTransition && this.isExecutingDestruction) {
-      this.frames = OUTRO_TRANSITION_DURATION;
+    if (!TiltVisualizer.Prefs.outroTransition && this._isExecutingDestruction) {
+      this._time = OUTRO_TRANSITION_DURATION;
     }
 
-    if (this.frames === INTRO_TRANSITION_DURATION &&
-       !this.isExecutingDestruction) {
+    if (this._time >= INTRO_TRANSITION_DURATION &&
+       !this._isInitializationFinished &&
+       !this._isExecutingDestruction) {
 
+      this._isInitializationFinished = true;
       Services.obs.notifyObservers(null, this.NOTIFICATIONS.INITIALIZED, null);
 
-      if ("function" === typeof this.onInitializationFinished) {
-        this.onInitializationFinished();
+      if ("function" === typeof this._onInitializationFinished) {
+        this._onInitializationFinished();
       }
     }
 
-    if (this.frames === OUTRO_TRANSITION_DURATION &&
-        this.isExecutingDestruction) {
+    if (this._time >= OUTRO_TRANSITION_DURATION &&
+       !this._isDestructionFinished &&
+        this._isExecutingDestruction) {
 
+      this._isDestructionFinished = true;
       Services.obs.notifyObservers(null, this.NOTIFICATIONS.BEFORE_DESTROYED, null);
 
-      if ("function" === typeof this.onDestructionFinished) {
-        this.onDestructionFinished();
+      if ("function" === typeof this._onDestructionFinished) {
+        this._onDestructionFinished();
       }
     }
   },
 
   /**
    * Starts executing the destruction sequence and issues a callback function
    * when finished.
    *
    * @param {Function} aCallback
    *                   the destruction finished callback
    */
   executeDestruction: function TV_executeDestruction(aCallback)
   {
-    if (!this.isExecutingDestruction) {
-      this.isExecutingDestruction = true;
-      this.onDestructionFinished = aCallback;
+    if (!this._isExecutingDestruction) {
+      this._isExecutingDestruction = true;
+      this._onDestructionFinished = aCallback;
 
       // if we execute the destruction after the initialization finishes,
       // proceed normally; otherwise, skip everything and immediately issue
       // the callback
 
-      if (this.frames > OUTRO_TRANSITION_DURATION) {
-        this.frames = 0;
-        this.redraw = true;
+      if (this._time > OUTRO_TRANSITION_DURATION) {
+        this._time = 0;
+        this._redraw = true;
       } else {
         aCallback();
       }
     }
   },
 
   /**
    * Checks if this object was initialized properly.
    *
    * @return {Boolean} true if the object was initialized properly
    */
   isInitialized: function TVP_isInitialized()
   {
-    return this.renderer && this.renderer.context;
+    return this._renderer && this._renderer.context;
   },
 
   /**
    * Function called when this object is destroyed.
    */
-  finalize: function TVP_finalize()
+  _finalize: function TVP__finalize()
   {
-    TiltUtils.destroyObject(this.visualizationProgram);
-    TiltUtils.destroyObject(this.texture);
+    TiltUtils.destroyObject(this._visualizationProgram);
+    TiltUtils.destroyObject(this._texture);
 
-    if (this.meshStacks) {
-      TiltUtils.destroyObject(this.meshStacks.vertices);
-      TiltUtils.destroyObject(this.meshStacks.texCoord);
-      TiltUtils.destroyObject(this.meshStacks.color);
-      TiltUtils.destroyObject(this.meshStacks.indices);
+    if (this._meshStacks) {
+      this._meshStacks.forEach(function(group) {
+        TiltUtils.destroyObject(group.vertices);
+        TiltUtils.destroyObject(group.texCoord);
+        TiltUtils.destroyObject(group.color);
+        TiltUtils.destroyObject(group.indices);
+      });
+    }
+    if (this._meshWireframe) {
+      this._meshWireframe.forEach(function(group) {
+        TiltUtils.destroyObject(group.indices);
+      });
     }
 
-    if (this.meshWireframe) {
-      TiltUtils.destroyObject(this.meshWireframe.indices);
-    }
+    TiltUtils.destroyObject(this._renderer);
 
-    TiltUtils.destroyObject(this.highlight);
-    TiltUtils.destroyObject(this.transforms);
-    TiltUtils.destroyObject(this.renderer);
-
-    this.contentWindow.removeEventListener("resize", this.onResize, false);
+    this.contentWindow.removeEventListener("resize", this._onResize, false);
   }
 };
 
 /**
  * A mouse and keyboard controller implementation.
  *
  * @param {HTMLCanvasElement} aCanvas
  *                            the visualization canvas element
@@ -1018,141 +1067,143 @@ TiltVisualizer.Controller = function TV_
    * Save a reference to the presenter to modify its model-view transforms.
    */
   this.presenter = aPresenter;
   this.presenter.controller = this;
 
   /**
    * The initial controller dimensions and offset, in pixels.
    */
-  this.zoom = aPresenter.transforms.zoom;
-  this.left = (aPresenter.contentWindow.pageXOffset || 0) * this.zoom;
-  this.top = (aPresenter.contentWindow.pageYOffset || 0) * this.zoom;
-  this.width = aCanvas.width;
-  this.height = aCanvas.height;
+  this._zoom = aPresenter.transforms.zoom;
+  this._left = (aPresenter.contentWindow.pageXOffset || 0) * this._zoom;
+  this._top = (aPresenter.contentWindow.pageYOffset || 0) * this._zoom;
+  this._width = aCanvas.width;
+  this._height = aCanvas.height;
 
   /**
    * Arcball used to control the visualization using the mouse.
    */
   this.arcball = new TiltVisualizer.Arcball(
-    this.presenter.chromeWindow, this.width, this.height, 0,
+    this.presenter.chromeWindow, this._width, this._height, 0,
     [
-      this.width + this.left < aPresenter.maxTextureSize ? -this.left : 0,
-      this.height + this.top < aPresenter.maxTextureSize ? -this.top : 0
+      this._width + this._left < aPresenter._maxTextureSize ? -this._left : 0,
+      this._height + this._top < aPresenter._maxTextureSize ? -this._top : 0
     ]);
 
   /**
    * Object containing the rotation quaternion and the translation amount.
    */
-  this.coordinates = null;
+  this._coordinates = null;
 
   // bind the owner object to the necessary functions
-  TiltUtils.bindObjectFunc(this, "update");
-  TiltUtils.bindObjectFunc(this, "^on");
+  TiltUtils.bindObjectFunc(this, "_update");
+  TiltUtils.bindObjectFunc(this, "^_on");
 
   // add the necessary event listeners
   this.addEventListeners();
 
   // attach this controller's update function to the presenter ondraw event
-  aPresenter.ondraw = this.update;
+  this.presenter._controllerUpdate = this._update;
 };
 
 TiltVisualizer.Controller.prototype = {
 
   /**
    * Adds events listeners required by this controller.
    */
   addEventListeners: function TVC_addEventListeners()
   {
     let canvas = this.canvas;
     let presenter = this.presenter;
 
     // bind commonly used mouse and keyboard events with the controller
-    canvas.addEventListener("mousedown", this.onMouseDown, false);
-    canvas.addEventListener("mouseup", this.onMouseUp, false);
-    canvas.addEventListener("mousemove", this.onMouseMove, false);
-    canvas.addEventListener("mouseover", this.onMouseOver, false);
-    canvas.addEventListener("mouseout", this.onMouseOut, false);
-    canvas.addEventListener("MozMousePixelScroll", this.onMozScroll, false);
-    canvas.addEventListener("keydown", this.onKeyDown, false);
-    canvas.addEventListener("keyup", this.onKeyUp, false);
-    canvas.addEventListener("keypress", this.onKeyPress, true);
-    canvas.addEventListener("blur", this.onBlur, false);
+    canvas.addEventListener("mousedown", this._onMouseDown, false);
+    canvas.addEventListener("mouseup", this._onMouseUp, false);
+    canvas.addEventListener("mousemove", this._onMouseMove, false);
+    canvas.addEventListener("mouseover", this._onMouseOver, false);
+    canvas.addEventListener("mouseout", this._onMouseOut, false);
+    canvas.addEventListener("MozMousePixelScroll", this._onMozScroll, false);
+    canvas.addEventListener("keydown", this._onKeyDown, false);
+    canvas.addEventListener("keyup", this._onKeyUp, false);
+    canvas.addEventListener("keypress", this._onKeyPress, true);
+    canvas.addEventListener("blur", this._onBlur, false);
 
     // handle resize events to change the arcball dimensions
-    presenter.contentWindow.addEventListener("resize", this.onResize, false);
+    presenter.contentWindow.addEventListener("resize", this._onResize, false);
   },
 
   /**
    * Removes all added events listeners required by this controller.
    */
   removeEventListeners: function TVC_removeEventListeners()
   {
     let canvas = this.canvas;
     let presenter = this.presenter;
 
-    canvas.removeEventListener("mousedown", this.onMouseDown, false);
-    canvas.removeEventListener("mouseup", this.onMouseUp, false);
-    canvas.removeEventListener("mousemove", this.onMouseMove, false);
-    canvas.removeEventListener("mouseover", this.onMouseOver, false);
-    canvas.removeEventListener("mouseout", this.onMouseOut, false);
-    canvas.removeEventListener("MozMousePixelScroll", this.onMozScroll, false);
-    canvas.removeEventListener("keydown", this.onKeyDown, false);
-    canvas.removeEventListener("keyup", this.onKeyUp, false);
-    canvas.removeEventListener("keypress", this.onKeyPress, true);
-    canvas.removeEventListener("blur", this.onBlur, false);
+    canvas.removeEventListener("mousedown", this._onMouseDown, false);
+    canvas.removeEventListener("mouseup", this._onMouseUp, false);
+    canvas.removeEventListener("mousemove", this._onMouseMove, false);
+    canvas.removeEventListener("mouseover", this._onMouseOver, false);
+    canvas.removeEventListener("mouseout", this._onMouseOut, false);
+    canvas.removeEventListener("MozMousePixelScroll", this._onMozScroll, false);
+    canvas.removeEventListener("keydown", this._onKeyDown, false);
+    canvas.removeEventListener("keyup", this._onKeyUp, false);
+    canvas.removeEventListener("keypress", this._onKeyPress, true);
+    canvas.removeEventListener("blur", this._onBlur, false);
 
-    presenter.contentWindow.removeEventListener("resize", this.onResize,false);
+    presenter.contentWindow.removeEventListener("resize", this._onResize, false);
   },
 
   /**
    * Function called each frame, updating the visualization camera transforms.
    *
-   * @param {Number} aFrames
-   *                 the current animation frame count
+   * @param {Number} aTime
+   *                 total time passed since rendering started
+   * @param {Number} aDelta
+   *                 the current animation frame delta
    */
-  update: function TVC_update(aFrames)
+  _update: function TVC__update(aTime, aDelta)
   {
-    this.frames = aFrames;
-    this.coordinates = this.arcball.update();
+    this._time = aTime;
+    this._coordinates = this.arcball.update(aDelta);
 
-    this.presenter.setRotation(this.coordinates.rotation);
-    this.presenter.setTranslation(this.coordinates.translation);
+    this.presenter.setRotation(this._coordinates.rotation);
+    this.presenter.setTranslation(this._coordinates.translation);
   },
 
   /**
    * Called once after every time a mouse button is pressed.
    */
-  onMouseDown: function TVC_onMouseDown(e)
+  _onMouseDown: function TVC__onMouseDown(e)
   {
     e.target.focus();
     e.preventDefault();
     e.stopPropagation();
 
-    if (this.frames < MOUSE_INTRO_DELAY) {
+    if (this._time < MOUSE_INTRO_DELAY) {
       return;
     }
 
     // calculate x and y coordinates using using the client and target offset
     let button = e.which;
     this._downX = e.clientX - e.target.offsetLeft;
     this._downY = e.clientY - e.target.offsetTop;
 
     this.arcball.mouseDown(this._downX, this._downY, button);
   },
 
   /**
    * Called every time a mouse button is released.
    */
-  onMouseUp: function TVC_onMouseUp(e)
+  _onMouseUp: function TVC__onMouseUp(e)
   {
     e.preventDefault();
     e.stopPropagation();
 
-    if (this.frames < MOUSE_INTRO_DELAY) {
+    if (this._time < MOUSE_INTRO_DELAY) {
       return;
     }
 
     // calculate x and y coordinates using using the client and target offset
     let button = e.which;
     let upX = e.clientX - e.target.offsetLeft;
     let upY = e.clientY - e.target.offsetTop;
 
@@ -1165,123 +1216,131 @@ TiltVisualizer.Controller.prototype = {
     }
 
     this.arcball.mouseUp(upX, upY, button);
   },
 
   /**
    * Called every time the mouse moves.
    */
-  onMouseMove: function TVC_onMouseMove(e)
+  _onMouseMove: function TVC__onMouseMove(e)
   {
     e.preventDefault();
     e.stopPropagation();
 
-    if (this.frames < MOUSE_INTRO_DELAY) {
+    if (this._time < MOUSE_INTRO_DELAY) {
       return;
     }
 
     // calculate x and y coordinates using using the client and target offset
     let moveX = e.clientX - e.target.offsetLeft;
     let moveY = e.clientY - e.target.offsetTop;
 
     this.arcball.mouseMove(moveX, moveY);
   },
 
   /**
    * Called when the mouse leaves the visualization bounds.
    */
-  onMouseOver: function TVC_onMouseOver(e)
+  _onMouseOver: function TVC__onMouseOver(e)
   {
     e.preventDefault();
     e.stopPropagation();
 
     this.arcball.mouseOver();
   },
 
   /**
    * Called when the mouse leaves the visualization bounds.
    */
-  onMouseOut: function TVC_onMouseOut(e)
+  _onMouseOut: function TVC__onMouseOut(e)
   {
     e.preventDefault();
     e.stopPropagation();
 
     this.arcball.mouseOut();
   },
 
   /**
    * Called when the mouse wheel is used.
    */
-  onMozScroll: function TVC_onMozScroll(e)
+  _onMozScroll: function TVC__onMozScroll(e)
   {
     e.preventDefault();
     e.stopPropagation();
 
     this.arcball.zoom(e.detail);
   },
 
   /**
    * Called when a key is pressed.
    */
-  onKeyDown: function TVC_onKeyDown(e)
+  _onKeyDown: function TVC__onKeyDown(e)
   {
     let code = e.keyCode || e.which;
 
     if (!e.altKey && !e.ctrlKey && !e.metaKey && !e.shiftKey) {
       e.preventDefault();
       e.stopPropagation();
       this.arcball.keyDown(code);
     } else {
       this.arcball.cancelKeyEvents();
     }
   },
 
   /**
    * Called when a key is released.
    */
-  onKeyUp: function TVC_onKeyUp(e)
+  _onKeyUp: function TVC__onKeyUp(e)
   {
     let code = e.keyCode || e.which;
 
     if (code === e.DOM_VK_X) {
       this.presenter.deleteNode();
     }
+    if (code === e.DOM_VK_F) {
+      let highlight = this.presenter._highlight;
+      let zoom = this.presenter.transforms.zoom;
+
+      this.arcball.moveIntoView(vec3.lerp(
+        vec3.scale(highlight.v0, zoom, []),
+        vec3.scale(highlight.v1, zoom, []), 0.5));
+    }
     if (!e.altKey && !e.ctrlKey && !e.metaKey && !e.shiftKey) {
       e.preventDefault();
       e.stopPropagation();
       this.arcball.keyUp(code);
     }
   },
 
   /**
    * Called when a key is pressed.
    */
-  onKeyPress: function TVC_onKeyPress(e)
+  _onKeyPress: function TVC__onKeyPress(e)
   {
     let tilt = this.presenter.chromeWindow.Tilt;
 
     if (e.keyCode === e.DOM_VK_ESCAPE) {
       e.preventDefault();
       e.stopPropagation();
       tilt.destroy(tilt.currentWindowId, true);
     }
   },
 
   /**
    * Called when the canvas looses focus.
    */
-  onBlur: function TVC_onBlur(e) {
+  _onBlur: function TVC__onBlur(e) {
     this.arcball.cancelKeyEvents();
   },
 
   /**
    * Called when the content window of the current browser is resized.
    */
-  onResize: function TVC_onResize(e)
+  _onResize: function TVC__onResize(e)
   {
     let zoom = this.presenter.chromeWindow.InspectorUI.highlighter.zoom;
     let width = e.target.innerWidth * zoom;
     let height = e.target.innerHeight * zoom;
 
     this.arcball.resize(width, height);
   },
 
@@ -1293,23 +1352,24 @@ TiltVisualizer.Controller.prototype = {
   isInitialized: function TVC_isInitialized()
   {
     return this.arcball ? true : false;
   },
 
   /**
    * Function called when this object is destroyed.
    */
-  finalize: function TVC_finalize()
+  _finalize: function TVC__finalize()
   {
     TiltUtils.destroyObject(this.arcball);
-    TiltUtils.destroyObject(this.coordinates);
+    TiltUtils.destroyObject(this._coordinates);
 
     this.removeEventListeners();
-    this.presenter.ondraw = null;
+    this.presenter.controller = null;
+    this.presenter._controllerUpdate = null;
   }
 };
 
 /**
  * This is a general purpose 3D rotation controller described by Ken Shoemake
  * in the Graphics Interface ’92 Proceedings. It features good behavior
  * easy implementation, cheap execution.
  *
@@ -1389,19 +1449,22 @@ TiltVisualizer.Arcball = function TV_Arc
 
 TiltVisualizer.Arcball.prototype = {
 
   /**
    * Call this function whenever you need the updated rotation quaternion
    * and the zoom amount. These values will be returned as "rotation" and
    * "translation" properties inside an object.
    *
+   * @param {Number} aDelta
+   *                 the current animation frame delta
+   *
    * @return {Object} the rotation quaternion and the translation amount
    */
-  update: function TVA_update()
+  update: function TVA_update(aDelta)
   {
     let mousePress = this._mousePress;
     let mouseRelease = this._mouseRelease;
     let mouseMove = this._mouseMove;
     let mouseLerp = this._mouseLerp;
     let mouseButton = this._mouseButton;
 
     // smoothly update the mouse coordinates
@@ -1429,17 +1492,17 @@ TiltVisualizer.Arcball.prototype = {
 
     // left mouse button handles rotation
     if (mouseButton === 1 || this._rotating) {
       // the rotation doesn't stop immediately after the left mouse button is
       // released, so add a flag to smoothly continue it until it ends
       this._rotating = true;
 
       // find the sphere coordinates of the mouse positions
-      this.pointToSphere(x, y, this.width, this.height, this.radius, endVec);
+      this._pointToSphere(x, y, this.width, this.height, this.radius, endVec);
 
       // compute the vector perpendicular to the start & end vectors
       vec3.cross(startVec, endVec, pVec);
 
       // if the begin and end vectors don't coincide
       if (vec3.length(pVec) > 0) {
         deltaRot[0] = pVec[0];
         deltaRot[1] = pVec[1];
@@ -1549,21 +1612,29 @@ TiltVisualizer.Arcball.prototype = {
       (additionalRot[2] - deltaAdditionalRot[2]) * ARCBALL_SENSITIVITY;
 
     deltaAdditionalTrans[0] +=
       (additionalTrans[0] - deltaAdditionalTrans[0]) * ARCBALL_SENSITIVITY;
     deltaAdditionalTrans[1] +=
       (additionalTrans[1] - deltaAdditionalTrans[1]) * ARCBALL_SENSITIVITY;
 
     // create an additional rotation based on the key events
-    quat4.fromEuler(deltaAdditionalRot[0], deltaAdditionalRot[1], 0, deltaRot);
+    quat4.fromEuler(
+      deltaAdditionalRot[0],
+      deltaAdditionalRot[1],
+      deltaAdditionalRot[2], deltaRot);
 
     // create an additional translation based on the key events
     vec3.set([deltaAdditionalTrans[0], deltaAdditionalTrans[1], 0], deltaTrans);
 
+    // handle the reset animation steps if necessary
+    if (this._resetInProgress) {
+      this._nextResetStep(aDelta || 1);
+    }
+
     // return the current rotation and translation
     return {
       rotation: quat4.multiply(deltaRot, currentRot),
       translation: vec3.add(deltaTrans, currentTrans)
     };
   },
 
   /**
@@ -1578,21 +1649,21 @@ TiltVisualizer.Arcball.prototype = {
    *                 which mouse button was pressed
    */
   mouseDown: function TVA_mouseDown(x, y, aButton)
   {
     // save the mouse down state and prepare for rotations or translations
     this._mousePress[0] = x;
     this._mousePress[1] = y;
     this._mouseButton = aButton;
-    this._cancelResetInterval();
+    this._cancelReset();
     this._save();
 
     // find the sphere coordinates of the mouse positions
-    this.pointToSphere(
+    this._pointToSphere(
       x, y, this.width, this.height, this.radius, this._startVec);
 
     quat4.set(thi