merge backout
authorMats Palmgren <matspal@gmail.com>
Tue, 07 Feb 2012 12:31:19 +0100
changeset 86406 66458f5036df876716ad85a3a666a06c6d5052ac
parent 86405 cffa39f708c003c341cdeb6111a01f88a4813a62 (current diff)
parent 86404 f7ca5d73bbdd74dff00d1e459557cee246170f32 (diff)
child 86408 9237d0cacba5170aa0deb666b15d9a98314b08d9
push id94
push userbturner@mozilla.com
push dateWed, 08 Feb 2012 05:39:15 +0000
milestone13.0a1
merge backout
accessible/tests/mochitest/name/nsRootAcc_wnd.xul
accessible/tests/mochitest/name/test_nsRootAcc.xul
build/mobile/robocop/FennecNativeAssert.java.in
build/mobile/sutagent/android/NtpMessage.java
content/canvas/test/webgl/undo-r15330-async-test-list-loading.patch
gfx/layers/opengl/MacIOSurfaceImageOGL.h
gfx/layers/opengl/MacIOSurfaceImageOGL.mm
js/src/jsstdint.h
mobile/android/base/resources/drawable-hdpi-v8/abouthome_icon.png
mobile/android/base/resources/drawable-hdpi-v8/abouthome_logo.png
mobile/android/base/resources/drawable-hdpi-v8/abouthome_separator.9.png
mobile/android/base/resources/drawable-hdpi-v8/abouthome_sync_bg.9.png
mobile/android/base/resources/drawable-hdpi-v8/abouthome_sync_logo.png
mobile/android/base/resources/drawable-hdpi-v8/abouthome_sync_pressed_bg.9.png
mobile/android/base/resources/drawable-hdpi-v8/abouthome_topsite_placeholder.png
mobile/android/base/resources/drawable-hdpi-v8/abouthome_topsite_shadow.9.png
mobile/android/base/resources/drawable-hdpi-v8/address_bar_texture.png
mobile/android/base/resources/drawable-hdpi-v8/address_bar_url_bg.9.png
mobile/android/base/resources/drawable-hdpi-v8/address_bar_url_outline.9.png
mobile/android/base/resources/drawable-hdpi-v8/awesomebar_tab.9.png
mobile/android/base/resources/drawable-hdpi-v8/awesomebar_tab_pressed.9.png
mobile/android/base/resources/drawable-hdpi-v8/doorhanger_arrow.png
mobile/android/base/resources/drawable-hdpi-v8/doorhanger_bg.9.png
mobile/android/base/resources/drawable-hdpi-v8/doorhanger_popup_bg.9.png
mobile/android/base/resources/drawable-hdpi-v8/doorhanger_shadow_bg.9.png
mobile/android/base/resources/drawable-hdpi-v8/favicon.png
mobile/android/base/resources/drawable-hdpi-v8/home_bg.png
mobile/android/base/resources/drawable-hdpi-v8/home_star.png
mobile/android/base/resources/drawable-hdpi-v8/ic_addons_empty.png
mobile/android/base/resources/drawable-hdpi-v8/ic_awesomebar_go.png
mobile/android/base/resources/drawable-hdpi-v8/ic_awesomebar_search.png
mobile/android/base/resources/drawable-hdpi-v8/ic_menu_bookmark_add.png
mobile/android/base/resources/drawable-hdpi-v8/ic_menu_bookmark_remove.png
mobile/android/base/resources/drawable-hdpi-v8/ic_menu_find_in_page.png
mobile/android/base/resources/drawable-hdpi-v8/ic_menu_forward.png
mobile/android/base/resources/drawable-hdpi-v8/ic_menu_reload.png
mobile/android/base/resources/drawable-hdpi-v8/ic_menu_save_as_pdf.png
mobile/android/base/resources/drawable-hdpi-v8/ic_menu_share.png
mobile/android/base/resources/drawable-hdpi-v8/site_security_identified.png
mobile/android/base/resources/drawable-hdpi-v8/site_security_verified.png
mobile/android/base/resources/drawable-hdpi-v8/tab_close.png
mobile/android/base/resources/drawable-hdpi-v8/tab_new.png
mobile/android/base/resources/drawable-hdpi-v8/tab_selected.png
mobile/android/base/resources/drawable-hdpi-v8/tab_thumbnail_default.png
mobile/android/base/resources/drawable-hdpi-v8/tab_thumbnail_shadow.png
mobile/android/base/resources/drawable-hdpi-v8/tabs_more.png
mobile/android/base/resources/drawable-hdpi-v8/tabs_normal.png
mobile/android/base/resources/drawable-hdpi-v8/tabs_plus.png
mobile/android/base/resources/drawable-hdpi-v8/tabs_pressed.png
mobile/android/base/resources/drawable-hdpi-v8/urlbar_stop.png
mobile/android/base/resources/drawable-mdpi-v8/abouthome_icon.png
mobile/android/base/resources/drawable-mdpi-v8/abouthome_logo.png
mobile/android/base/resources/drawable-mdpi-v8/abouthome_separator.9.png
mobile/android/base/resources/drawable-mdpi-v8/abouthome_sync_bg.9.png
mobile/android/base/resources/drawable-mdpi-v8/abouthome_sync_logo.png
mobile/android/base/resources/drawable-mdpi-v8/abouthome_sync_pressed_bg.9.png
mobile/android/base/resources/drawable-mdpi-v8/abouthome_topsite_placeholder.png
mobile/android/base/resources/drawable-mdpi-v8/abouthome_topsite_shadow.9.png
mobile/android/base/resources/drawable-mdpi-v8/address_bar_texture.png
mobile/android/base/resources/drawable-mdpi-v8/address_bar_url_bg.9.png
mobile/android/base/resources/drawable-mdpi-v8/address_bar_url_outline.9.png
mobile/android/base/resources/drawable-mdpi-v8/awesomebar_tab.9.png
mobile/android/base/resources/drawable-mdpi-v8/awesomebar_tab_pressed.9.png
mobile/android/base/resources/drawable-mdpi-v8/doorhanger_arrow.png
mobile/android/base/resources/drawable-mdpi-v8/doorhanger_bg.9.png
mobile/android/base/resources/drawable-mdpi-v8/doorhanger_popup_bg.9.png
mobile/android/base/resources/drawable-mdpi-v8/doorhanger_shadow_bg.9.png
mobile/android/base/resources/drawable-mdpi-v8/favicon.png
mobile/android/base/resources/drawable-mdpi-v8/ic_addons_empty.png
mobile/android/base/resources/drawable-mdpi-v8/ic_awesomebar_go.png
mobile/android/base/resources/drawable-mdpi-v8/ic_awesomebar_search.png
mobile/android/base/resources/drawable-mdpi-v8/ic_menu_bookmark_add.png
mobile/android/base/resources/drawable-mdpi-v8/ic_menu_bookmark_remove.png
mobile/android/base/resources/drawable-mdpi-v8/ic_menu_find_in_page.png
mobile/android/base/resources/drawable-mdpi-v8/ic_menu_forward.png
mobile/android/base/resources/drawable-mdpi-v8/ic_menu_reload.png
mobile/android/base/resources/drawable-mdpi-v8/ic_menu_save_as_pdf.png
mobile/android/base/resources/drawable-mdpi-v8/ic_menu_share.png
mobile/android/base/resources/drawable-mdpi-v8/site_security_identified.png
mobile/android/base/resources/drawable-mdpi-v8/site_security_verified.png
mobile/android/base/resources/drawable-mdpi-v8/tab_close.png
mobile/android/base/resources/drawable-mdpi-v8/tab_new.png
mobile/android/base/resources/drawable-mdpi-v8/tab_selected.png
mobile/android/base/resources/drawable-mdpi-v8/tab_thumbnail_default.png
mobile/android/base/resources/drawable-mdpi-v8/tab_thumbnail_shadow.png
mobile/android/base/resources/drawable-mdpi-v8/tabs_more.png
mobile/android/base/resources/drawable-mdpi-v8/tabs_normal.png
mobile/android/base/resources/drawable-mdpi-v8/tabs_plus.png
mobile/android/base/resources/drawable-mdpi-v8/tabs_pressed.png
mobile/android/base/resources/drawable-mdpi-v8/urlbar_stop.png
mobile/android/base/resources/drawable/sync_icon.png
mobile/android/base/resources/xml/sync_options.xml
mobile/android/sync/android-xml-resources.mn
toolkit/components/url-classifier/tests/unit/test_cleankeycache.js
toolkit/content/tests/chrome/test_bug649840.xul
tools/profiler/public/nsIProfiler.idl
tools/profiler/sps/TableTicker.cpp
tools/profiler/sps/platform-linux.cc
tools/profiler/sps/platform-win32.cc
tools/profiler/sps/platform.h
tools/profiler/sps/shared-libraries-linux.cc
tools/profiler/sps/shared-libraries-macos.cc
tools/profiler/sps/shared-libraries-win32.cc
tools/profiler/sps/shared-libraries.h
tools/profiler/sps/sps_sampler.h
tools/profiler/sps/thread_helper.h
tools/profiler/sps/v8-support.h
widget/tests/window_bug596600.xul
xpcom/io/nsLocalFileUnicode.h
--- a/.hgtags
+++ b/.hgtags
@@ -68,8 +68,13 @@ 138f593553b66c9f815e8f57870c19d6347f7702
 462c726144bc1fb45b61e774f64ac5d61b4e047c UPDATE_PACKAGING_R14
 5eb553dd2ceae5f88d80f27afc5ef3935c5d43b0 AURORA_BASE_20110705
 41b84b87c816403e1b74963d8094cff0406c989e AURORA_BASE_20110816
 c0983049bcaa9551e5f276d5a77ce154c151e0b0 AURORA_BASE_20110927
 462c726144bc1fb45b61e774f64ac5d61b4e047c UPDATE_PACKAGING_R15
 54bfd8bf682e295ffd7f22fa921ca343957b6c1c AURORA_BASE_20111108
 a8506ab2c65480cf2f85f54e203ea746522c62bb AURORA_BASE_20111220
 462c726144bc1fb45b61e774f64ac5d61b4e047c UPDATE_PACKAGING_R16
+bbc7014db2de49e2301680d2a86be8a53108a88a AURORA_BASE_20120131
+bbc7014db2de49e2301680d2a86be8a53108a88a AURORA_BASE_20120131
+0000000000000000000000000000000000000000 AURORA_BASE_20120131
+0000000000000000000000000000000000000000 AURORA_BASE_20120131
+bbc7014db2de49e2301680d2a86be8a53108a88a AURORA_BASE_20120131
--- a/accessible/Makefile.in
+++ b/accessible/Makefile.in
@@ -40,14 +40,12 @@ topsrcdir = @top_srcdir@
 srcdir    = @srcdir@
 VPATH   = @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 MODULE	= accessibility
 DIRS    = public src build
 
-ifdef ENABLE_TESTS
-DIRS    += tests
-endif
+TEST_DIRS += tests
 
 include $(topsrcdir)/config/rules.mk
 
--- a/accessible/public/Makefile.in
+++ b/accessible/public/Makefile.in
@@ -57,20 +57,22 @@ XPIDLSRCS = \
       nsIAccessibleApplication.idl \
       nsIAccessibleRelation.idl \
       nsIAccessibleRole.idl \
       nsIAccessibleStates.idl \
       nsIAccessibleDocument.idl \
       nsIAccessibleProvider.idl \
       nsIAccessibleSelectable.idl \
       nsIAccessNode.idl \
+      nsIAccessibleCursorable.idl \
       nsIAccessibleEvent.idl \
       nsIAccessibleEditableText.idl \
       nsIAccessibleHyperLink.idl \
       nsIAccessibleHyperText.idl \
+      nsIAccessiblePivot.idl \
       nsIAccessibleTable.idl \
       nsIAccessibleText.idl \
       nsIAccessibleValue.idl \
       nsIAccessibleImage.idl \
       nsIXBLAccessible.idl \
       $(NULL)
 
 EXPORTS		= \
new file mode 100644
--- /dev/null
+++ b/accessible/public/nsIAccessibleCursorable.idl
@@ -0,0 +1,59 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* ***** 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.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2012
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Eitan Isaacson <eitan@monotonous.org> (original author)
+ *
+ * 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 ***** */
+
+#include "nsISupports.idl"
+
+interface nsIAccessiblePivot;
+
+/**
+ * An interface implemented by an accessible object that has an associated
+ * virtual cursor. Typically, a top-level application or content document.
+ * A virtual cursor is an implementation of nsIAccessiblePivot that provides an
+ * exclusive spot in the cursorable's subtree, this could be used to create a
+ * pseudo-focus or caret browsing experience that is centered around the
+ * accessibility API.
+ */
+[scriptable, uuid(5452dea5-d235-496f-8757-3ca016ff49ff)]
+interface nsIAccessibleCursorable : nsISupports
+{
+  /**
+   * The virtual cursor pivot this object manages.
+   */
+  readonly attribute nsIAccessiblePivot virtualCursor;
+};
--- a/accessible/public/nsIAccessibleEvent.idl
+++ b/accessible/public/nsIAccessibleEvent.idl
@@ -437,19 +437,24 @@ interface nsIAccessibleEvent : nsISuppor
   const unsigned long EVENT_HYPERTEXT_NLINKS_CHANGED = 0x0054;
 
   /**
    * An object's attributes changed. Also see EVENT_TEXT_ATTRIBUTE_CHANGED.
    */
   const unsigned long EVENT_OBJECT_ATTRIBUTE_CHANGED = 0x0055;
 
   /**
+   * A cursorable's virtual cursor has changed.
+   */
+  const unsigned long EVENT_VIRTUALCURSOR_CHANGED = 0x0056;
+
+  /**
    * Help make sure event map does not get out-of-line.
    */
-  const unsigned long EVENT_LAST_ENTRY = 0x0056;
+  const unsigned long EVENT_LAST_ENTRY = 0x0057;
 
   /**
    * The type of event, based on the enumerated event values
    * defined in this interface.
    */
   readonly attribute unsigned long eventType;
   
   /**
new file mode 100644
--- /dev/null
+++ b/accessible/public/nsIAccessiblePivot.idl
@@ -0,0 +1,221 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* ***** 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.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Eitan Isaacson <eitan@monotonous.org> (original author)
+ *
+ * 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 ***** */
+
+#include "nsISupports.idl"
+
+typedef short TextBoundaryType;
+
+interface nsIAccessible;
+interface nsIAccessibleText;
+interface nsIAccessibleTraversalRule;
+interface nsIAccessiblePivotObserver;
+
+/**
+ * The pivot interface encapsulates a reference to a single place in an accessible
+ * subtree. The pivot is a point or a range in the accessible tree. This interface
+ * provides traversal methods to move the pivot to next/prev state that complies 
+ * to a given rule.
+ */
+[scriptable, uuid(689058ae-e301-444f-acb0-b5c2b189f350)]
+interface nsIAccessiblePivot : nsISupports
+{
+  const TextBoundaryType CHAR_BOUNDARY = 0;
+  const TextBoundaryType WORD_BOUNDARY = 1;
+  const TextBoundaryType LINE_BOUNDARY = 2;
+  const TextBoundaryType ATTRIBUTE_RANGE_BOUNDARY = 3;
+
+  /**
+   * The accessible the pivot is currently pointed at.
+   */
+  attribute nsIAccessible position;
+
+  /**
+   * The root of the subtree in which the pivot traverses.
+   */
+  readonly attribute nsIAccessible root;
+
+  /**
+   * The start offset of the text range the pivot points at, otherwise -1.
+   */
+  readonly attribute long startOffset;
+
+  /**
+   * The end offset of the text range the pivot points at, otherwise -1.
+   */
+  readonly attribute long endOffset;
+
+  /**
+   * Set the pivot's text range in a text accessible.
+   *
+   * @param aTextAccessible [in] the text accessible that contains the desired
+   *                        range.
+   * @param aStartOffset    [in] the start offset to set.
+   * @param aEndOffset      [in] the end offset to set.
+   * @throws NS_ERROR_INVALID_ARG when the offset exceeds the accessible's
+   *   character count.
+   */
+  void setTextRange(in nsIAccessibleText aTextAccessible,
+                    in long aStartOffset, in long aEndOffset);
+
+  /**
+   * Move pivot to next object complying to given traversal rule.
+   *
+   * @param aRule [in] traversal rule to use.
+   * @return true on success, false if there are no new nodes to traverse to.
+   */
+  boolean moveNext(in nsIAccessibleTraversalRule aRule);
+
+  /**
+   * Move pivot to previous object complying to given traversal rule.
+   *
+   * @param aRule [in] traversal rule to use.
+   * @return true on success, false if there are no new nodes to traverse to.
+   */
+  boolean movePrevious(in nsIAccessibleTraversalRule aRule);
+
+  /**
+   * Move pivot to first object in subtree complying to given traversal rule.
+   *
+   * @param aRule [in] traversal rule to use.
+   * @return true on success, false if there are no new nodes to traverse to.
+   */
+  boolean moveFirst(in nsIAccessibleTraversalRule aRule);
+
+  /**
+   * Move pivot to last object in subtree complying to given traversal rule.
+   *
+   * @param aRule [in] traversal rule to use.
+   * @return true on success, false if there are no new nodes to traverse to.
+   */
+  boolean moveLast(in nsIAccessibleTraversalRule aRule);
+
+  /**
+   * Move pivot to next text range.
+   *
+   * @param aBoundary [in] type of boundary for next text range, character, word,
+   *                  etc.
+   * @return true on success, false if there are is no more text.
+   */
+  boolean moveNextByText(in TextBoundaryType aBoundary);
+  
+  /**
+   * Move pivot to previous text range.
+   *
+   * @param aBoundary [in] type of boundary for previous text range, character,
+   *                  word, etc.
+   * @return true on success, false if there are is no more text.
+   */
+  boolean movePreviousByText(in TextBoundaryType aBoundary);
+
+  /**
+   * Add an observer for pivot changes.
+   *
+   * @param aObserver [in] the observer object to be notified of pivot changes.
+   */
+  void addObserver(in nsIAccessiblePivotObserver aObserver);
+
+  /**
+   * Remove an observer for pivot changes.
+   *
+   * @param aObserver [in] the observer object to remove from being notified.
+   */
+  void removeObserver(in nsIAccessiblePivotObserver aObserver);
+};
+
+/**
+ * An observer interface for pivot changes.
+ */
+[scriptable, uuid(b6508c5e-c081-467d-835c-613eedf9ee9b)]
+interface nsIAccessiblePivotObserver : nsISupports
+{
+  /**
+   * Called when the pivot changes.
+   *
+   * @param aPivot         [in] the pivot that has changed.
+   * @param aOldAccessible [in] the old pivot position before the change, or null.
+   * @param aOldStart      [in] the old start offset, or -1.
+   * @param aOldEnd        [in] the old end offset, or -1.
+   */
+  void onPivotChanged(in nsIAccessiblePivot aPivot,
+                      in nsIAccessible aOldAccessible,
+                      in long aOldStart, in long aOldEnd);
+};
+
+[scriptable, uuid(307d98b6-dba9-49cf-ba17-ef8b053044eb)]
+interface nsIAccessibleTraversalRule : nsISupports
+{
+  /* Ignore this accessible object */
+  const unsigned short FILTER_IGNORE = 0x0;
+  /* Accept this accessible object */
+  const unsigned short FILTER_MATCH = 0x1;
+  /* Don't traverse accessibles children */
+  const unsigned short FILTER_IGNORE_SUBTREE = 0x2;
+
+  /* Pre-filters */
+  const unsigned long PREFILTER_INVISIBLE     = 0x00000001;
+  const unsigned long PREFILTER_OFFSCREEN     = 0x00000002;
+  const unsigned long PREFILTER_NOT_FOCUSABLE = 0x00000004;
+
+  /**
+   * Pre-filter bitfield to filter out obviously ignorable nodes and lighten
+   * the load on match().
+   */
+  readonly attribute unsigned long preFilter;
+
+  /**
+   * Retrieve a list of roles that the traversal rule should test for. Any node
+   * with a role not in this list will automatically be ignored. An empty list
+   * will match all roles. It should be assumed that this method is called once
+   * at the start of a traversal, so changing the method's return result after
+   * that would have no affect.
+   *
+   * @param aRoles [out] an array of the roles to match.
+   * @param aCount [out] the length of the array.
+   */
+  void getMatchRoles([array, size_is(aCount)]out unsigned long aRoles,
+                     [retval]out unsigned long aCount);
+
+  /**
+   * Determines if a given accessible is to be accepted in our traversal rule
+   *
+   * @param aAccessible [in] accessible to examine.
+   * @return a bitfield of FILTER_MATCH and FILTER_IGNORE_SUBTREE.
+   */
+  unsigned short match(in nsIAccessible aAccessible);
+};
--- a/accessible/public/nsIAccessibleRetrieval.idl
+++ b/accessible/public/nsIAccessibleRetrieval.idl
@@ -40,17 +40,17 @@
 
 interface nsIDOMNode;
 interface nsIAccessible;
 interface nsIWeakReference;
 interface nsIPresShell;
 interface nsIDOMWindow;
 interface nsIAccessNode;
 interface nsIDOMDOMStringList;
-
+interface nsIAccessiblePivot;
 
 /**
  * An interface for in-process accessibility clients
  * wishing to get an nsIAccessible or nsIAccessNode for
  * a given DOM node.
  * More documentation at:
  *   http://www.mozilla.org/projects/ui/accessibility
  */
@@ -107,16 +107,24 @@ interface nsIAccessibleRetrieval : nsISu
    * Return an accessible for the given DOM node from the cache.
    * @note  the method is intended for testing purposes
    *
    * @param aNode  [in] the DOM node to get an accessible for
    *
    * @return       cached accessible for the given DOM node if any
    */
   nsIAccessible getAccessibleFromCache(in nsIDOMNode aNode);
+
+  /**
+   * Create a new pivot for tracking a position and traversing a subtree.
+   *
+   * @param aRoot [in] the accessible root for the pivot
+   * @return a new pivot
+   */
+  nsIAccessiblePivot createAccessiblePivot(in nsIAccessible aRoot);
 };
 
 
 %{ C++
 
 // for component registration
 // {663CA4A8-D219-4000-925D-D8F66406B626}
 #define NS_ACCESSIBLE_RETRIEVAL_CID \
--- a/accessible/src/base/Makefile.in
+++ b/accessible/src/base/Makefile.in
@@ -60,16 +60,17 @@ CPPSRCS = \
   nsARIAGridAccessible.cpp \
   nsARIAMap.cpp \
   nsDocAccessible.cpp \
   nsOuterDocAccessible.cpp \
   nsCoreUtils.cpp \
   nsAccUtils.cpp \
   nsAccessibilityService.cpp \
   nsAccessible.cpp \
+  nsAccessiblePivot.cpp \
   nsAccTreeWalker.cpp \
   nsBaseWidgetAccessible.cpp \
   nsEventShell.cpp \
   nsFormControlAccessible.cpp \
   nsRootAccessible.cpp \
   nsApplicationAccessible.cpp \
   nsCaretAccessible.cpp \
   nsTextAccessible.cpp \
--- a/accessible/src/base/Statistics.h
+++ b/accessible/src/base/Statistics.h
@@ -51,17 +51,23 @@ namespace statistics {
 
   inline void A11yConsumers(PRUint32 aConsumer)
     { Telemetry::Accumulate(Telemetry::A11Y_CONSUMERS, aConsumer); }
 
   /**
    * Report that ISimpleDOM* has been used.
    */
   inline void ISimpleDOMUsed()
-    { Telemetry::Accumulate(Telemetry::ISIMPLE_DOM_USAGE, 1); }
+  {
+    static bool firstTime = true;
+    if (firstTime) {
+      Telemetry::Accumulate(Telemetry::ISIMPLE_DOM_USAGE, 1);
+      firstTime = false;
+    }
+  }
 
   /**
    * Report that IAccessibleTable has been used.
    */
   inline void IAccessibleTableUsed()
     { Telemetry::Accumulate(Telemetry::IACCESSIBLE_TABLE_USAGE, 1); }
 
 } // namespace statistics
--- a/accessible/src/base/nsARIAMap.cpp
+++ b/accessible/src/base/nsARIAMap.cpp
@@ -100,17 +100,17 @@ nsRoleMapEntry nsARIAMap::gWAIRoleMap[] 
     eNoLiveAttr,
     states::READONLY
   },
   {
     "button",
     roles::PUSHBUTTON,
     kUseMapRole,
     eNoValue,
-    eClickAction,
+    ePressAction,
     eNoLiveAttr,
     kNoReqStates,
     eARIAPressed
   },
   {
     "checkbox",
     roles::CHECKBUTTON,
     kUseMapRole,
@@ -215,25 +215,16 @@ nsRoleMapEntry nsARIAMap::gWAIRoleMap[] 
     roles::GRAPHIC,
     kUseMapRole,
     eNoValue,
     eNoAction,
     eNoLiveAttr,
     kNoReqStates
   },
   {
-    "label",
-    roles::LABEL,
-    kUseMapRole,
-    eNoValue,
-    eNoAction,
-    eNoLiveAttr,
-    kNoReqStates
-  },
-  {
     "link",
     roles::LINK,
     kUseMapRole,
     eNoValue,
     eJumpAction,
     eNoLiveAttr,
     states::LINKED
   },
--- a/accessible/src/base/nsARIAMap.h
+++ b/accessible/src/base/nsARIAMap.h
@@ -73,16 +73,17 @@ enum EValueRule
 /**
  * Used to define if the role requires to expose action.
  */
 enum EActionRule
 {
   eNoAction,
   eActivateAction,
   eClickAction,
+  ePressAction,
   eCheckUncheckAction,
   eExpandAction,
   eJumpAction,
   eOpenCloseAction,
   eSelectAction,
   eSortAction,
   eSwitchAction
 };
--- a/accessible/src/base/nsAccessibilityService.cpp
+++ b/accessible/src/base/nsAccessibilityService.cpp
@@ -35,16 +35,17 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "mozilla/Util.h"
 
 // NOTE: alphabetically ordered
 #include "nsAccessibilityService.h"
+#include "nsAccessiblePivot.h"
 #include "nsCoreUtils.h"
 #include "nsAccUtils.h"
 #include "nsApplicationAccessibleWrap.h"
 #include "nsARIAGridAccessibleWrap.h"
 #include "nsARIAMap.h"
 #include "FocusManager.h"
 
 #include "nsIContentViewer.h"
@@ -859,16 +860,33 @@ nsAccessibilityService::GetAccessibleFro
     if (document)
       accessible = GetDocAccessibleFromCache(document);
   }
 
   NS_IF_ADDREF(*aAccessible = accessible);
   return NS_OK;
 }
 
+NS_IMETHODIMP
+nsAccessibilityService::CreateAccessiblePivot(nsIAccessible* aRoot,
+                                              nsIAccessiblePivot** aPivot)
+{
+  NS_ENSURE_ARG_POINTER(aPivot);
+  NS_ENSURE_ARG(aRoot);
+  *aPivot = nsnull;
+
+  nsRefPtr<nsAccessible> accessibleRoot(do_QueryObject(aRoot));
+  NS_ENSURE_TRUE(accessibleRoot, NS_ERROR_INVALID_ARG);
+
+  nsAccessiblePivot* pivot = new nsAccessiblePivot(accessibleRoot);
+  NS_ADDREF(*aPivot = pivot);
+
+  return NS_OK;
+}
+
 // nsIAccesibilityService
 nsAccessible*
 nsAccessibilityService::GetAccessibleInShell(nsINode* aNode,
                                              nsIPresShell* aPresShell)
 {
   if (!aNode || !aPresShell)
     return nsnull;
 
--- a/accessible/src/base/nsAccessibilityService.h
+++ b/accessible/src/base/nsAccessibilityService.h
@@ -529,16 +529,17 @@ static const char kEventTypeNames[][40] 
   "hyperlink number of anchors changed",     // EVENT_HYPERLINK_NUMBER_OF_ANCHORS_CHANGED
   "hyperlink selected link changed",         // EVENT_HYPERLINK_SELECTED_LINK_CHANGED
   "hypertext link activated",                // EVENT_HYPERTEXT_LINK_ACTIVATED
   "hypertext link selected",                 // EVENT_HYPERTEXT_LINK_SELECTED
   "hyperlink start index changed",           // EVENT_HYPERLINK_START_INDEX_CHANGED
   "hypertext changed",                       // EVENT_HYPERTEXT_CHANGED
   "hypertext links count changed",           // EVENT_HYPERTEXT_NLINKS_CHANGED
   "object attribute changed",                // EVENT_OBJECT_ATTRIBUTE_CHANGED
+  "virtual cursor changed"                   // EVENT_VIRTUALCURSOR_CHANGED
 };
 
 /**
  * Map nsIAccessibleRelation constants to strings. Used by
  * nsIAccessibleRetrieval::getStringRelationType() method.
  */
 static const char kRelationTypeNames[][20] = {
   "unknown",             // RELATION_NUL
--- a/accessible/src/base/nsAccessible.cpp
+++ b/accessible/src/base/nsAccessible.cpp
@@ -588,40 +588,23 @@ nsAccessible::TranslateString(const nsAS
   aStringOut.Assign(xsValue);
 }
 
 PRUint64
 nsAccessible::VisibilityState()
 {
   PRUint64 vstates = states::INVISIBLE | states::OFFSCREEN;
 
-  // We need to check the parent chain for visibility.
-  nsAccessible* accessible = this;
-  do {
-    // We don't want background tab page content to be aggressively invisible.
-    // Otherwise this foils screen reader virtual buffer caches.
-    roles::Role role = accessible->Role();
-    if (role == roles::PROPERTYPAGE || role == roles::PANE)
-      break;
-
-    nsIFrame* frame = accessible->GetFrame();
-    if (!frame)
-      return vstates;
-
-    const nsIView* view = frame->GetView();
-    if (view && view->GetVisibility() == nsViewVisibility_kHide)
-      return vstates;
-    
-  } while (accessible = accessible->Parent());
-
   nsIFrame* frame = GetFrame();
   if (!frame)
     return vstates;
 
   const nsCOMPtr<nsIPresShell> shell(GetPresShell());
+  if (!shell)
+    return vstates;
 
   // We need to know if at least a kMinPixels around the object is visible,
   // otherwise it will be marked states::OFFSCREEN.
   const PRUint16 kMinPixels  = 12;
   const nsSize frameSize = frame->GetSize();
   const nsRectVisibility rectVisibility =
     shell->GetRectVisibility(frame, nsRect(nsPoint(0,0), frameSize),
                              nsPresContext::CSSPixelsToAppUnits(kMinPixels));
@@ -639,16 +622,20 @@ nsAccessible::VisibilityState()
       frame->GetRect().IsEmpty()) {
     nsAutoString renderedText;
     frame->GetRenderedText(&renderedText, nsnull, nsnull, 0, 1);
     if (renderedText.IsEmpty())
       return vstates;
 
   }
 
+  // XXX Do we really need to cross from content to chrome ancestor?
+  if (!frame->IsVisibleConsideringAncestors(nsIFrame::VISIBILITY_CROSS_CHROME_CONTENT_BOUNDARY))
+    return vstates;
+
   // Assume we are visible enough.
   return vstates &= ~states::INVISIBLE;
 }
 
 PRUint64
 nsAccessible::NativeState()
 {
   PRUint64 state = 0;
@@ -1382,16 +1369,40 @@ nsAccessible::GetAttributesInternal(nsIP
     nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::textAlign, value);
 
   // Expose 'text-indent' attribute.
   rv = GetComputedStyleValue(EmptyString(), NS_LITERAL_STRING("text-indent"),
                              value);
   if (NS_SUCCEEDED(rv))
     nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::textIndent, value);
 
+  // Expose 'margin-left' attribute.
+  rv = GetComputedStyleValue(EmptyString(), NS_LITERAL_STRING("margin-left"),
+                             value);
+  if (NS_SUCCEEDED(rv))
+    nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::marginLeft, value);
+
+  // Expose 'margin-right' attribute.
+  rv = GetComputedStyleValue(EmptyString(), NS_LITERAL_STRING("margin-right"),
+                             value);
+  if (NS_SUCCEEDED(rv))
+    nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::marginRight, value);
+
+  // Expose 'margin-top' attribute.
+  rv = GetComputedStyleValue(EmptyString(), NS_LITERAL_STRING("margin-top"),
+                             value);
+  if (NS_SUCCEEDED(rv))
+    nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::marginTop, value);
+
+  // Expose 'margin-bottom' attribute.
+  rv = GetComputedStyleValue(EmptyString(), NS_LITERAL_STRING("margin-bottom"),
+                             value);
+  if (NS_SUCCEEDED(rv))
+    nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::marginBottom, value);
+
   // Expose draggable object attribute?
   nsCOMPtr<nsIDOMHTMLElement> htmlElement = do_QueryInterface(mContent);
   if (htmlElement) {
     bool draggable = false;
     htmlElement->GetDraggable(&draggable);
     if (draggable) {
       nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::draggable,
                              NS_LITERAL_STRING("true"));
@@ -1836,16 +1847,20 @@ nsAccessible::GetActionName(PRUint8 aInd
    case eActivateAction:
      aName.AssignLiteral("activate");
      return NS_OK;
 
    case eClickAction:
      aName.AssignLiteral("click");
      return NS_OK;
 
+   case ePressAction:
+     aName.AssignLiteral("press");
+     return NS_OK;
+
    case eCheckUncheckAction:
      if (states & states::CHECKED)
        aName.AssignLiteral("uncheck");
      else if (states & states::MIXED)
        aName.AssignLiteral("cycle");
      else
        aName.AssignLiteral("check");
      return NS_OK;
new file mode 100644
--- /dev/null
+++ b/accessible/src/base/nsAccessiblePivot.cpp
@@ -0,0 +1,526 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* ***** 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.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Eitan Isaacson <eitan@monotonous.org> (original author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of 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 ***** */
+
+#include "nsAccessiblePivot.h"
+
+#include "nsAccessible.h"
+#include "nsAccUtils.h"
+#include "nsHyperTextAccessible.h"
+#include "States.h"
+
+#include "nsArrayUtils.h"
+#include "nsComponentManagerUtils.h"
+#include "nsISupportsPrimitives.h"
+
+using namespace mozilla::a11y;
+
+
+/**
+ * An object that stores a given traversal rule during 
+ */
+class RuleCache
+{
+public:
+  RuleCache(nsIAccessibleTraversalRule* aRule) : mRule(aRule),
+                                                 mAcceptRoles(nsnull) { }
+  ~RuleCache () {
+    if (mAcceptRoles)
+      nsMemory::Free(mAcceptRoles);
+  }
+
+  nsresult ApplyFilter(nsAccessible* aAccessible, PRUint16* aResult);
+
+private:
+  nsCOMPtr<nsIAccessibleTraversalRule> mRule;
+  PRUint32* mAcceptRoles;
+  PRUint32 mAcceptRolesLength;
+  PRUint32 mPreFilter;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// nsAccessiblePivot
+
+nsAccessiblePivot::nsAccessiblePivot(nsAccessible* aRoot) :
+  mRoot(aRoot), mPosition(nsnull),
+  mStartOffset(-1), mEndOffset(-1)
+{
+  NS_ASSERTION(aRoot, "A root accessible is required");
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// nsISupports
+
+NS_IMPL_CYCLE_COLLECTION_CLASS(nsAccessiblePivot)
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsAccessiblePivot)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR_AMBIGUOUS(mRoot, nsIAccessible)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR_AMBIGUOUS(mPosition, nsIAccessible)
+  PRUint32 i, length = tmp->mObservers.Length();                               \
+  for (i = 0; i < length; ++i) {
+    cb.NoteXPCOMChild(tmp->mObservers.ElementAt(i).get());
+  }
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsAccessiblePivot)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mRoot)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mPosition)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSTARRAY(mObservers)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsAccessiblePivot)
+  NS_INTERFACE_MAP_ENTRY(nsIAccessiblePivot)
+  NS_INTERFACE_MAP_ENTRY(nsAccessiblePivot)
+  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIAccessiblePivot)
+NS_INTERFACE_MAP_END
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(nsAccessiblePivot)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(nsAccessiblePivot)
+
+////////////////////////////////////////////////////////////////////////////////
+// nsIAccessiblePivot
+
+NS_IMETHODIMP
+nsAccessiblePivot::GetRoot(nsIAccessible** aRoot)
+{
+  NS_ENSURE_ARG_POINTER(aRoot);
+
+  NS_IF_ADDREF(*aRoot = mRoot);
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsAccessiblePivot::GetPosition(nsIAccessible** aPosition)
+{
+  NS_ENSURE_ARG_POINTER(aPosition);
+
+  NS_IF_ADDREF(*aPosition = mPosition);
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsAccessiblePivot::SetPosition(nsIAccessible* aPosition)
+{
+  nsRefPtr<nsAccessible> secondPosition;
+
+  if (aPosition) {
+    secondPosition = do_QueryObject(aPosition);
+    if (!secondPosition || !IsRootDescendant(secondPosition))
+      return NS_ERROR_INVALID_ARG;
+  }
+
+  // Swap old position with new position, saves us an AddRef/Release.
+  mPosition.swap(secondPosition);
+  PRInt32 oldStart = mStartOffset, oldEnd = mEndOffset;
+  mStartOffset = mEndOffset = -1;
+  NotifyPivotChanged(secondPosition, oldStart, oldEnd);
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsAccessiblePivot::GetStartOffset(PRInt32* aStartOffset)
+{
+  NS_ENSURE_ARG_POINTER(aStartOffset);
+
+  *aStartOffset = mStartOffset;
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsAccessiblePivot::GetEndOffset(PRInt32* aEndOffset)
+{
+  NS_ENSURE_ARG_POINTER(aEndOffset);
+
+  *aEndOffset = mEndOffset;
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsAccessiblePivot::SetTextRange(nsIAccessibleText* aTextAccessible,
+                                PRInt32 aStartOffset, PRInt32 aEndOffset)
+{
+  NS_ENSURE_ARG(aTextAccessible);
+
+  // Check that start offset is smaller than end offset, and that if a value is
+  // smaller than 0, both should be -1.
+  NS_ENSURE_TRUE(aStartOffset <= aEndOffset &&
+                 (aStartOffset >= 0 || (aStartOffset != -1 && aEndOffset != -1)),
+                 NS_ERROR_INVALID_ARG);
+
+  nsRefPtr<nsHyperTextAccessible> newPosition = do_QueryObject(aTextAccessible);
+  if (!newPosition || !IsRootDescendant(newPosition))
+    return NS_ERROR_INVALID_ARG;
+
+  // Make sure the given offsets don't exceed the character count.
+  PRInt32 charCount = newPosition->CharacterCount();
+
+  if (aEndOffset > charCount)
+    return NS_ERROR_FAILURE;
+
+  PRInt32 oldStart = mStartOffset, oldEnd = mEndOffset;
+  mStartOffset = aStartOffset;
+  mEndOffset = aEndOffset;
+
+  nsRefPtr<nsAccessible> oldPosition = mPosition.forget();
+  mPosition = newPosition.forget();
+
+  NotifyPivotChanged(oldPosition, oldStart, oldEnd);
+
+  return NS_OK;
+}
+
+// Traversal functions
+
+NS_IMETHODIMP
+nsAccessiblePivot::MoveNext(nsIAccessibleTraversalRule* aRule, bool* aResult)
+{
+  NS_ENSURE_ARG(aResult);
+  NS_ENSURE_ARG(aRule);
+
+  nsresult rv = NS_OK;
+  nsAccessible* accessible = SearchForward(mPosition, aRule, false, &rv);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  *aResult = accessible;
+  if (*aResult)
+    MovePivotInternal(accessible);
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsAccessiblePivot::MovePrevious(nsIAccessibleTraversalRule* aRule, bool* aResult)
+{
+  NS_ENSURE_ARG(aResult);
+  NS_ENSURE_ARG(aRule);
+
+  nsresult rv = NS_OK;
+  nsAccessible* accessible = SearchBackward(mPosition, aRule, false, &rv);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  *aResult = accessible;
+  if (*aResult)
+    MovePivotInternal(accessible);
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsAccessiblePivot::MoveFirst(nsIAccessibleTraversalRule* aRule, bool* aResult)
+{
+  NS_ENSURE_ARG(aResult);
+  NS_ENSURE_ARG(aRule);
+  nsresult rv = NS_OK;
+  nsAccessible* accessible = SearchForward(mRoot, aRule, true, &rv);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  *aResult = accessible;
+  if (*aResult)
+    MovePivotInternal(accessible);
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsAccessiblePivot::MoveLast(nsIAccessibleTraversalRule* aRule, bool* aResult)
+{
+  NS_ENSURE_ARG(aResult);
+  NS_ENSURE_ARG(aRule);
+
+  *aResult = false;
+  nsresult rv = NS_OK;
+  nsAccessible* lastAccessible = mRoot;
+  nsAccessible* accessible = nsnull;
+
+  // First got to the last accessible in pre-order
+  while (lastAccessible->HasChildren())
+    lastAccessible = lastAccessible->LastChild();
+
+  // Search backwards from last accessible and find the last occurrence in the doc
+  accessible = SearchBackward(lastAccessible, aRule, true, &rv);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  *aResult = accessible;
+  if (*aResult)
+    MovePivotInternal(accessible);
+
+  return NS_OK;
+}
+
+// TODO: Implement
+NS_IMETHODIMP
+nsAccessiblePivot::MoveNextByText(TextBoundaryType aBoundary, bool* aResult)
+{
+  NS_ENSURE_ARG(aResult);
+
+  *aResult = false;
+
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// TODO: Implement
+NS_IMETHODIMP
+nsAccessiblePivot::MovePreviousByText(TextBoundaryType aBoundary, bool* aResult)
+{
+  NS_ENSURE_ARG(aResult);
+
+  *aResult = false;
+
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// Observer functions
+
+NS_IMETHODIMP
+nsAccessiblePivot::AddObserver(nsIAccessiblePivotObserver* aObserver)
+{
+  NS_ENSURE_ARG(aObserver);
+
+  mObservers.AppendElement(aObserver);
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsAccessiblePivot::RemoveObserver(nsIAccessiblePivotObserver* aObserver)
+{
+  NS_ENSURE_ARG(aObserver);
+
+  return mObservers.RemoveElement(aObserver) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+// Private utility methods
+
+bool
+nsAccessiblePivot::IsRootDescendant(nsAccessible* aAccessible)
+{
+  nsAccessible* accessible = aAccessible;
+  do {
+    if (accessible == mRoot)
+      return true;
+  } while ((accessible = accessible->Parent()));
+
+  return false;
+}
+
+void
+nsAccessiblePivot::MovePivotInternal(nsAccessible* aPosition)
+{
+  nsRefPtr<nsAccessible> oldPosition = mPosition.forget();
+  mPosition = aPosition;
+  PRInt32 oldStart = mStartOffset, oldEnd = mEndOffset;
+  mStartOffset = mEndOffset = -1;
+
+  NotifyPivotChanged(oldPosition, oldStart, oldEnd);
+}
+
+nsAccessible*
+nsAccessiblePivot::SearchBackward(nsAccessible* aAccessible,
+                                  nsIAccessibleTraversalRule* aRule,
+                                  bool searchCurrent,
+                                  nsresult* rv)
+{
+  *rv = NS_OK;
+
+  // Initial position could be unset, in that case return null.
+  if (!aAccessible)
+    return nsnull;
+
+  RuleCache cache(aRule);
+  nsAccessible* accessible = aAccessible;
+
+  PRUint16 filtered = nsIAccessibleTraversalRule::FILTER_IGNORE;
+
+  if (searchCurrent) {
+    *rv = cache.ApplyFilter(accessible, &filtered);
+    NS_ENSURE_SUCCESS(*rv, nsnull);
+    if (filtered & nsIAccessibleTraversalRule::FILTER_MATCH)
+      return accessible;
+  }
+
+  while (accessible != mRoot) {
+    nsAccessible* parent = accessible->Parent();
+    PRInt32 idxInParent = accessible->IndexInParent();
+    while (idxInParent > 0) {
+      if (!(accessible = parent->GetChildAt(--idxInParent)))
+        continue;
+
+      *rv = cache.ApplyFilter(accessible, &filtered);
+      NS_ENSURE_SUCCESS(*rv, nsnull);
+
+      nsAccessible* lastChild;
+      while (!(filtered & nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE) &&
+             (lastChild = accessible->LastChild())) {
+        parent = accessible;
+        accessible = lastChild;
+        *rv = cache.ApplyFilter(accessible, &filtered);
+        NS_ENSURE_SUCCESS(*rv, nsnull);
+      }
+
+      if (filtered & nsIAccessibleTraversalRule::FILTER_MATCH)
+        return accessible;
+    }
+
+    if (!(accessible = parent))
+      break;
+
+    *rv = cache.ApplyFilter(accessible, &filtered);
+    NS_ENSURE_SUCCESS(*rv, nsnull);
+
+    if (filtered & nsIAccessibleTraversalRule::FILTER_MATCH)
+      return accessible;
+  }
+
+  return nsnull;
+}
+
+nsAccessible*
+nsAccessiblePivot::SearchForward(nsAccessible* aAccessible,
+                                 nsIAccessibleTraversalRule* aRule,
+                                 bool searchCurrent,
+                                 nsresult* rv)
+{
+  *rv = NS_OK;
+
+  // Initial position could be not set, in that case begin search from root.
+  nsAccessible *accessible = (!aAccessible) ? mRoot.get() : aAccessible;
+
+  RuleCache cache(aRule);
+
+  PRUint16 filtered = nsIAccessibleTraversalRule::FILTER_IGNORE;
+  *rv = cache.ApplyFilter(accessible, &filtered);
+  NS_ENSURE_SUCCESS(*rv, nsnull);
+  if (searchCurrent && (filtered & nsIAccessibleTraversalRule::FILTER_MATCH))
+    return accessible;
+
+  while (true) {
+    nsAccessible* firstChild = nsnull;
+    while (!(filtered & nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE) &&
+           (firstChild = accessible->FirstChild())) {
+      accessible = firstChild;
+      *rv = cache.ApplyFilter(accessible, &filtered);
+      NS_ENSURE_SUCCESS(*rv, nsnull);
+
+      if (filtered & nsIAccessibleTraversalRule::FILTER_MATCH)
+        return accessible;
+    }
+
+    nsAccessible* sibling = nsnull;
+    nsAccessible* temp = accessible;
+    do {
+      if (temp == mRoot)
+        break;
+
+      sibling = temp->NextSibling();
+
+      if (sibling)
+        break;
+    } while ((temp = temp->Parent()));
+
+    if (!sibling)
+      break;
+
+    accessible = sibling;
+    *rv = cache.ApplyFilter(accessible, &filtered);
+    NS_ENSURE_SUCCESS(*rv, nsnull);
+
+    if (filtered & nsIAccessibleTraversalRule::FILTER_MATCH)
+      return accessible;
+  }
+
+  return nsnull;
+}
+
+void
+nsAccessiblePivot::NotifyPivotChanged(nsAccessible* aOldPosition,
+                                      PRInt32 aOldStart, PRInt32 aOldEnd)
+{
+  nsTObserverArray<nsCOMPtr<nsIAccessiblePivotObserver> >::ForwardIterator iter(mObservers);
+  while (iter.HasMore()) {
+    nsIAccessiblePivotObserver* obs = iter.GetNext();
+    obs->OnPivotChanged(this, aOldPosition, aOldStart, aOldEnd);
+  }
+}
+
+nsresult
+RuleCache::ApplyFilter(nsAccessible* aAccessible, PRUint16* aResult)
+{
+  *aResult = nsIAccessibleTraversalRule::FILTER_IGNORE;
+
+  if (!mAcceptRoles) {
+    nsresult rv = mRule->GetMatchRoles(&mAcceptRoles, &mAcceptRolesLength);
+    NS_ENSURE_SUCCESS(rv, rv);
+    rv = mRule->GetPreFilter(&mPreFilter);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+  if (mPreFilter) {
+    PRUint64 state = aAccessible->State();
+
+    if ((nsIAccessibleTraversalRule::PREFILTER_INVISIBLE & mPreFilter) &&
+        (state & states::INVISIBLE))
+      return NS_OK;
+
+    if ((nsIAccessibleTraversalRule::PREFILTER_OFFSCREEN & mPreFilter) &&
+        (state & states::OFFSCREEN))
+      return NS_OK;
+
+    if ((nsIAccessibleTraversalRule::PREFILTER_NOT_FOCUSABLE & mPreFilter) &&
+        !(state & states::FOCUSABLE))
+      return NS_OK;
+  }
+
+  if (mAcceptRolesLength > 0) {
+    PRUint32 accessibleRole = aAccessible->Role();
+    bool matchesRole = false;
+    for (PRUint32 idx = 0; idx < mAcceptRolesLength; idx++) {
+      matchesRole = mAcceptRoles[idx] == accessibleRole;
+      if (matchesRole)
+        break;
+    }
+    if (!matchesRole)
+      return NS_OK;
+  }
+
+  return mRule->Match(aAccessible, aResult);
+}
new file mode 100644
--- /dev/null
+++ b/accessible/src/base/nsAccessiblePivot.h
@@ -0,0 +1,134 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* ***** 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.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Eitan Isaacson <eitan@monotonous.org> (original author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of 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 ***** */
+
+#ifndef _nsAccessiblePivot_H_
+#define _nsAccessiblePivot_H_
+
+#include "nsIAccessiblePivot.h"
+
+#include "nsAutoPtr.h"
+#include "nsTObserverArray.h"
+#include "nsCycleCollectionParticipant.h"
+
+class nsAccessible;
+class nsIAccessibleTraversalRule;
+
+/**
+ * Class represents an accessible pivot.
+ */
+class nsAccessiblePivot: public nsIAccessiblePivot
+{
+public:
+  nsAccessiblePivot(nsAccessible* aRoot);
+
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsAccessiblePivot, nsIAccessiblePivot)
+
+  NS_DECL_NSIACCESSIBLEPIVOT
+
+  /*
+   * A simple getter for the pivot's position.
+   */
+  nsAccessible* Position() { return mPosition; }
+
+private:
+  nsAccessiblePivot() MOZ_DELETE;
+  nsAccessiblePivot(const nsAccessiblePivot&) MOZ_DELETE;
+  void operator = (const nsAccessiblePivot&) MOZ_DELETE;
+
+  /*
+   * Notify all observers on a pivot change.
+   */
+  void NotifyPivotChanged(nsAccessible* aOldAccessible,
+                          PRInt32 aOldStart, PRInt32 aOldEnd);
+
+  /*
+   * Check to see that the given accessible is in the pivot's subtree.
+   */
+  bool IsRootDescendant(nsAccessible* aAccessible);
+
+
+  /*
+   * Search in preorder for the first accessible to match the rule.
+   */
+  nsAccessible* SearchForward(nsAccessible* aAccessible,
+                              nsIAccessibleTraversalRule* aRule,
+                              bool searchCurrent,
+                              nsresult* rv);
+
+  /*
+   * Reverse search in preorder for the first accessible to match the rule.
+   */
+  nsAccessible* SearchBackward(nsAccessible* aAccessible,
+                               nsIAccessibleTraversalRule* aRule,
+                               bool searchCurrent,
+                               nsresult* rv);
+
+  /*
+   * Update the pivot, and notify observers.
+   */
+  void MovePivotInternal(nsAccessible* aPosition);
+
+  /*
+   * The root accessible.
+   */
+  nsRefPtr<nsAccessible> mRoot;
+
+  /*
+   * The current pivot position.
+   */
+  nsRefPtr<nsAccessible> mPosition;
+
+  /*
+   * The text start offset ofthe pivot.
+   */
+  PRInt32 mStartOffset;
+
+  /*
+   * The text end offset ofthe pivot.
+   */
+  PRInt32 mEndOffset;
+
+  /*
+   * The list of pivot-changed observers.
+   */
+  nsTObserverArray<nsCOMPtr<nsIAccessiblePivotObserver> > mObservers;
+};
+
+#endif
--- a/accessible/src/base/nsDocAccessible.cpp
+++ b/accessible/src/base/nsDocAccessible.cpp
@@ -34,16 +34,17 @@
  * 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 ***** */
 
 #include "AccIterator.h"
 #include "nsAccCache.h"
 #include "nsAccessibilityService.h"
+#include "nsAccessiblePivot.h"
 #include "nsAccTreeWalker.h"
 #include "nsAccUtils.h"
 #include "nsRootAccessible.h"
 #include "nsTextEquivUtils.h"
 #include "Role.h"
 #include "States.h"
 
 #include "nsIMutableArray.h"
@@ -100,17 +101,18 @@ static const PRUint32 kRelationAttrsLen 
 ////////////////////////////////////////////////////////////////////////////////
 // Constructor/desctructor
 
 nsDocAccessible::
   nsDocAccessible(nsIDocument *aDocument, nsIContent *aRootContent,
                   nsIWeakReference *aShell) :
   nsHyperTextAccessibleWrap(aRootContent, aShell),
   mDocument(aDocument), mScrollPositionChangedTicks(0),
-  mLoadState(eTreeConstructionPending), mLoadEventType(0)
+  mLoadState(eTreeConstructionPending), mLoadEventType(0),
+  mVirtualCursor(nsnull)
 {
   mFlags |= eDocAccessible;
 
   mDependentIDsHash.Init();
   // XXX aaronl should we use an algorithm for the initial cache size?
   mAccessibleCache.Init(kDefaultCacheSize);
   mNodeToAccessibleMap.Init(kDefaultCacheSize);
 
@@ -120,16 +122,20 @@ nsDocAccessible::
 
   // For GTK+ native window, we do nothing here.
   if (!mDocument)
     return;
 
   // nsAccDocManager creates document accessible when scrollable frame is
   // available already, it should be safe time to add scroll listener.
   AddScrollListener();
+
+  // We provide a virtual cursor if this is a root doc or if it's a tab doc.
+  mIsCursorable = (!(mDocument->GetParentDocument()) ||
+                   nsCoreUtils::IsTabDocument(mDocument));
 }
 
 nsDocAccessible::~nsDocAccessible()
 {
 }
 
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -137,42 +143,51 @@ nsDocAccessible::~nsDocAccessible()
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsDocAccessible)
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsDocAccessible, nsAccessible)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mDocument)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NATIVE_MEMBER(mNotificationController,
                                                   NotificationController)
 
+  if (tmp->mVirtualCursor) {
+    NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NATIVE_MEMBER(mVirtualCursor,
+                                                    nsAccessiblePivot)
+  }
+
   PRUint32 i, length = tmp->mChildDocuments.Length();
   for (i = 0; i < length; ++i) {
     NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR_AMBIGUOUS(mChildDocuments[i],
                                                          nsIAccessible)
   }
 
   CycleCollectorTraverseCache(tmp->mAccessibleCache, &cb);
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsDocAccessible, nsAccessible)
   NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mDocument)
   NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mNotificationController)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mVirtualCursor)
   NS_IMPL_CYCLE_COLLECTION_UNLINK_NSTARRAY(mChildDocuments)
   tmp->mDependentIDsHash.Clear();
   tmp->mNodeToAccessibleMap.Clear();
   ClearCache(tmp->mAccessibleCache);
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsDocAccessible)
   NS_INTERFACE_MAP_STATIC_AMBIGUOUS(nsDocAccessible)
   NS_INTERFACE_MAP_ENTRY(nsIAccessibleDocument)
   NS_INTERFACE_MAP_ENTRY(nsIDocumentObserver)
   NS_INTERFACE_MAP_ENTRY(nsIMutationObserver)
   NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
   NS_INTERFACE_MAP_ENTRY(nsIObserver)
+  NS_INTERFACE_MAP_ENTRY(nsIAccessiblePivotObserver)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIAccessibleDocument)
+  NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIAccessibleCursorable,
+                                     mIsCursorable)
     foundInterface = 0;
 
   nsresult status;
   if (!foundInterface) {
     // HTML document accessible must inherit from nsHyperTextAccessible to get
     // support text interfaces. XUL document accessible doesn't need this.
     // However at some point we may push <body> to implement the interfaces and
     // return nsDocAccessible to inherit from nsAccessibleWrap.
@@ -511,16 +526,37 @@ nsDocAccessible::GetChildDocumentAt(PRUi
 
   if (IsDefunct())
     return NS_OK;
 
   NS_IF_ADDREF(*aDocument = GetChildDocumentAt(aIndex));
   return *aDocument ? NS_OK : NS_ERROR_INVALID_ARG;
 }
 
+// nsIAccessibleVirtualCursor method
+NS_IMETHODIMP
+nsDocAccessible::GetVirtualCursor(nsIAccessiblePivot** aVirtualCursor)
+{
+  NS_ENSURE_ARG_POINTER(aVirtualCursor);
+  *aVirtualCursor = nsnull;
+
+  if (IsDefunct())
+    return NS_ERROR_FAILURE;
+
+  NS_ENSURE_TRUE(mIsCursorable, NS_ERROR_NOT_IMPLEMENTED);
+
+  if (!mVirtualCursor) {
+    mVirtualCursor = new nsAccessiblePivot(this);
+    mVirtualCursor->AddObserver(this);
+  }
+
+  NS_ADDREF(*aVirtualCursor = mVirtualCursor);
+  return NS_OK;
+}
+
 // nsIAccessibleHyperText method
 NS_IMETHODIMP nsDocAccessible::GetAssociatedEditor(nsIEditor **aEditor)
 {
   NS_ENSURE_ARG_POINTER(aEditor);
   *aEditor = nsnull;
 
   if (IsDefunct())
     return NS_ERROR_FAILURE;
@@ -632,16 +668,21 @@ nsDocAccessible::Shutdown()
   // Walk the array backwards because child documents remove themselves from the
   // array as they are shutdown.
   PRInt32 childDocCount = mChildDocuments.Length();
   for (PRInt32 idx = childDocCount - 1; idx >= 0; idx--)
     mChildDocuments[idx]->Shutdown();
 
   mChildDocuments.Clear();
 
+  if (mVirtualCursor) {
+    mVirtualCursor->RemoveObserver(this);
+    mVirtualCursor = nsnull;
+  }
+
   mWeakShell = nsnull;  // Avoid reentrancy
 
   mDependentIDsHash.Clear();
   mNodeToAccessibleMap.Clear();
   ClearCache(mAccessibleCache);
 
   nsHyperTextAccessibleWrap::Shutdown();
 
@@ -884,16 +925,30 @@ NS_IMETHODIMP nsDocAccessible::Observe(n
       new AccStateChangeEvent(this, states::EDITABLE, true);
     FireDelayedAccessibleEvent(event);
   }
 
   return NS_OK;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
+// nsIAccessiblePivotObserver
+
+NS_IMETHODIMP
+nsDocAccessible::OnPivotChanged(nsIAccessiblePivot* aPivot,
+                                nsIAccessible* aOldAccessible,
+                                PRInt32 aOldStart, PRInt32 aOldEnd)
+{
+  nsRefPtr<AccEvent> event = new AccEvent(nsIAccessibleEvent::EVENT_VIRTUALCURSOR_CHANGED, this);
+  nsEventShell::FireEvent(event);
+
+  return NS_OK;
+}
+
+////////////////////////////////////////////////////////////////////////////////
 // nsIDocumentObserver
 
 NS_IMPL_NSIDOCUMENTOBSERVER_CORE_STUB(nsDocAccessible)
 NS_IMPL_NSIDOCUMENTOBSERVER_LOAD_STUB(nsDocAccessible)
 NS_IMPL_NSIDOCUMENTOBSERVER_STYLE_STUB(nsDocAccessible)
 
 void
 nsDocAccessible::AttributeWillChange(nsIDocument *aDocument,
--- a/accessible/src/base/nsDocAccessible.h
+++ b/accessible/src/base/nsDocAccessible.h
@@ -34,17 +34,19 @@
  * 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 ***** */
 
 #ifndef _nsDocAccessible_H_
 #define _nsDocAccessible_H_
 
+#include "nsIAccessibleCursorable.h"
 #include "nsIAccessibleDocument.h"
+#include "nsIAccessiblePivot.h"
 
 #include "nsEventShell.h"
 #include "nsHyperTextAccessibleWrap.h"
 #include "NotificationController.h"
 
 #include "nsClassHashtable.h"
 #include "nsDataHashtable.h"
 #include "nsIDocument.h"
@@ -53,42 +55,49 @@
 #include "nsIObserver.h"
 #include "nsIScrollPositionListener.h"
 #include "nsITimer.h"
 #include "nsIWeakReference.h"
 #include "nsCOMArray.h"
 #include "nsIDocShellTreeNode.h"
 
 class nsIScrollableView;
+class nsAccessiblePivot;
 
 const PRUint32 kDefaultCacheSize = 256;
 
 #define NS_DOCACCESSIBLE_IMPL_CID                       \
 {  /* 5641921c-a093-4292-9dca-0b51813db57d */           \
   0x5641921c,                                           \
   0xa093,                                               \
   0x4292,                                               \
   { 0x9d, 0xca, 0x0b, 0x51, 0x81, 0x3d, 0xb5, 0x7d }    \
 }
 
 class nsDocAccessible : public nsHyperTextAccessibleWrap,
                         public nsIAccessibleDocument,
                         public nsIDocumentObserver,
                         public nsIObserver,
                         public nsIScrollPositionListener,
-                        public nsSupportsWeakReference
-{  
+                        public nsSupportsWeakReference,
+                        public nsIAccessibleCursorable,
+                        public nsIAccessiblePivotObserver
+{
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(nsDocAccessible, nsAccessible)
 
   NS_DECL_NSIACCESSIBLEDOCUMENT
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_DOCACCESSIBLE_IMPL_CID)
 
   NS_DECL_NSIOBSERVER
 
+  NS_DECL_NSIACCESSIBLECURSORABLE
+
+  NS_DECL_NSIACCESSIBLEPIVOTOBSERVER
+
 public:
   using nsAccessible::GetParent;
 
   nsDocAccessible(nsIDocument *aDocument, nsIContent *aRootContent,
                   nsIWeakReference* aShell);
   virtual ~nsDocAccessible();
 
   // nsIAccessible
@@ -591,16 +600,26 @@ protected:
    * Keep the ARIA attribute old value that is initialized by
    * AttributeWillChange and used by AttributeChanged notifications.
    */
   nsIAtom* mARIAAttrOldValue;
 
   nsTArray<nsRefPtr<nsDocAccessible> > mChildDocuments;
 
   /**
+   * Whether we support nsIAccessibleCursorable, used when querying the interface.
+   */
+  bool mIsCursorable;
+
+  /**
+   * The virtual cursor of the document when it supports nsIAccessibleCursorable.
+   */
+  nsRefPtr<nsAccessiblePivot> mVirtualCursor;
+
+  /**
    * A storage class for pairing content with one of its relation attributes.
    */
   class AttrRelProvider
   {
   public:
     AttrRelProvider(nsIAtom* aRelAttr, nsIContent* aContent) :
       mRelAttr(aRelAttr), mContent(aContent) { }
 
--- a/accessible/src/base/nsRootAccessible.cpp
+++ b/accessible/src/base/nsRootAccessible.cpp
@@ -569,84 +569,42 @@ nsRootAccessible::Shutdown()
 {
   // Called manually or by nsAccessNode::LastRelease()
   if (!mWeakShell)
     return;  // Already shutdown
 
   nsDocAccessibleWrap::Shutdown();
 }
 
-// nsRootAccessible protected member
-already_AddRefed<nsIDocShellTreeItem>
-nsRootAccessible::GetContentDocShell(nsIDocShellTreeItem *aStart)
-{
-  if (!aStart) {
-    return nsnull;
-  }
-
-  PRInt32 itemType;
-  aStart->GetItemType(&itemType);
-  if (itemType == nsIDocShellTreeItem::typeContent) {
-    nsDocAccessible *accDoc = nsAccUtils::GetDocAccessibleFor(aStart);
-
-    // Hidden documents don't have accessibles (like SeaMonkey's sidebar),
-    // they are of no interest for a11y.
-    if (!accDoc)
-      return nsnull;
-
-    // If ancestor chain of accessibles is not completely visible,
-    // don't use this one. This happens for example if it's inside
-    // a background tab (tabbed browsing)
-    nsAccessible* parent = accDoc->Parent();
-    while (parent) {
-      if (parent->State() & states::INVISIBLE)
-        return nsnull;
-
-      if (parent == this)
-        break; // Don't check past original root accessible we started with
-
-      parent = parent->Parent();
-    }
-
-    NS_ADDREF(aStart);
-    return aStart;
-  }
-  nsCOMPtr<nsIDocShellTreeNode> treeNode(do_QueryInterface(aStart));
-  if (treeNode) {
-    PRInt32 subDocuments;
-    treeNode->GetChildCount(&subDocuments);
-    for (PRInt32 count = 0; count < subDocuments; count ++) {
-      nsCOMPtr<nsIDocShellTreeItem> treeItemChild, contentTreeItem;
-      treeNode->GetChildAt(count, getter_AddRefs(treeItemChild));
-      NS_ENSURE_TRUE(treeItemChild, nsnull);
-      contentTreeItem = GetContentDocShell(treeItemChild);
-      if (contentTreeItem) {
-        NS_ADDREF(aStart = contentTreeItem);
-        return aStart;
-      }
-    }
-  }
-  return nsnull;
-}
-
 // nsIAccessible method
 Relation
 nsRootAccessible::RelationByType(PRUint32 aType)
 {
   if (!mDocument || aType != nsIAccessibleRelation::RELATION_EMBEDS)
     return nsDocAccessibleWrap::RelationByType(aType);
 
-  nsCOMPtr<nsIDocShellTreeItem> treeItem =
-    nsCoreUtils::GetDocShellTreeItemFor(mDocument);
-  nsCOMPtr<nsIDocShellTreeItem> contentTreeItem = GetContentDocShell(treeItem);
-  // there may be no content area, so we need a null check
-  if (!contentTreeItem)
-    return Relation();
+  nsIDOMWindow* rootWindow = mDocument->GetWindow();
+  if (rootWindow) {
+    nsCOMPtr<nsIDOMWindow> contentWindow;
+    rootWindow->GetContent(getter_AddRefs(contentWindow));
+    if (contentWindow) {
+      nsCOMPtr<nsIDOMDocument> contentDOMDocument;
+      contentWindow->GetDocument(getter_AddRefs(contentDOMDocument));
+      nsCOMPtr<nsIDocument> contentDocumentNode =
+        do_QueryInterface(contentDOMDocument);
+      if (contentDocumentNode) {
+        nsDocAccessible* contentDocument =
+          GetAccService()->GetDocAccessible(contentDocumentNode);
+        if (contentDocument)
+          return Relation(contentDocument);
+      }
+    }
+  }
 
-  return Relation(nsAccUtils::GetDocAccessibleFor(contentTreeItem));
+  return Relation();
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // Protected members
 
 void
 nsRootAccessible::HandlePopupShownEvent(nsAccessible* aAccessible)
 {
--- a/accessible/src/base/nsRootAccessible.h
+++ b/accessible/src/base/nsRootAccessible.h
@@ -122,18 +122,17 @@ protected:
 #ifdef MOZ_XUL
     void HandleTreeRowCountChangedEvent(nsIDOMEvent* aEvent,
                                         nsXULTreeAccessible* aAccessible);
     void HandleTreeInvalidatedEvent(nsIDOMEvent* aEvent,
                                     nsXULTreeAccessible* aAccessible);
 
     PRUint32 GetChromeFlags();
 #endif
-    already_AddRefed<nsIDocShellTreeItem>
-           GetContentDocShell(nsIDocShellTreeItem *aStart);
+
     nsRefPtr<nsCaretAccessible> mCaretAccessible;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsRootAccessible, NS_ROOTACCESSIBLE_IMPL_CID)
 
 inline nsRootAccessible*
 nsAccessible::AsRoot()
 {
--- a/accessible/src/msaa/nsAccessNodeWrap.cpp
+++ b/accessible/src/msaa/nsAccessNodeWrap.cpp
@@ -58,24 +58,16 @@
 #include "nsPIDOMWindow.h"
 #include "nsIServiceManager.h"
 
 #include "mozilla/Preferences.h"
 
 using namespace mozilla;
 using namespace mozilla::a11y;
 
-/// the accessible library and cached methods
-HINSTANCE nsAccessNodeWrap::gmAccLib = nsnull;
-HINSTANCE nsAccessNodeWrap::gmUserLib = nsnull;
-LPFNACCESSIBLEOBJECTFROMWINDOW nsAccessNodeWrap::gmAccessibleObjectFromWindow = nsnull;
-LPFNLRESULTFROMOBJECT nsAccessNodeWrap::gmLresultFromObject = NULL;
-LPFNNOTIFYWINEVENT nsAccessNodeWrap::gmNotifyWinEvent = nsnull;
-LPFNGETGUITHREADINFO nsAccessNodeWrap::gmGetGUIThreadInfo = nsnull;
-
 AccTextChangeEvent* nsAccessNodeWrap::gTextEvent = nsnull;
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsAccessNodeWrap
 ////////////////////////////////////////////////////////////////////////////////
 
 nsAccessNodeWrap::
   nsAccessNodeWrap(nsIContent *aContent, nsIWeakReference *aShell) :
@@ -586,27 +578,16 @@ nsAccessNodeWrap::get_localInterface(
   *localInterface = static_cast<nsIAccessNode*>(this);
   NS_ADDREF_THIS();
 } __except(FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
   return S_OK;
 }
  
 void nsAccessNodeWrap::InitAccessibility()
 {
-  if (!gmUserLib) {
-    gmUserLib =::LoadLibraryW(L"USER32.DLL");
-  }
-
-  if (gmUserLib) {
-    if (!gmNotifyWinEvent)
-      gmNotifyWinEvent = (LPFNNOTIFYWINEVENT)GetProcAddress(gmUserLib,"NotifyWinEvent");
-    if (!gmGetGUIThreadInfo)
-      gmGetGUIThreadInfo = (LPFNGETGUITHREADINFO)GetProcAddress(gmUserLib,"GetGUIThreadInfo");
-  }
-
   Compatibility::Init();
 
   nsWinUtils::MaybeStartWindowEmulation();
 
   nsAccessNode::InitXPAccessibility();
 }
 
 void nsAccessNodeWrap::ShutdownAccessibility()
@@ -673,18 +654,18 @@ nsAccessNodeWrap::WindowProc(HWND hWnd, 
     case WM_GETOBJECT:
     {
       if (lParam == OBJID_CLIENT) {
         nsDocAccessible* document = sHWNDCache.GetWeak(static_cast<void*>(hWnd));
         if (document) {
           IAccessible* msaaAccessible = NULL;
           document->GetNativeInterface((void**)&msaaAccessible); // does an addref
           if (msaaAccessible) {
-            LRESULT result = LresultFromObject(IID_IAccessible, wParam,
-                                               msaaAccessible); // does an addref
+            LRESULT result = ::LresultFromObject(IID_IAccessible, wParam,
+                                                 msaaAccessible); // does an addref
             msaaAccessible->Release(); // release extra addref
             return result;
           }
         }
       }
       return 0;
     }
     case WM_NCHITTEST:
@@ -693,26 +674,8 @@ nsAccessNodeWrap::WindowProc(HWND hWnd, 
       if (HTCLIENT == lRet)
         lRet = HTTRANSPARENT;
       return lRet;
     }
   }
 
   return ::DefWindowProcW(hWnd, msg, wParam, lParam);
 }
-
-STDMETHODIMP_(LRESULT)
-nsAccessNodeWrap::LresultFromObject(REFIID riid, WPARAM wParam, LPUNKNOWN pAcc)
-{
-  // open the dll dynamically
-  if (!gmAccLib)
-    gmAccLib =::LoadLibraryW(L"OLEACC.DLL");
-
-  if (gmAccLib) {
-    if (!gmLresultFromObject)
-      gmLresultFromObject = (LPFNLRESULTFROMOBJECT)GetProcAddress(gmAccLib,"LresultFromObject");
-
-    if (gmLresultFromObject)
-      return gmLresultFromObject(riid, wParam, pAcc);
-  }
-
-  return 0;
-}
--- a/accessible/src/msaa/nsAccessNodeWrap.h
+++ b/accessible/src/msaa/nsAccessNodeWrap.h
@@ -62,18 +62,16 @@
 #include "OLEACC.H"
 #include <winuser.h>
 #ifdef MOZ_CRASHREPORTER
 #include "nsICrashReporter.h"
 #endif
 
 #include "nsRefPtrHashtable.h"
 
-typedef LRESULT (STDAPICALLTYPE *LPFNNOTIFYWINEVENT)(DWORD event,HWND hwnd,LONG idObjectType,LONG idObject);
-typedef LRESULT (STDAPICALLTYPE *LPFNGETGUITHREADINFO)(DWORD idThread, GUITHREADINFO* pgui);
 
 class AccTextChangeEvent;
 
 class nsAccessNodeWrap :  public nsAccessNode,
                           public nsIWinAccessNode,
                           public ISimpleDOMNode,
                           public IServiceProvider
 {
@@ -144,28 +142,18 @@ public: // construction, destruction
         /* [retval][out] */ void __RPC_FAR *__RPC_FAR *localInterface);
         
     virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_language(
         /* [out][retval] */ BSTR __RPC_FAR *language);
 
     static void InitAccessibility();
     static void ShutdownAccessibility();
 
-    /// the accessible library and cached methods
-    static HINSTANCE gmAccLib;
-    static HINSTANCE gmUserLib;
-    static LPFNACCESSIBLEOBJECTFROMWINDOW gmAccessibleObjectFromWindow;
-    static LPFNLRESULTFROMOBJECT gmLresultFromObject;
-    static LPFNNOTIFYWINEVENT gmNotifyWinEvent;
-    static LPFNGETGUITHREADINFO gmGetGUIThreadInfo;
-
     static int FilterA11yExceptions(unsigned int aCode, EXCEPTION_POINTERS *aExceptionInfo);
 
-  static STDMETHODIMP_(LRESULT) LresultFromObject(REFIID riid, WPARAM wParam, LPUNKNOWN pAcc);
-
   static LRESULT CALLBACK WindowProc(HWND hWnd, UINT Msg,
                                      WPARAM WParam, LPARAM lParam);
 
   static nsRefPtrHashtable<nsVoidPtrHashKey, nsDocAccessible> sHWNDCache;
 
 protected:
 
   /**
--- a/accessible/src/msaa/nsAccessibleWrap.cpp
+++ b/accessible/src/msaa/nsAccessibleWrap.cpp
@@ -158,67 +158,35 @@ STDMETHODIMP nsAccessibleWrap::QueryInte
 } __except(FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
   return S_OK;
 }
 
 //-----------------------------------------------------
 // IAccessible methods
 //-----------------------------------------------------
 
-
-STDMETHODIMP nsAccessibleWrap::AccessibleObjectFromWindow(HWND hwnd,
-                                                          DWORD dwObjectID,
-                                                          REFIID riid,
-                                                          void **ppvObject)
-{
-  // open the dll dynamically
-  if (!gmAccLib)
-    gmAccLib =::LoadLibraryW(L"OLEACC.DLL");
-
-  if (gmAccLib) {
-    if (!gmAccessibleObjectFromWindow)
-      gmAccessibleObjectFromWindow = (LPFNACCESSIBLEOBJECTFROMWINDOW)GetProcAddress(gmAccLib,"AccessibleObjectFromWindow");
-
-    if (gmAccessibleObjectFromWindow)
-      return gmAccessibleObjectFromWindow(hwnd, dwObjectID, riid, ppvObject);
-  }
-
-  return E_FAIL;
-}
-
-STDMETHODIMP nsAccessibleWrap::NotifyWinEvent(DWORD event,
-                                              HWND hwnd,
-                                              LONG idObjectType,
-                                              LONG idObject)
-{
-  if (gmNotifyWinEvent)
-    return gmNotifyWinEvent(event, hwnd, idObjectType, idObject);
-
-  return E_FAIL;
-}
-
 STDMETHODIMP nsAccessibleWrap::get_accParent( IDispatch __RPC_FAR *__RPC_FAR *ppdispParent)
 {
 __try {
   *ppdispParent = NULL;
 
   if (IsDefunct())
     return E_FAIL;
 
   nsRefPtr<nsDocAccessible> doc(do_QueryObject(this));
   if (doc) {
     // Return window system accessible object for root document and tab document
     // accessibles.
     if (!doc->ParentDocument() ||
         nsWinUtils::IsWindowEmulationStarted() &&
         nsCoreUtils::IsTabDocument(doc->GetDocumentNode())) {
       HWND hwnd = static_cast<HWND>(doc->GetNativeWindow());
-      if (hwnd && SUCCEEDED(AccessibleObjectFromWindow(hwnd, OBJID_WINDOW,
-                                                       IID_IAccessible,
-                                                       (void**)ppdispParent))) {
+      if (hwnd && SUCCEEDED(::AccessibleObjectFromWindow(hwnd, OBJID_WINDOW,
+                                                         IID_IAccessible,
+                                                         (void**)ppdispParent))) {
         return S_OK;
       }
     }
   }
 
   nsAccessible* xpParentAcc = Parent();
   if (!xpParentAcc) {
     if (IsApplication())
@@ -1585,23 +1553,23 @@ nsAccessibleWrap::FirePlatformEvent(AccE
 
 #ifdef DEBUG_A11Y
   printf("\n\nMSAA event: event: %d, target: %s@id='%s', childid: %d, hwnd: %d\n\n",
          eventType, NS_ConvertUTF16toUTF8(tag).get(), id.get(),
          childID, hWnd);
 #endif
 
   // Fire MSAA event for client area window.
-  NotifyWinEvent(winEvent, hWnd, OBJID_CLIENT, childID);
+  ::NotifyWinEvent(winEvent, hWnd, OBJID_CLIENT, childID);
 
   // JAWS announces collapsed combobox navigation based on focus events.
   if (Compatibility::IsJAWS()) {
     if (eventType == nsIAccessibleEvent::EVENT_SELECTION &&
       accessible->Role() == roles::COMBOBOX_OPTION) {
-      NotifyWinEvent(EVENT_OBJECT_FOCUS, hWnd, OBJID_CLIENT, childID);
+      ::NotifyWinEvent(EVENT_OBJECT_FOCUS, hWnd, OBJID_CLIENT, childID);
     }
   }
 
   return NS_OK;
 }
 
 //------- Helper methods ---------
 
@@ -1724,18 +1692,19 @@ IDispatch *nsAccessibleWrap::NativeAcces
   }
 
   nsCOMPtr<nsIAccessibleWin32Object> accObject(do_QueryInterface(aXPAccessible));
   if (accObject) {
     void* hwnd = nsnull;
     accObject->GetHwnd(&hwnd);
     if (hwnd) {
       IDispatch *retval = nsnull;
-      AccessibleObjectFromWindow(reinterpret_cast<HWND>(hwnd),
-        OBJID_WINDOW, IID_IAccessible, (void **) &retval);
+      ::AccessibleObjectFromWindow(reinterpret_cast<HWND>(hwnd),
+                                   OBJID_WINDOW, IID_IAccessible,
+                                   (void **) &retval);
       return retval;
     }
   }
 
   IAccessible *msaaAccessible;
   aXPAccessible->GetNativeInterface((void**)&msaaAccessible);
 
   return static_cast<IDispatch*>(msaaAccessible);
--- a/accessible/src/msaa/nsAccessibleWrap.h
+++ b/accessible/src/msaa/nsAccessibleWrap.h
@@ -326,21 +326,16 @@ public: // construction, destruction
 
   /**
    * Find an accessible by the given child ID in cached documents.
    */
   nsAccessible* GetXPAccessibleFor(const VARIANT& aVarChild);
 
   NS_IMETHOD GetNativeInterface(void **aOutAccessible);
 
-  // NT4 does not have the oleacc that defines these methods. So we define copies here that automatically
-  // load the library only if needed.
-  static STDMETHODIMP AccessibleObjectFromWindow(HWND hwnd,DWORD dwObjectID,REFIID riid,void **ppvObject);
-  static STDMETHODIMP NotifyWinEvent(DWORD event,HWND hwnd,LONG idObjectType,LONG idObject);
-
   static IDispatch *NativeAccessible(nsIAccessible *aXPAccessible);
 
   /**
    * Drops the IEnumVariant current position so that navigation methods
    * Next() and Skip() doesn't work until Reset() method is called. The method
    * is used when children of the accessible are changed.
    */
   void UnattachIEnumVariant();
--- a/accessible/src/msaa/nsEventMap.h
+++ b/accessible/src/msaa/nsEventMap.h
@@ -126,11 +126,12 @@ static const PRUint32 gWinEventMap[] = {
   IA2_EVENT_HYPERLINK_NUMBER_OF_ANCHORS_CHANGED,     // nsIAccessibleEvent::EVENT_HYPERLINK_NUMBER_OF_ANCHORS_CHANGED
   IA2_EVENT_HYPERLINK_SELECTED_LINK_CHANGED,         // nsIAccessibleEvent::EVENT_HYPERLINK_SELECTED_LINK_CHANGED
   IA2_EVENT_HYPERTEXT_LINK_ACTIVATED,                // nsIAccessibleEvent::EVENT_HYPERTEXT_LINK_ACTIVATED
   IA2_EVENT_HYPERTEXT_LINK_SELECTED,                 // nsIAccessibleEvent::EVENT_HYPERTEXT_LINK_SELECTED
   IA2_EVENT_HYPERLINK_START_INDEX_CHANGED,           // nsIAccessibleEvent::EVENT_HYPERLINK_START_INDEX_CHANGED
   IA2_EVENT_HYPERTEXT_CHANGED,                       // nsIAccessibleEvent::EVENT_HYPERTEXT_CHANGED
   IA2_EVENT_HYPERTEXT_NLINKS_CHANGED,                // nsIAccessibleEvent::EVENT_HYPERTEXT_NLINKS_CHANGED
   IA2_EVENT_OBJECT_ATTRIBUTE_CHANGED,                // nsIAccessibleEvent::EVENT_OBJECT_ATTRIBUTE_CHANGED
+  kEVENT_WIN_UNKNOWN,                                // nsIAccessibleEvent::EVENT_VIRTUALCURSOR_CHANGED
   kEVENT_LAST_ENTRY                                  // nsIAccessibleEvent::EVENT_LAST_ENTRY
 };
 
--- a/accessible/tests/mochitest/Makefile.in
+++ b/accessible/tests/mochitest/Makefile.in
@@ -47,16 +47,17 @@ DIRS	= \
   attributes \
   editabletext \
   elm \
   events \
   focus \
   hyperlink \
   hypertext \
   name \
+  pivot \
   relations \
   selectable \
   states \
   table \
   text \
   textselection \
   tree \
   treeupdate \
@@ -76,16 +77,17 @@ include $(topsrcdir)/config/rules.mk
 		attributes.js \
 		autocomplete.js \
 		browser.js \
 		common.js \
 		events.js \
 		grid.js \
 		layout.js \
 		name.js \
+		pivot.js \
 		relations.js \
 		role.js \
 		selectable.js \
 		states.js \
 		table.js \
 		value.js \
 		test_aria_role_article.html \
 		test_aria_role_equation.html \
--- a/accessible/tests/mochitest/actions/test_anchors.html
+++ b/accessible/tests/mochitest/actions/test_anchors.html
@@ -44,16 +44,25 @@
           ID: "anchor1",
           actionName: "jump",
           actionIndex: 0,
           events: CLICK_EVENTS,
           eventSeq: [
             new scrollingChecker(getAccessible("bottom1"))
           ]
         },
+        { // jump again (test for bug 437607)
+          ID: "anchor1",
+          actionName: "jump",
+          actionIndex: 0,
+          events: CLICK_EVENTS,
+          eventSeq: [
+            new scrollingChecker(getAccessible("bottom1"))
+          ]
+        },
         {
           ID: "anchor2",
           actionName: "jump",
           actionIndex: 0,
           events: CLICK_EVENTS,
           eventSeq: [
             new scrollingChecker(getAccessible("bottom2"))
           ]
--- a/accessible/tests/mochitest/actions/test_aria.html
+++ b/accessible/tests/mochitest/actions/test_aria.html
@@ -22,17 +22,17 @@
       var actionsArray = [
         {
           ID: "clickable",
           actionName: "click",
           events: CLICK_EVENTS
         },
         {
           ID: "button",
-          actionName: "click",
+          actionName: "press",
           events: CLICK_EVENTS
         },
         {
           ID: "checkbox_unchecked",
           actionName: "check",
           events: CLICK_EVENTS
         },
         {
--- a/accessible/tests/mochitest/attributes/test_obj_css.html
+++ b/accessible/tests/mochitest/attributes/test_obj_css.html
@@ -1,12 +1,13 @@
 <html>
 <!--
 https://bugzilla.mozilla.org/show_bug.cgi?id=439566
 https://bugzilla.mozilla.org/show_bug.cgi?id=460932
+https://bugzilla.mozilla.org/show_bug.cgi?id=689540
 -->
 <head>
   <title>CSS-like attributes tests</title>
   <link rel="stylesheet" type="text/css"
         href="chrome://mochikit/content/tests/SimpleTest/test.css" />
 
   <script type="application/javascript"
           src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
@@ -20,26 +21,38 @@ https://bugzilla.mozilla.org/show_bug.cg
     function testCSSAttrs(aID)
     {
       var node = document.getElementById(aID);
       var computedStyle = document.defaultView.getComputedStyle(node, "");
 
       var attrs = {
         "display": computedStyle.display,
         "text-align": computedStyle.textAlign,
-        "text-indent": computedStyle.textIndent
+        "text-indent": computedStyle.textIndent,
+        "margin-left": computedStyle.marginLeft,
+        "margin-right": computedStyle.marginRight,
+        "margin-top": computedStyle.marginTop,
+        "margin-bottom": computedStyle.marginBottom
       };
       testAttrs(aID, attrs, true);
     }
 
     function doTest()
     {
       testCSSAttrs("span");
       testCSSAttrs("div");
+
       testCSSAttrs("p");
+      testCSSAttrs("p2");
+
+      testCSSAttrs("pml");
+      testCSSAttrs("pmr");
+      testCSSAttrs("pmt");
+      testCSSAttrs("pmb");
+
       testCSSAttrs("input");
       testCSSAttrs("table");
       testCSSAttrs("tr");
       testCSSAttrs("td");
 
       SimpleTest.finish();
     }
 
@@ -54,25 +67,38 @@ https://bugzilla.mozilla.org/show_bug.cg
      title="Include the css display property as an IAccessible2 object attribute">
     Mozilla Bug 439566
   </a>
   <a target="_blank"
      href="https://bugzilla.mozilla.org/show_bug.cgi?id=460932"
      title="text-indent and text-align should really be object attribute">
     Mozilla Bug 460932
   </a>
+  <a target="_blank"
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=689540"
+     title="Expose IA2 margin- object attributes">
+    Mozilla Bug 689540
+  </a>
 
   <p id="display"></p>
   <div id="content" style="display: none"></div>
   <pre id="test">
   </pre>
 
   <span id="span" role="group">It's span</span>
   <div id="div">It's div</div>
+
   <p id="p">It's paragraph"</p>
+  <p id="p2"  style="text-indent: 5px">It's another paragraph</p>
+
+  <p id="pml" style="margin-left : 11px;">It's a paragraph with left margin</p>
+  <p id="pmr" style="margin-right : 21px;">It's a paragraph with right margin</p>
+  <p id="pmt" style="margin-top : 31px;">It's a paragraph with top margin</p>
+  <p id="pmb" style="margin-bottom : 41px;">It's a paragraph with bottom margin</p>
+
   <input id="input"/>
   <table id="table">
     <tr id="tr" role="group">
       <td id="td">td</td>
     </tr>
   </table>
 </body>
 </html>
--- a/accessible/tests/mochitest/browser.js
+++ b/accessible/tests/mochitest/browser.js
@@ -21,16 +21,24 @@ function closeBrowserWindow()
  * Return the browser window object.
  */
 function browserWindow()
 {
   return gBrowserContext.browserWnd;
 }
 
 /**
+ * Return the document of the browser window.
+ */
+function browserDocument()
+{
+  return browserWindow().document;
+}
+
+/**
  * Return tab browser object.
  */
 function tabBrowser()
 {
   return browserWindow().gBrowser;
 }
 
 /**
@@ -45,16 +53,32 @@ function currentBrowser()
  * Return DOM document of the current tab.
  */
 function currentTabDocument()
 {
   return currentBrowser().contentDocument;
 }
 
 /**
+ * Return browser element of the tab at the given index.
+ */
+function browserAt(aIndex)
+{
+  return tabBrowser().getBrowserAtIndex(aIndex);
+}
+
+/**
+ * Return DOM document of the tab at the given index.
+ */
+function tabDocumentAt(aIndex)
+{
+  return browserAt(aIndex).contentDocument;
+}
+
+/**
  * Return input element of address bar.
  */
 function urlbarInput()
 {
   return browserWindow().document.getElementById("urlbar").inputField;
 }
 
 /**
--- a/accessible/tests/mochitest/common.js
+++ b/accessible/tests/mochitest/common.js
@@ -25,20 +25,23 @@ const nsIAccessibleDocument = Components
 const nsIAccessibleApplication = Components.interfaces.nsIAccessibleApplication;
 
 const nsIAccessibleText = Components.interfaces.nsIAccessibleText;
 const nsIAccessibleEditableText = Components.interfaces.nsIAccessibleEditableText;
 
 const nsIAccessibleHyperLink = Components.interfaces.nsIAccessibleHyperLink;
 const nsIAccessibleHyperText = Components.interfaces.nsIAccessibleHyperText;
 
+const nsIAccessibleCursorable = Components.interfaces.nsIAccessibleCursorable;
 const nsIAccessibleImage = Components.interfaces.nsIAccessibleImage;
+const nsIAccessiblePivot = Components.interfaces.nsIAccessiblePivot;
 const nsIAccessibleSelectable = Components.interfaces.nsIAccessibleSelectable;
 const nsIAccessibleTable = Components.interfaces.nsIAccessibleTable;
 const nsIAccessibleTableCell = Components.interfaces.nsIAccessibleTableCell;
+const nsIAccessibleTraversalRule = Components.interfaces.nsIAccessibleTraversalRule;
 const nsIAccessibleValue = Components.interfaces.nsIAccessibleValue;
 
 const nsIObserverService = Components.interfaces.nsIObserverService;
 
 const nsIDOMDocument = Components.interfaces.nsIDOMDocument;
 const nsIDOMEvent = Components.interfaces.nsIDOMEvent;
 const nsIDOMHTMLDocument = Components.interfaces.nsIDOMHTMLDocument;
 const nsIDOMNode = Components.interfaces.nsIDOMNode;
--- a/accessible/tests/mochitest/events.js
+++ b/accessible/tests/mochitest/events.js
@@ -22,16 +22,17 @@ const EVENT_SELECTION_WITHIN = nsIAccess
 const EVENT_SHOW = nsIAccessibleEvent.EVENT_SHOW;
 const EVENT_STATE_CHANGE = nsIAccessibleEvent.EVENT_STATE_CHANGE;
 const EVENT_TEXT_ATTRIBUTE_CHANGED = nsIAccessibleEvent.EVENT_TEXT_ATTRIBUTE_CHANGED;
 const EVENT_TEXT_CARET_MOVED = nsIAccessibleEvent.EVENT_TEXT_CARET_MOVED;
 const EVENT_TEXT_INSERTED = nsIAccessibleEvent.EVENT_TEXT_INSERTED;
 const EVENT_TEXT_REMOVED = nsIAccessibleEvent.EVENT_TEXT_REMOVED;
 const EVENT_TEXT_SELECTION_CHANGED = nsIAccessibleEvent.EVENT_TEXT_SELECTION_CHANGED;
 const EVENT_VALUE_CHANGE = nsIAccessibleEvent.EVENT_VALUE_CHANGE;
+const EVENT_VIRTUALCURSOR_CHANGED = nsIAccessibleEvent.EVENT_VIRTUALCURSOR_CHANGED;
 
 ////////////////////////////////////////////////////////////////////////////////
 // General
 
 Components.utils.import("resource://gre/modules/Services.jsm");
 
 /**
  * Set up this variable to dump events into DOM.
--- a/accessible/tests/mochitest/events/Makefile.in
+++ b/accessible/tests/mochitest/events/Makefile.in
@@ -40,18 +40,16 @@ DEPTH		= ../../../..
 topsrcdir	= @top_srcdir@
 srcdir		= @srcdir@
 VPATH		= @srcdir@
 relativesrcdir  = accessible/events
 
 include $(DEPTH)/config/autoconf.mk
 include $(topsrcdir)/config/rules.mk
 
-# test_scroll.xul disabled for misusing <tabbrowser> (bug 715857)
-
 _TEST_FILES =\
 		docload_wnd.html \
 		focus.html \
 		scroll.html \
 		test_aria_alert.html \
 		test_aria_menu.html \
 		test_aria_objattr.html \
 		test_aria_statechange.html \
@@ -77,16 +75,17 @@ include $(topsrcdir)/config/rules.mk
 		test_focus_menu.xul \
 		test_focus_name.html \
 		test_focus_selects.html \
 		test_focus_tabbox.xul \
 		test_focus_tree.xul \
 		test_menu.xul \
 		test_mutation.html \
 		test_mutation.xhtml \
+		test_scroll.xul \
 		test_selection_aria.html \
 		test_selection.html \
 		test_selection.xul \
 		test_statechange.html \
 		test_text_alg.html \
 		test_text.html \
 		test_textattrchange.html \
 		test_tree.xul \
--- a/accessible/tests/mochitest/events/test_scroll.xul
+++ b/accessible/tests/mochitest/events/test_scroll.xul
@@ -1,125 +1,97 @@
 <?xml version="1.0"?>
 <?xml-stylesheet href="chrome://global/skin" type="text/css"?>
 
-<!-- Firefox tabbrowser -->
-<?xml-stylesheet href="chrome://browser/content/browser.css"
-                 type="text/css"?>
-<!-- SeaMonkey tabbrowser -->
-<?xml-stylesheet href="chrome://navigator/content/navigator.css"
-                 type="text/css"?>
-
 <?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
                  type="text/css"?>
 
 <window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
 
   <script type="application/javascript"
           src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
   <script type="application/javascript"
-          src="chrome://browser/content/utilityOverlay.js"/>
+          src="chrome://mochikit/content/chrome-harness.js"/>
 
   <script type="application/javascript"
           src="../common.js" />
   <script type="application/javascript"
           src="../role.js" />
   <script type="application/javascript"
           src="../states.js" />
   <script type="application/javascript"
           src="../events.js" />
-
   <script type="application/javascript"
-          src="chrome://mochikit/content/chrome-harness.js"/>
+          src="../browser.js"></script>
 
   <script type="application/javascript">
   <![CDATA[
 
     ////////////////////////////////////////////////////////////////////////////
-    // Hacks to make xul:tabbrowser work
-
-    const Ci = Components.interfaces;
-    const CC = Components.classes;
-
-    Components.utils.import("resource://gre/modules/Services.jsm");
-
-    var handleDroppedLink = null;
-
-    var XULBrowserWindow = {
-      isBusy: false,
-      setOverLink: function (link, b) {
-      }
-    };
-
-    var gURLBar = {
-      focused: false
-    };
-
-    var gFindBarInitialized = false;
-
-    function goSetCommandEnabled() {}
-
-    ////////////////////////////////////////////////////////////////////////////
     // Tests
 
-    function getTabDocument()
+    function getAnchorJumpInTabDocument(aTabIdx)
     {
-      return getNode("tabBrowser").selectedBrowser.contentDocument;
-    }
-
-    function getAnchorJumpInTabDocument()
-    {
-      return getTabDocument().querySelector("a[name='link1']");
+      var tabDoc = aTabIdx ? tabDocumentAt(aTabIdx) : currentTabDocument();
+      return tabDoc.querySelector("a[name='link1']");
     }
 
-    function loadTab(aTabBrowserID, aURL)
+    function loadTab(aURL)
     {
-      function loadTabChecker()
-      {
-        this.type = EVENT_REORDER;
-        this.match = function loadTabChecker_match(aEvent)
-        {
-          var target = aEvent.accessible;
-          if (target.role == ROLE_INTERNAL_FRAME &&
-              target.parent.parent == getAccessible(getNode(aTabBrowserID).mTabBox.tabpanels)) {
-            return true;
-          }
-          return false;
-        }
-      }
-
-      this.eventSeq = [ new loadTabChecker() ];
+      this.eventSeq = [
+        new invokerChecker(EVENT_DOCUMENT_LOAD_COMPLETE, currentTabDocument),
+        new invokerChecker(EVENT_SCROLLING_START, getAnchorJumpInTabDocument)
+      ];
 
       this.invoke = function loadTab_invoke()
       {
-        getNode(aTabBrowserID).loadURI(aURL);
+        tabBrowser().loadURI(aURL);
       }
 
       this.getID = function loadTab_getID()
       {
-        return "load tab " + aURL + " for " + prettyName(aTabBrowserID);
+        return "load tab: " + aURL;
       }
     }
 
-    function advanceFocusIntoTab(aTabBrowserID)
+    function loadTabInBackground(aURL)
     {
       this.eventSeq = [
-        new focusChecker(getTabDocument),
+        new invokerChecker(EVENT_DOCUMENT_LOAD_COMPLETE, tabDocumentAt, 1)
+      ];
+
+      this.unexpectedEventSeq = [
+        new invokerChecker(EVENT_SCROLLING_START, getAnchorJumpInTabDocument, 1)
+      ];
+
+      this.invoke = function loadTabInBackground_invoke()
+      {
+        tabBrowser().loadOneTab(aURL, null, "", null, true);
+      }
+
+      this.getID = function loadTabInBackground_getID()
+      {
+        return "load tab in background: " + aURL;
+      }
+    }
+
+    function switchToBackgroundTab()
+    {
+      this.eventSeq = [
         new invokerChecker(EVENT_SCROLLING_START, getAnchorJumpInTabDocument)
       ];
 
-      this.invoke = function advanceFocusIntoTab_invoke()
+      this.invoke = function switchToBackgroundTab_invoke()
       {
-        var tabDoc = getAccessible(getTabDocument());
-        tabDoc.takeFocus();
+        tabBrowser().selectTabAtIndex(1);
       }
 
-      this.getID = function advanceFocusIntoTab_getID()
+      this.getID = function switchToBackgroundTab_getID()
       {
-        return "advance focus into loaded tab";
+        return "switch to background tab";
       }
     }
 
     //gA11yEventDumpToConsole = true; // debug stuff
 
     var gQueue = null;
     function doTest()
     {
@@ -133,73 +105,40 @@
       var jar = getJar(rootDir);
       if (jar) {
         var tmpdir = extractJarToTmp(jar);
         rootDir = "file://" + tmpdir.path + '/';
       }
       var url = rootDir + "scroll.html#link1";
 
       gQueue = new eventQueue();
-      gQueue.push(new loadTab("tabBrowser", url));
-      gQueue.push(new advanceFocusIntoTab("tabBrowser"));
+
+      gQueue.push(new loadTab(url));
+      gQueue.push(new loadTabInBackground(url));
+      gQueue.push(new switchToBackgroundTab());
+      gQueue.onFinish = function() { closeBrowserWindow(); }
+
       gQueue.invoke(); // Will call SimpleTest.finish();
     }
 
     SimpleTest.waitForExplicitFinish();
-    addA11yLoadEvent(doTest);
+    openBrowserWindow(doTest);
   ]]>
   </script>
 
-  <hbox flex="1" style="overflow: auto;">
+  <vbox flex="1" style="overflow: auto;">
     <body xmlns="http://www.w3.org/1999/xhtml">
       <a target="_blank"
-         href="https://bugzilla.mozilla.org/show_bug.cgi?id=437607"
-         title="Clicking the 'Skip to main content' link once works, second time fails to initiate a V cursor jump">
-        Mozilla Bug 437607
-      </a><br/>
-      <a target="_blank"
-         href="https://bugzilla.mozilla.org/show_bug.cgi?id=519303"
-         title="Same page links to targets with content fires scrolling start accessible event on leaf text node">
-        Mozilla Bug 519303
-      </a>
-      <a target="_blank"
          href="https://bugzilla.mozilla.org/show_bug.cgi?id=691734"
          title="Make sure scrolling start event is fired when document receive focus">
         Mozilla Bug 691734
       </a>
 
       <p id="display"></p>
       <div id="content" style="display: none">
       </div>
       <pre id="test">
       </pre>
     </body>
 
-    <vbox flex="1">
-      <!-- Hack to make xul:tabbrowser work -->
-      <menubar>
-        <menu label="menu">
-          <menupopup>
-            <menuitem label="close window hook" id="menu_closeWindow"/>
-            <menuitem label="close hook" id="menu_close"/>
-          </menupopup>
-        </menu>
-      </menubar>
-      <keyset>
-        <key id="key_close"/>
-      </keyset>
-
-      <hbox>
-      <tabs id="tabbrowser-tabs" class="tabbrowser-tabs"
-            tabbrowser="tabBrowser"
-            flex="1">
-        <tab class="tabbrowser-tab" selected="true" label="tab"/>
-      </tabs>
-      </hbox>
-      <tabbrowser id="tabBrowser"
-                  type="content-primary"
-                  tabcontainer="tabbrowser-tabs"
-                  flex="1"/>
-    </vbox>
-    <toolbar id="addon-bar"/>
-  </hbox>
-
+    <vbox id="eventdump"></vbox>
+  </vbox>
 </window>
--- a/accessible/tests/mochitest/name/Makefile.in
+++ b/accessible/tests/mochitest/name/Makefile.in
@@ -40,26 +40,25 @@ DEPTH		= ../../../..
 topsrcdir	= @top_srcdir@
 srcdir		= @srcdir@
 VPATH		= @srcdir@
 relativesrcdir  = accessible/name
 
 include $(DEPTH)/config/autoconf.mk
 include $(topsrcdir)/config/rules.mk
 
-# test_nsRootAcc.xul, nsRootAcc_wnd.xul disabled for misusing <tabbrowser> (bug 715857)
-
 _TEST_FILES =\
 		general.css \
 		general.xbl \
 		markup.js \
 		test_button.html \
 		test_general.html \
 		test_general.xul \
 		test_link.html \
 		test_list.html \
 		test_markup.html \
+		test_browserui.xul \
 		test_tree.xul \
 		markuprules.xml \
 		$(NULL)
 
 libs:: $(_TEST_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/a11y/$(relativesrcdir)
deleted file mode 100644
--- a/accessible/tests/mochitest/name/nsRootAcc_wnd.xul
+++ /dev/null
@@ -1,128 +0,0 @@
-<?xml version="1.0"?>
-<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
-
-<!-- Firefox tabbrowser -->
-<?xml-stylesheet href="chrome://browser/content/browser.css"
-                 type="text/css"?>
-<!-- SeaMonkey tabbrowser -->
-<?xml-stylesheet href="chrome://navigator/content/navigator.css"
-                 type="text/css"?>
-
-<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
-
-  <script type="application/javascript"
-          src="chrome://browser/content/utilityOverlay.js"/>
-
-  <script type="application/javascript">
-  <![CDATA[
-    var gOpenerWnd = window.opener.wrappedJSObject;
-
-    function ok(aCond, aMsg) {
-      gOpenerWnd.SimpleTest.ok(aCond, aMsg);
-    }
-
-    function is(aExpected, aActual, aMsg) {
-      gOpenerWnd.SimpleTest.is(aExpected, aActual, aMsg);
-    }
-
-    // Hacks to make xul:tabbrowser work.
-    var handleDroppedLink = null; // needed for tabbrowser usage
-
-    Components.utils.import("resource://gre/modules/Services.jsm");
-    var XULBrowserWindow = {
-      isBusy: false,
-      setOverLink: function (link, b) {
-      }
-    };
-
-    gFindBarInitialized = false;
-
-    ////////////////////////////////////////////////////////////////////////////
-    // Invoker implementation.
-
-    function switchTabSelectChecker(aInvoker)
-    {
-      this.type = "select";
-      Object.defineProperty(this, "target", { get: function() { return aInvoker.getTabsElm(); }});
-      this.getID = function() { return "switch tab, select event"; }
-    }
-
-    function switchTabFocusChecker(aInvoker)
-    {
-      this.type = gOpenerWnd.EVENT_FOCUS;
-      Object.defineProperty(this, "target", { get: function() { return aInvoker.getContentDoc(); }});
-      this.check = function(aEvent)
-      {
-        is(gOpenerWnd.getAccessible(document).name, "about:mozilla" + aEvent.accessible.name,
-           "Oops almost :)");
-      }
-      this.getID = function() { return "switch tab, focus event"; }
-    }
-
-    function switchTabInvoker(aTabBrowser, aWindow)
-    {
-      this.invoke = function switchTabInvoker_invoke()
-      {
-        gOpenerWnd.synthesizeKey("VK_TAB", { ctrlKey: true }, aWindow);
-      }
-
-      this.eventSeq = [
-        new switchTabSelectChecker(this),
-        new switchTabFocusChecker(this)
-      ];
-
-      this.getContentDoc = function switchTabInvoker_getContentDoc()
-      {
-        return aTabBrowser.getBrowserAtIndex(1).contentDocument;
-      }
-      this.getTabsElm = function switchTabInvoker_getTabsElm()
-      {
-        return aTabBrowser.tabContainer;
-      }
-    }
-
-    ////////////////////////////////////////////////////////////////////////////
-    // Tests
-
-    var gQueue = null;
-
-    const Ci = Components.interfaces;
-
-    function doTest()
-    {
-      var tabBrowser = document.getElementById("content");
-      tabBrowser.loadURI("about:");
-      tabBrowser.addTab("about:mozilla");
-
-      gQueue = new gOpenerWnd.eventQueue();
-      gQueue.push(new switchTabInvoker(tabBrowser, window));
-      gQueue.onFinish = function() { window.close(); }
-      gQueue.invoke();
-    }
-
-    gOpenerWnd.addA11yLoadEvent(doTest);
-  ]]>
-  </script>
-
-  <!-- Hack to make xul:tabbrowser work -->
-  <menubar>
-    <menu label="menu">
-      <menupopup>
-        <menuitem label="close window hook" id="menu_closeWindow"/>
-        <menuitem label="close hook" id="menu_close"/>
-      </menupopup>
-    </menu>
-  </menubar>
-
-  <tabs id="tabbrowser-tabs" class="tabbrowser-tabs"
-        tabbrowser="content"
-        flex="1"
-        setfocus="false">
-    <tab class="tabbrowser-tab" selected="true"/>
-  </tabs>
-  <tabbrowser id="content"
-              type="content-primary"
-              tabcontainer="tabbrowser-tabs"
-              flex="1"/>
-
-</window>
rename from accessible/tests/mochitest/name/test_nsRootAcc.xul
rename to accessible/tests/mochitest/name/test_browserui.xul
--- a/accessible/tests/mochitest/name/test_nsRootAcc.xul
+++ b/accessible/tests/mochitest/name/test_browserui.xul
@@ -11,41 +11,84 @@
   <script type="application/javascript"
           src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
 
   <script type="application/javascript"
           src="../common.js"></script>
   <script type="application/javascript"
           src="../role.js"></script>
   <script type="application/javascript"
+          src="../states.js"></script>
+  <script type="application/javascript"
           src="../events.js"></script>
+  <script type="application/javascript"
+          src="../browser.js"></script>
 
   <script type="application/javascript">
   <![CDATA[
-    // var gA11yEventDumpID = "eventdump"; // debug stuff
-
-    function doTest()
+    function addTab(aURL)
     {
-      todo(false, "Disabled test. (Bug 586818)");
-      SimpleTest.finish();
-      return;
+      this.eventSeq = [
+        new invokerChecker(EVENT_DOCUMENT_LOAD_COMPLETE, tabDocumentAt, 1)
+      ];
+
+      this.invoke = function addTab_invoke()
+      {
+        tabBrowser().addTab(aURL);
+      }
 
-      if (LINUX) {
-        todo(false, "Skip test on Linux. (Bug 525175)");
-        SimpleTest.finish();
-        return;
+      this.getID = function addTab_getID()
+      {
+        return "add tab: " + aURL;
+      }
+    }
+
+    function switchTab(aTabBrowser, aWindow)
+    {
+      this.invoke = function switchTab_invoke()
+      {
+        synthesizeKey("VK_TAB", { ctrlKey: true }, browserWindow());
       }
 
-      var w = window.openDialog("nsRootAcc_wnd.xul",
-                                "nsRootAcc_name_test", 
-                                "chrome,width=600,height=600");
+      this.eventSeq = [
+        new focusChecker(tabDocumentAt, 1)
+      ];
+
+      this.check = function switchTab_check(aEvent)
+      {
+        var title = getAccessible(browserDocument()).name;
+        ok(title.indexOf(aEvent.accessible.name) != -1,
+           "Window title contains the name of active tab document");
+      }
+
+      this.getID = function switchTab_getID() { return "switch tab"; }
+    }
+
+    ////////////////////////////////////////////////////////////////////////////
+    // Tests
+
+    //gA11yEventDumpID = "eventdump"; // debug stuff
+    //gA11yEventDumpToConsole = true; // debug
+
+    var gQueue = null;
+    function doTests()
+    {
+      gQueue = new eventQueue();
+      gQueue.push(new addTab("about:mozilla"));
+      gQueue.push(new switchTab());
+      gQueue.onFinish = function()
+      {
+        closeBrowserWindow();
+      }
+
+      gQueue.invoke();
     }
 
     SimpleTest.waitForExplicitFinish();
-    addLoadEvent(doTest);
+    openBrowserWindow(doTests, "about:");
   ]]>
   </script>
 
   <vbox flex="1" style="overflow: auto;">
   <body xmlns="http://www.w3.org/1999/xhtml">
     <a target="_blank"
        href="https://bugzilla.mozilla.org/show_bug.cgi?id=507382"
        title="focus is fired earlier than root accessible name is changed when switching between tabs">
new file mode 100644
--- /dev/null
+++ b/accessible/tests/mochitest/pivot.js
@@ -0,0 +1,217 @@
+Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
+
+////////////////////////////////////////////////////////////////////////////////
+// Constants
+
+const PREFILTER_INVISIBLE = nsIAccessibleTraversalRule.PREFILTER_INVISIBLE;
+const FILTER_MATCH = nsIAccessibleTraversalRule.FILTER_MATCH;
+const FILTER_IGNORE = nsIAccessibleTraversalRule.FILTER_IGNORE;
+const FILTER_IGNORE_SUBTREE = nsIAccessibleTraversalRule.FILTER_IGNORE_SUBTREE;
+
+////////////////////////////////////////////////////////////////////////////////
+// Traversal rules
+
+/**
+ * Rule object to traverse all focusable nodes and text nodes.
+ */
+var HeadersTraversalRule =
+{
+  getMatchRoles: function(aRules)
+  {
+    aRules.value = [ROLE_HEADING];
+    return aRules.value.length;
+  },
+
+  preFilter: PREFILTER_INVISIBLE,
+
+  match: function(aAccessible)
+  {
+    return FILTER_MATCH;
+  },
+
+  QueryInterface: XPCOMUtils.generateQI([nsIAccessibleTraversalRule])
+}
+
+/**
+ * Traversal rule for all focusable nodes or leafs.
+ */
+var ObjectTraversalRule =
+{
+  getMatchRoles: function(aRules)
+  {
+    aRules.value = [];
+    return 0;
+  },
+
+  preFilter: PREFILTER_INVISIBLE,
+
+  match: function(aAccessible)
+  {
+    var rv = FILTER_IGNORE;
+    var role = aAccessible.role;
+    if (hasState(aAccessible, STATE_FOCUSABLE) &&
+        (role != ROLE_DOCUMENT && role != ROLE_INTERNAL_FRAME))
+      rv = FILTER_IGNORE_SUBTREE | FILTER_MATCH;
+    else if (aAccessible.childCount == 0 &&
+             role != ROLE_STATICTEXT && aAccessible.name.trim())
+      rv = FILTER_MATCH;
+
+    return rv;
+  },
+
+  QueryInterface: XPCOMUtils.generateQI([nsIAccessibleTraversalRule])
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// Virtual state invokers and checkers
+
+/**
+ * A checker for virtual cursor changed events.
+ */
+function virtualCursorChangedChecker(aDocAcc, aIdOrNameOrAcc, aTextOffsets)
+{
+  this.__proto__ = new invokerChecker(EVENT_VIRTUALCURSOR_CHANGED, aDocAcc);
+
+  this.check = function virtualCursorChangedChecker_check(aEvent)
+  {
+    var position = aDocAcc.virtualCursor.position;
+    position.QueryInterface(nsIAccessNode);
+
+    var idMatches = position.DOMNode.id == aIdOrNameOrAcc;
+    var nameMatches = position.name == aIdOrNameOrAcc;
+    var accMatches = position == aIdOrNameOrAcc;
+
+    SimpleTest.ok(idMatches || nameMatches || accMatches, "id or name matches",
+                  "expecting " + aIdOrNameOrAcc + ", got '" +
+                  prettyName(position));
+
+    if (aTextOffsets) {
+      SimpleTest.is(aDocAcc.virtualCursor.startOffset, aTextOffsets[0],
+                    "wrong start offset");
+      SimpleTest.is(aDocAcc.virtualCursor.endOffset, aTextOffsets[1],
+                    "wrong end offset");
+    }
+  };
+}
+
+/**
+ * Set a text range in the pivot and wait for virtual cursor change event.
+ *
+ * @param aDocAcc document that manages the virtual cursor
+ * @param aTextAccessible accessible to set to virtual cursor's position
+ * @param aTextOffsets start and end offsets of text range to set in virtual
+ *   cursor
+ */
+function setVirtualCursorRangeInvoker(aDocAcc, aTextAccessible, aTextOffsets)
+{
+  this.invoke = function virtualCursorChangedInvoker_invoke()
+  {
+    SimpleTest.info(prettyName(aTextAccessible) + " " + aTextOffsets);
+    aDocAcc.virtualCursor.setTextRange(aTextAccessible,
+                                       aTextOffsets[0],
+                                       aTextOffsets[1]);
+  };
+
+  this.getID = function setVirtualCursorRangeInvoker_getID()
+  {
+    return "Set offset in " + prettyName(aTextAccessible) +
+      " to (" + aTextOffsets[0] + ", " + aTextOffsets[1] + ")";
+  }
+
+  this.eventSeq = [
+    new virtualCursorChangedChecker(aDocAcc, aTextAccessible, aTextOffsets)
+  ];
+}
+
+/**
+ * Move the pivot and wait for virtual cursor change event.
+ *
+ * @param aDocAcc document that manages the virtual cursor
+ * @param aPivotMoveMethod method to test (ie. "moveNext", "moveFirst", etc.)
+ * @param aRule traversal rule object
+ * @param aIdOrNameOrAcc id, accessivle or accessible name to expect virtual
+ *   cursor to land on after performing move method.
+ */
+function setVirtualCursorPosInvoker(aDocAcc, aPivotMoveMethod, aRule,
+                                    aIdOrNameOrAcc)
+{
+  this.invoke = function virtualCursorChangedInvoker_invoke()
+  {
+    var moved = aDocAcc.virtualCursor[aPivotMoveMethod](aRule);
+    SimpleTest.ok((aIdOrNameOrAcc && moved) || (!aIdOrNameOrAcc && !moved),
+                  "moved pivot");
+  };
+
+  this.getID = function setVirtualCursorPosInvoker_getID()
+  {
+    return "Do " + (aIdOrNameOrAcc ? "" : "no-op ") + aPivotMoveMethod;
+  }
+
+  if (aIdOrNameOrAcc) {
+    this.eventSeq = [ new virtualCursorChangedChecker(aDocAcc, aIdOrNameOrAcc) ];
+  } else {
+    this.eventSeq = [];
+    this.unexpectedEventSeq = [
+      new invokerChecker(EVENT_VIRTUALCURSOR_CHANGED, aDocAcc)
+    ];
+  }
+}
+
+/**
+ * Add invokers to a queue to test a rule and an expected sequence of element ids
+ * or accessible names for that rule in the given document.
+ *
+ * @param aQueue event queue in which to push invoker sequence.
+ * @param aDocAcc the managing document of the virtual cursor we are testing
+ * @param aRule the traversal rule to use in the invokers
+ * @param aSequence a sequence of accessible names or elemnt ids to expect with
+ *   the given rule in the given document
+ */
+function queueTraversalSequence(aQueue, aDocAcc, aRule, aSequence)
+{
+  aDocAcc.virtualCursor.position = null;
+
+  for (var i = 0; i < aSequence.length; i++) {
+    var invoker = new setVirtualCursorPosInvoker(aDocAcc, "moveNext",
+                                                 aRule, aSequence[i]);
+    aQueue.push(invoker);
+  }
+
+  // No further more matches for given rule, expect no virtual cursor changes.
+  aQueue.push(new setVirtualCursorPosInvoker(aDocAcc, "moveNext", aRule, null));
+
+  for (var i = aSequence.length-2; i >= 0; i--) {
+    var invoker = new setVirtualCursorPosInvoker(aDocAcc, "movePrevious",
+                                                 aRule, aSequence[i])
+    aQueue.push(invoker);
+  }
+
+  // No previous more matches for given rule, expect no virtual cursor changes.
+  aQueue.push(new setVirtualCursorPosInvoker(aDocAcc, "movePrevious", aRule, null));
+
+  aQueue.push(new setVirtualCursorPosInvoker(
+    aDocAcc, "moveLast", aRule, aSequence[aSequence.length - 1]));
+
+  // No further more matches for given rule, expect no virtual cursor changes.
+  aQueue.push(new setVirtualCursorPosInvoker(aDocAcc, "moveNext", aRule, null));
+
+  aQueue.push(new setVirtualCursorPosInvoker(
+    aDocAcc, "moveFirst", aRule, aSequence[0]));
+
+  // No previous more matches for given rule, expect no virtual cursor changes.
+  aQueue.push(new setVirtualCursorPosInvoker(aDocAcc, "movePrevious", aRule, null));
+}
+
+/**
+ * A debug utility for writing proper sequences for queueTraversalSequence.
+ */
+function dumpTraversalSequence(aPivot, aRule)
+{
+  var sequence = []
+  if (aPivot.moveFirst(aRule)) {
+    do {
+      sequence.push("'" + prettyName(aPivot.position) + "'");
+    } while (aPivot.moveNext(aRule))
+  }
+  SimpleTest.info("\n[" + sequence.join(", ") + "]\n");
+}
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/accessible/tests/mochitest/pivot/Makefile.in
@@ -0,0 +1,54 @@
+#
+# ***** 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.org code.
+#
+# The Initial Developer of the Original Code is
+# Mozilla Foundation.
+# Portions created by the Initial Developer are Copyright (C) 2010
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+#   Eitan Isaacson <eitan@monotonous.org> (original author)
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either of 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 *****
+
+DEPTH		= ../../../..
+topsrcdir	= @top_srcdir@
+srcdir		= @srcdir@
+VPATH		= @srcdir@
+relativesrcdir  = accessible/pivot
+
+include $(DEPTH)/config/autoconf.mk
+include $(topsrcdir)/config/rules.mk
+
+_TEST_FILES = \
+		doc_virtualcursor.html \
+		test_virtualcursor.html \
+		$(NULL)
+
+libs:: $(_TEST_FILES)
+	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/a11y/$(relativesrcdir)
new file mode 100644
--- /dev/null
+++ b/accessible/tests/mochitest/pivot/doc_virtualcursor.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <title>Pivot test document</title>
+  <meta charset="utf-8" />
+</head>
+<body>
+  <h1 id="heading-1-1">Main Title</h1>
+  <h2 id="heading-2-1">First Section Title</h2>
+  <p id="paragraph-1">
+    Lorem ipsum <strong>dolor</strong> sit amet. Integer vitae urna
+    leo, id <a href="#">semper</a> nulla.
+  </p>
+  <h2 id="heading-2-2">Second Section Title</h2>
+  <p id="paragraph-2">
+    Sed accumsan luctus lacus, vitae mollis arcu tristique vulputate.</p>
+  <iframe
+     src="data:text/html,<html><body>An <i>embedded</i> document.</body></html>">
+  </iframe>
+<p>
+  <a href="http://mozilla.org" title="Link 1 title">Link 1</a>
+  <a href="http://mozilla.org" title="Link 2 title">Link 2</a>
+  <a href="http://mozilla.org" title="Link 3 title">Link 3</a>
+</p>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/accessible/tests/mochitest/pivot/test_virtualcursor.html
@@ -0,0 +1,95 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <title>Tests pivot functionality in virtual cursors</title>
+  <meta charset="utf-8" />
+  <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js">
+  </script>
+
+  <script type="application/javascript" src="../common.js"></script>
+  <script type="application/javascript" src="../browser.js"></script>
+  <script type="application/javascript" src="../events.js"></script>
+  <script type="application/javascript" src="../role.js"></script>
+  <script type="application/javascript" src="../states.js"></script>
+  <script type="application/javascript" src="../pivot.js"></script>
+
+  <script type="application/javascript">
+    var gBrowserWnd = null;
+    var gQueue = null;
+
+    function doTest()
+    {
+      var rootAcc = getRootAccessible(browserWindow().document);
+      try {
+        rootAcc.QueryInterface(nsIAccessibleCursorable);
+      } catch (e) {
+        ok(false, "Root accessible does not support nsIAccessibleCursorable");
+      }
+      var doc = currentTabDocument();
+      var docAcc = getAccessible(doc, [nsIAccessibleDocument,
+                                       nsIAccessibleCursorable]);
+
+      // Test that embedded documents don't have their own virtual cursor.
+      is(docAcc.childDocumentCount, 1, "Expecting one child document");
+      var childDoc = docAcc.getChildDocumentAt(0);
+      var supportsVC = true;
+      try {
+        childDoc.QueryInterface(nsIAccessibleCursorable);
+      } catch (e) {
+        supportsVC = false;
+      }
+
+      ok(!supportsVC, "no nsIAccessibleCursorable support in child document");
+
+      gQueue = new eventQueue();
+
+      gQueue.onFinish = function onFinish()
+      {
+        closeBrowserWindow();
+      }
+
+      queueTraversalSequence(gQueue, docAcc, HeadersTraversalRule,
+                             ['heading-1-1', 'heading-2-1', 'heading-2-2']);
+
+      queueTraversalSequence(
+        gQueue, docAcc, ObjectTraversalRule,
+        ['Main Title', 'First Section Title', 'Lorem ipsum ',
+         'dolor', ' sit amet. Integer vitae urna leo, id ',
+         'semper', ' nulla. ', 'Second Section Title',
+         'Sed accumsan luctus lacus, vitae mollis arcu tristique vulputate.',
+         'An ', 'embedded', ' document.', 'Link 1', 'Link 2', 'Link 3']);
+
+      // Just a random smoke test to see if our setTextRange works.
+      gQueue.push(
+        new setVirtualCursorRangeInvoker(
+          docAcc,
+          getAccessible(doc.getElementById('paragraph-2'), nsIAccessibleText),
+          [2,6]));
+
+      gQueue.invoke();
+    }
+
+    SimpleTest.waitForExplicitFinish();
+    addLoadEvent(function () {
+      /* We open a new browser because we need to test with a top-level content
+         document. */
+      openBrowserWindow(
+        doTest,
+        "chrome://mochitests/content/a11y/accessible/pivot/doc_virtualcursor.html");
+    });
+  </script>
+</head>
+<body id="body">
+
+  <a target="_blank"
+     title="Introduce virtual cursor/soft focus functionality to a11y API"
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=698823">Mozilla Bug 698823</a>
+  <p id="display"></p>
+  <div id="content" style="display: none"></div>
+  <pre id="test">
+  </pre>
+</body>
+</html>
--- a/accessible/tests/mochitest/relations/Makefile.in
+++ b/accessible/tests/mochitest/relations/Makefile.in
@@ -40,19 +40,19 @@ DEPTH		= ../../../..
 topsrcdir	= @top_srcdir@
 srcdir		= @srcdir@
 VPATH		= @srcdir@
 relativesrcdir  = accessible/relations
 
 include $(DEPTH)/config/autoconf.mk
 include $(topsrcdir)/config/rules.mk
 
-# test_tabbrowser.xul disabled for misusing <tabbrowser> (bug 715857)
-
 _TEST_FILES =\
+		test_embeds.xul \
 		test_general.html \
 		test_general.xul \
+		test_tabbrowser.xul \
 		test_tree.xul \
 		test_update.html \
 		$(NULL)
 
 libs:: $(_TEST_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/a11y/$(relativesrcdir)
new file mode 100644
--- /dev/null
+++ b/accessible/tests/mochitest/relations/test_embeds.xul
@@ -0,0 +1,152 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+                 type="text/css"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+        title="Embeds relation tests">
+
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+
+  <script type="application/javascript"
+          src="../common.js"></script>
+  <script type="application/javascript"
+          src="../role.js"></script>
+  <script type="application/javascript"
+          src="../states.js"></script>
+  <script type="application/javascript"
+          src="../events.js"></script>
+  <script type="application/javascript"
+          src="../relations.js"></script>
+
+  <script type="application/javascript">
+  <![CDATA[
+    ////////////////////////////////////////////////////////////////////////////
+    // Helpers
+
+    function tabBrowser()
+    {
+      return gBrowserWnd.gBrowser;
+    }
+
+    function currentBrowser()
+    {
+      return tabBrowser().selectedBrowser;
+    }
+
+    function currentTabDocument()
+    {
+      return currentBrowser().contentDocument;
+    }
+
+    ////////////////////////////////////////////////////////////////////////////
+    // Invokers
+
+    function loadURI(aURI)
+    {
+      this.invoke = function loadURI_invoke()
+      {
+        tabBrowser().loadURI(aURI);
+      }
+
+      this.eventSeq = [
+        new invokerChecker(EVENT_REORDER, currentBrowser)
+      ];
+
+      this.finalCheck = function loadURI_finalCheck()
+      {
+        testRelation(gBrowserWnd.document, RELATION_EMBEDS,
+                     getAccessible(currentTabDocument()));
+      }
+
+      this.getID = function loadURI_getID()
+      {
+        return "load uri " + aURI;
+      }
+    }
+
+    function loadOneTab(aURI)
+    {
+      this.invoke = function loadOneTab_invoke()
+      {
+        tabBrowser().loadOneTab(aURI, null, null, null, false);
+      }
+
+      this.eventSeq = [
+        new invokerChecker(EVENT_REORDER, currentBrowser)
+      ];
+
+      this.finalCheck = function loadURI_finalCheck()
+      {
+        testRelation(gBrowserWnd.document, RELATION_EMBEDS,
+                     getAccessible(currentTabDocument()));
+      }
+
+      this.getID = function loadOneTab_getID()
+      {
+        return "load uri '" + aURI + "' in new tab";
+      }
+    }
+
+    ////////////////////////////////////////////////////////////////////////////
+    // Testing
+
+    var gBrowserWnd = null;
+    function loadBrowser()
+    {
+      gBrowserWnd = window.openDialog("chrome://browser/content/", "_blank",
+                                      "chrome,all,dialog=no", "about:");
+
+      addA11yLoadEvent(startTests, gBrowserWnd);
+    }
+
+    function startTests()
+    {
+      // Wait for tab load.
+      var browser = currentBrowser();
+      addA11yLoadEvent(doTests, browser.contentWindow);
+    }
+
+    //gA11yEventDumpToConsole = true; // debug
+
+    var gQueue = null;
+    function doTests()
+    {
+      testRelation(gBrowserWnd.document, RELATION_EMBEDS,
+                   getAccessible(currentTabDocument()));
+
+      gQueue = new eventQueue();
+
+      gQueue.push(new loadURI("about:about"));
+      gQueue.push(new loadOneTab("about:mozilla"));
+
+      gQueue.onFinish = function()
+      {
+        gBrowserWnd.close();
+      }
+      gQueue.invoke();
+    }
+
+    SimpleTest.waitForExplicitFinish();
+    addLoadEvent(loadBrowser);
+  ]]>
+  </script>
+
+  <vbox flex="1" style="overflow: auto;">
+  <body xmlns="http://www.w3.org/1999/xhtml">
+    <a target="_blank"
+       href="https://bugzilla.mozilla.org/show_bug.cgi?id=707654"
+       title="Embeds relation on root accessible can return not content document">
+      Mozilla Bug 707654
+    </a>
+    <p id="display"></p>
+    <div id="content" style="display: none">
+    </div>
+    <pre id="test">
+    </pre>
+  </body>
+  </vbox>
+</window>
--- a/accessible/tests/mochitest/relations/test_general.html
+++ b/accessible/tests/mochitest/relations/test_general.html
@@ -121,29 +121,16 @@
       testRelation("caption", RELATION_LABEL_FOR, "table");
       testRelation("table", RELATION_LABELLED_BY, "caption");
 
       // 'labelled by'/'label for' relation for html:fieldset and
       // html:legend
       testRelation("legend", RELATION_LABEL_FOR, "fieldset");
       testRelation("fieldset", RELATION_LABELLED_BY, "legend");
 
-      // 'embeds' relation for root accessible
-      var docAcc = null;
-      var parentOfDocAcc = null;
-      var parentDocAcc = getAccessible(document);
-      do {
-        docAcc = parentDocAcc;
-        parentOfDocAcc = getAccessible(docAcc.parent, [nsIAccessNode]);
-        parentDocAcc = getAccessible(parentOfDocAcc.document,
-                                     [nsIAccessible]);
-      } while (getRole(parentDocAcc) != ROLE_CHROME_WINDOW)
-
-      testRelation(parentDocAcc, RELATION_EMBEDS, docAcc);
-
       // finish test
       SimpleTest.finish();
     }
 
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(doTest);
   </script>
 
--- a/accessible/tests/mochitest/relations/test_general.xul
+++ b/accessible/tests/mochitest/relations/test_general.xul
@@ -98,29 +98,16 @@
       // aria-flowto, multiple relations
       testRelation("flowto1", RELATION_FLOWS_TO, ["flowfrom1", "flowfrom2"]);
       testRelation("flowfrom1", RELATION_FLOWS_FROM, "flowto1");
       testRelation("flowfrom2", RELATION_FLOWS_FROM, "flowto1");
 
       // 'default button' relation
       testRelation("textbox", RELATION_DEFAULT_BUTTON, "submit");
 
-      // 'embeds' relation for root accessible
-      var docAcc = null;
-      var parentOfDocAcc = null;
-      var parentDocAcc = getAccessible(document);
-      do {
-        docAcc = parentDocAcc;
-        parentOfDocAcc = getAccessible(docAcc.parent, [nsIAccessNode]);
-        parentDocAcc = getAccessible(parentOfDocAcc.document,
-                                     [nsIAccessible]);
-      } while (getRole(parentDocAcc) != ROLE_CHROME_WINDOW)
-
-      testRelation(parentDocAcc, RELATION_EMBEDS, docAcc);
-
       // 'labelled by'/'label for' relation for xul:goupbox and xul:label of
       // xul:caption
       var groupboxAcc = getAccessible("groupbox");
       var labelAcc = groupboxAcc.firstChild;
       testRelation(labelAcc, RELATION_LABEL_FOR, groupboxAcc);
       testRelation(groupboxAcc, RELATION_LABELLED_BY, labelAcc);
 
       // 'labelled by'/'label for' relations for xul:tab and xul:tabpanel
--- a/accessible/tests/mochitest/relations/test_tabbrowser.xul
+++ b/accessible/tests/mochitest/relations/test_tabbrowser.xul
@@ -1,105 +1,86 @@
 <?xml version="1.0"?>
 <?xml-stylesheet href="chrome://global/skin" type="text/css"?>
 <?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
                  type="text/css"?>
 
-<!-- Firefox tabbrowser -->
-<?xml-stylesheet href="chrome://browser/content/browser.css"
-                 type="text/css"?>
-<!-- SeaMonkey tabbrowser -->
-<?xml-stylesheet href="chrome://navigator/content/navigator.css"
-                 type="text/css"?>
-
 <window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
         title="Accessible XUL tabbrowser relation tests">
 
   <script type="application/javascript"
           src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
   <script type="application/javascript"
-          src="chrome://browser/content/utilityOverlay.js"/>
+          src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
 
   <script type="application/javascript"
           src="../common.js" />
   <script type="application/javascript"
           src="../role.js" />
   <script type="application/javascript"
           src="../relations.js" />
   <script type="application/javascript"
           src="../events.js" />
+  <script type="application/javascript"
+          src="../browser.js"></script>
 
-  <script type="application/javascript" src="chrome://global/content/globalOverlay.js"/>
   <script type="application/javascript">
   <![CDATA[
     ////////////////////////////////////////////////////////////////////////////
-    // Test
+    // Invoker
+    function testTabRelations()
+    {
+      this.eventSeq = [
+        new invokerChecker(EVENT_DOCUMENT_LOAD_COMPLETE, tabDocumentAt, 0),
+        new invokerChecker(EVENT_DOCUMENT_LOAD_COMPLETE, tabDocumentAt, 1)
+     ];
 
-    const Ci = Components.interfaces;
+      this.invoke = function testTabRelations_invoke()
+      {
+        var docURIs = ["about:", "about:mozilla"];
+        tabBrowser().loadTabs(docURIs, false, true);
+      }
+
+      this.finalCheck = function testTabRelations_finalCheck(aEvent)
+      {
+        ////////////////////////////////////////////////////////////////////////
+        // 'labelled by'/'label for' relations for xul:tab and xul:tabpanel
 
-    // Hack to make xul:tabbrowser work.
-    var handleDroppedLink  = null;
-    Components.utils.import("resource://gre/modules/Services.jsm");
-    var XULBrowserWindow = {
-      isBusy: false,
-      setOverLink: function (link, b) {
+        var tabs = tabBrowser().tabContainer.childNodes;
+        var panels = tabBrowser().mTabBox.tabpanels.childNodes;
+
+        testRelation(panels[0], RELATION_LABELLED_BY, tabs[0]);
+        testRelation(tabs[0], RELATION_LABEL_FOR, panels[0]);
+        testRelation(panels[1], RELATION_LABELLED_BY, tabs[1]);
+        testRelation(tabs[1], RELATION_LABEL_FOR, panels[1]);
       }
-    };
-    var gFindBar = {
-      hidden: true
-    };
 
+      this.getID = function testTabRelations_getID()
+      {
+        return "relations of tabs";
+      }
+    }
+    
+    ////////////////////////////////////////////////////////////////////////////
+    // Test
+    var gQueue = null;
     function doTest()
     {
-      var tabBrowser = document.getElementById("tabbrowser");
-
-      // Load documents into tabs and wait for reorder events caused by these
-      // documents load before we start the test.
-      var docURIs = ["about:", "about:mozilla"];
-
-      var handler = {
-        handleEvent: function handleEvent(aEvent) {
-          var target = aEvent.accessible;
-          if (target.role == ROLE_INTERNAL_FRAME &&
-              target.parent.parent == getAccessible(this.tabBrowser.mTabBox.tabpanels)) {
-            this.reorderCnt++;
-          }
-
-          if (this.reorderCnt == docURIs.length) {
-            unregisterA11yEventListener(EVENT_REORDER, this);
-            testRelations();
-          }
-        },
+      // Load documents into tabs and wait for DocLoadComplete events caused by
+      // these documents load before we start the test.
 
-        tabBrowser: tabBrowser,
-        reorderCnt: 0
-      };
-      registerA11yEventListener(EVENT_REORDER, handler);
-
-      tabBrowser.loadTabs(docURIs, false, true);
-    }
+      gQueue = new eventQueue();
 
-    function testRelations()
-    {
-      //////////////////////////////////////////////////////////////////////////
-      // 'labelled by'/'label for' relations for xul:tab and xul:tabpanel
-
-      var tabs = getNode("tabbrowser").tabContainer.childNodes;
-      var panels = getNode("tabbrowser").mTabBox.tabpanels.childNodes;
-
-      testRelation(panels[0], RELATION_LABELLED_BY, tabs[0]);
-      testRelation(tabs[0], RELATION_LABEL_FOR, panels[0]);
-      testRelation(panels[1], RELATION_LABELLED_BY, tabs[1]);
-      testRelation(tabs[1], RELATION_LABEL_FOR, panels[1]);
-
-      SimpleTest.finish();
+      gQueue.push(new testTabRelations());
+      gQueue.onFinish = function() { closeBrowserWindow(); }
+      gQueue.invoke(); // Will call SimpleTest.finish();
     }
 
     SimpleTest.waitForExplicitFinish();
-    addA11yLoadEvent(doTest);
+    openBrowserWindow(doTest);
   ]]>
   </script>
 
   <vbox flex="1" style="overflow: auto;">
     <body xmlns="http://www.w3.org/1999/xhtml">
       <a target="_blank"
          href="https://bugzilla.mozilla.org/show_bug.cgi?id=552944"
          title="No relationship between tabs and associated property page in new tabbrowser construct">
@@ -107,32 +88,13 @@
       </a><br/>
       <p id="display"></p>
       <div id="content" style="display: none">
       </div>
       <pre id="test">
       </pre>
     </body>
 
-    <!-- Hack to make xul:tabbrowser work -->
-    <menubar>
-      <menu label="menu">
-        <menupopup>
-          <menuitem label="close window hook" id="menu_closeWindow"/>
-          <menuitem label="close hook" id="menu_close"/>
-        </menupopup>
-      </menu>
-    </menubar>
-
-    <tabs id="tabbrowser-tabs" class="tabbrowser-tabs"
-          tabbrowser="tabbrowser"
-          setfocus="false">
-      <tab class="tabbrowser-tab" selected="true"/>
-    </tabs>
-    <tabbrowser id="tabbrowser"
-                type="content-primary"
-                tabcontainer="tabbrowser-tabs"
-                flex="1"/>
-    <toolbar id="addon-bar"/>
+    <vbox id="eventdump"></vbox>
   </vbox>
 
 </window>
 
--- a/accessible/tests/mochitest/test_aria_roles.html
+++ b/accessible/tests/mochitest/test_aria_roles.html
@@ -126,17 +126,17 @@ https://bugzilla.mozilla.org/show_bug.cg
   <table role="main" id="main_table">main table
     <tr><td>hi<td></tr></table>
   <table role="navigation" id="navigation_table">navigation table
     <tr><td>hi<td></tr></table>
   <table role="search" id="search_table">search table
     <tr><td>hi<td></tr></table>
 
   <!-- test gEmptyRoleMap -->
-  <table role="label">
+  <table role="button">
     <tr>
       <td id="cell">cell</td>
     </tr>
   </table>
 
   <!-- user agents must not map abstract roles to platform API -->
   <!-- test abstract base type roles -->
   <div role="composite" id="composite">composite</div>
--- a/accessible/tests/mochitest/test_childAtPoint.html
+++ b/accessible/tests/mochitest/test_childAtPoint.html
@@ -32,25 +32,25 @@
       // document.
       testChildAtPoint(txt, -1, 1, false, null);
       testChildAtPoint(txt, -1, 1, true, null);
 
       // ::MustPrune case, point is outside of root accessible.
       testChildAtPoint(txt, -10000, 10000, false, null);
       testChildAtPoint(txt, -10000, 10000, true, null);
 
-      // Not specific case, point is inside of label accessible.
-      var label = getAccessible("label");
-      var labelText = label.firstChild;
-      testChildAtPoint(label, 1, 1, false, labelText);
-      testChildAtPoint(label, 1, 1, true, labelText);
+      // Not specific case, point is inside of btn accessible.
+      var btn = getAccessible("btn");
+      var btnText = btn.firstChild;
+      testChildAtPoint(btn, 1, 1, false, btnText);
+      testChildAtPoint(btn, 1, 1, true, btnText);
   
-      // Not specific case, point is outside of label accessible.
-      testChildAtPoint(label, -1, 1, false, null);
-      testChildAtPoint(label, -1, 1, true, null);
+      // Not specific case, point is outside of btn accessible.
+      testChildAtPoint(btn, -1, 1, false, null);
+      testChildAtPoint(btn, -1, 1, true, null);
 
       // Out of flow accessible testing, do not return out of flow accessible
       // because it's not a child of the accessible even visually it is.
       var rectArea = getNode("area").getBoundingClientRect();
       var outOfFlow = getNode("outofflow");
       outOfFlow.style.left = rectArea.left + "px";
       outOfFlow.style.top = rectArea.top + "px";
 
@@ -73,17 +73,17 @@
   <div id="content" style="display: none"></div>
   <pre id="test">
   </pre>
 
   <div role="list" id="list">
     <div role="listitem" id="listitem"><span role="image" id="image">img</span>item</div>
   </div>
 
-  <span role="label">label1</span><span role="label" id="label">label2</span>
+  <span role="button">button1</span><span role="button" id="btn">button2</span>
 
   <span role="textbox">textbox1</span><span role="textbox" id="txt">textbox2</span>
 
   <div id="outofflow" style="width: 10px; height: 10px; position: absolute; left: 0px; top: 0px; background-color: yellow;">
   </div>
   <div id="area" style="width: 100px; height: 100px; background-color: blue;"></div>
 
 </body>
--- a/accessible/tests/mochitest/tree/Makefile.in
+++ b/accessible/tests/mochitest/tree/Makefile.in
@@ -40,18 +40,16 @@ DEPTH		= ../../../..
 topsrcdir	= @top_srcdir@
 srcdir		= @srcdir@
 VPATH		= @srcdir@
 relativesrcdir  = accessible/tree
 
 include $(DEPTH)/config/autoconf.mk
 include $(topsrcdir)/config/rules.mk
 
-# test_tabbrowser.xul disabled for misusing <tabbrowser> (bug 715857)
-
 _TEST_FILES =\
 		dockids.html \
 	$(warning test_applicationacc.xul temporarily disabled, see bug 561508) \
 		test_aria_globals.html \
 		test_aria_imgmap.html \
 		test_aria_presentation.html \
 		test_button.xul \
 		test_canvas.html \
@@ -67,16 +65,17 @@ include $(topsrcdir)/config/rules.mk
 		test_iframe.html \
 		test_img.html \
 		test_invalidationlist.html \
 		test_list.html \
 		test_map.html \
 		test_media.html \
 		test_select.html \
 		test_tabbox.xul \
+		test_tabbrowser.xul \
 		test_table.html \
 		test_tree.xul \
 		test_txtcntr.html \
 		test_txtctrl.html \
 		test_txtctrl.xul \
 		wnd.xul \
 		$(NULL)
 
--- a/accessible/tests/mochitest/tree/test_tabbrowser.xul
+++ b/accessible/tests/mochitest/tree/test_tabbrowser.xul
@@ -1,230 +1,208 @@
 <?xml version="1.0"?>
 <?xml-stylesheet href="chrome://global/skin" type="text/css"?>
 
-<!-- Firefox tabbrowser -->
-<?xml-stylesheet href="chrome://browser/content/browser.css"
-                 type="text/css"?>
-<!-- SeaMonkey tabbrowser -->
-<?xml-stylesheet href="chrome://navigator/content/navigator.css"
-                 type="text/css"?>
-
 <?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
                  type="text/css"?>
 
 <window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
         title="Accessible XUL tabbrowser hierarchy tests">
 
   <script type="application/javascript"
           src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
   <script type="application/javascript"
-          src="chrome://browser/content/utilityOverlay.js"/>
+          src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
 
   <script type="application/javascript"
           src="../common.js" />
   <script type="application/javascript"
           src="../role.js" />
   <script type="application/javascript"
           src="../events.js" />
+  <script type="application/javascript"
+          src="../browser.js"></script>
 
-  <script type="application/javascript" src="chrome://global/content/globalOverlay.js"/>
   <script type="application/javascript">
   <![CDATA[
     ////////////////////////////////////////////////////////////////////////////
-    // Test
+    // invoker
+    function testTabHierarchy()
+    {
+      this.eventSeq = [
+        new invokerChecker(EVENT_DOCUMENT_LOAD_COMPLETE, tabDocumentAt, 0),
+        new invokerChecker(EVENT_DOCUMENT_LOAD_COMPLETE, tabDocumentAt, 1)
+      ];
+
+      this.invoke = function testTabHierarchy_invoke()
+      {
+        var docURIs = ["about:", "about:mozilla"];
+        tabBrowser().loadTabs(docURIs, false, true);
+      }
 
-    const Ci = Components.interfaces;
+      this.finalCheck = function testTabHierarchy_finalCheck(aEvent)
+      {
+        ////////////////////
+        // Tab bar
+        ////////////////////
+        var tabsAccTree = {
+          // xul:tabs
+          role: ROLE_PAGETABLIST,
+          children: [
+            // Children depend on application (UI): see below.
+          ]
+        };
+
+        // SeaMonkey and Firefox tabbrowser UIs differ.
+        if ("restoreTab" in tabBrowser) {
+          SimpleTest.ok(true, "Testing SeaMonkey tabbrowser UI.");
 
-    // Hack to make xul:tabbrowser work.
-    Components.utils.import("resource://gre/modules/Services.jsm");
-    var handleDroppedLink = null;
-    var XULBrowserWindow = {
-      isBusy: false,
-      setOverLink: function (link, b) {}
-    };
-    var gFindBar = {
-      hidden: true
-    };
-
-    function doTest()
-    {
-      var tabBrowser = document.getElementById("tabbrowser");
+          tabsAccTree.children.splice(0, 0,
+            {
+              // xul:toolbarbutton ("Open a new tab")
+              role: ROLE_PUSHBUTTON,
+              children: []
+            },
+            {
+              // xul:tab ("about:")
+              role: ROLE_PAGETAB,
+              children: []
+            },
+            {
+              // tab ("about:mozilla")
+              role: ROLE_PAGETAB,
+              children: []
+            },
+            {
+              // xul:toolbarbutton ("List all tabs")
+              role: ROLE_PUSHBUTTON,
+              children: [
+                {
+                  // xul:menupopup
+                  role: ROLE_MENUPOPUP,
+                  children: []
+                }
+              ]
+            },
+            {
+              // xul:toolbarbutton ("Close current tab")
+              role: ROLE_PUSHBUTTON,
+              children: []
+            }
+            );
+        } else {
+          SimpleTest.ok(true, "Testing Firefox tabbrowser UI.");
 
-      // Load documents into tabs and wait for reorder events caused by these
-      // documents load before we start the test.
-      var docURIs = ["about:", "about:mozilla"];
+          // NB: The (3) buttons are not visible, unless manually hovered,
+          //     probably due to size reduction in this test.
+          tabsAccTree.children.splice(0, 0,
+            {
+              // xul:tab ("about:")
+              role: ROLE_PAGETAB,
+              children: [
+                {
+                  // xul:toolbarbutton ("Close Tab")
+                  role: ROLE_PUSHBUTTON,
+                  children: []
+                }
+              ]
+            },
+            {
+              // tab ("about:mozilla")
+              role: ROLE_PAGETAB,
+              children: [
+                {
+                  // xul:toolbarbutton ("Close Tab")
+                  role: ROLE_PUSHBUTTON,
+                  children: []
+                }
+              ]
+            },
+            {
+              // xul:toolbarbutton ("Open a new tab")
+              role: ROLE_PUSHBUTTON,
+              children: []
+            }
+            // "List all tabs" dropdown
+            // XXX: This child(?) is not present in this test.
+            //      I'm not sure why (though probably expected).
+            );
+        }
 
-      var handler = {
-        handleEvent: function handleEvent(aEvent) {
-          var target = aEvent.accessible;
-          if (target.role == ROLE_INTERNAL_FRAME &&
-              target.parent.parent == getAccessible(this.tabBrowser.mTabBox.tabpanels)) {
-            this.reorderCnt++;
-          }
+        testAccessibleTree(tabBrowser().tabContainer, tabsAccTree);
 
-          if (this.reorderCnt == docURIs.length) {
-            unregisterA11yEventListener(EVENT_REORDER, this);
-            testAccTree();
-          }
-        },
+        ////////////////////
+        // Tab contents
+        ////////////////////
+        var tabboxAccTree = {
+          // xul:tabpanels
+          role: ROLE_PANE,
+          children: [
+            {
+              // xul:notificationbox
+              role: ROLE_PROPERTYPAGE,
+              children: [
+                {
+                  // xul:browser
+                  role: ROLE_INTERNAL_FRAME,
+                  children: [
+                    {
+                      // #document ("about:")
+                      role: ROLE_DOCUMENT
+                      // children: [ ... ] // Ignore document content.
+                    }
+                  ]
+                }
+              ]
+            },
+            {
+              // notificationbox
+              role: ROLE_PROPERTYPAGE,
+              children: [
+                {
+                  // browser
+                  role: ROLE_INTERNAL_FRAME,
+                  children: [
+                    {
+                      // #document ("about:mozilla")
+                      role: ROLE_DOCUMENT
+                      // children: [ ... ] // Ignore document content.
+                    }
+                  ]
+                }
+              ]
+            }
+          ]
+        };
 
-        tabBrowser: tabBrowser,
-        reorderCnt: 0
-      };
-      registerA11yEventListener(EVENT_REORDER, handler);
+        testAccessibleTree(tabBrowser().mTabBox.tabpanels, tabboxAccTree);
+      }
 
-      // Test XUL and HTML documents.
-      tabBrowser.loadTabs(docURIs, false, true);
+      this.getID = function testTabHierarchy_getID()
+      {
+        return "hierarchy of tabs";
+      }
     }
 
-    function testAccTree()
+    ////////////////////////////////////////////////////////////////////////////
+    // Test
+    var gQueue = null;
+    function doTest()
     {
-      var tabBrowser = document.getElementById("tabbrowser");
-
-      ////////////////////
-      // Tab bar
-      ////////////////////
-      var tabsAccTree = {
-        // xul:tabs
-        role: ROLE_PAGETABLIST,
-        children: [
-          // Children depend on application (UI): see below.
-        ]
-      };
-
-      // SeaMonkey and Firefox tabbrowser UIs differ.
-      if ("restoreTab" in tabBrowser) {
-        SimpleTest.ok(true, "Testing SeaMonkey tabbrowser UI.");
-
-        tabsAccTree.children.splice(0, 0,
-          {
-            // xul:toolbarbutton ("Open a new tab")
-            role: ROLE_PUSHBUTTON,
-            children: []
-          },
-          {
-            // xul:tab ("about:")
-            role: ROLE_PAGETAB,
-            children: []
-          },
-          {
-            // tab ("about:mozilla")
-            role: ROLE_PAGETAB,
-            children: []
-          },
-          {
-            // xul:toolbarbutton ("List all tabs")
-            role: ROLE_PUSHBUTTON,
-            children: [
-              {
-                // xul:menupopup
-                role: ROLE_MENUPOPUP,
-                children: []
-              }
-            ]
-          },
-          {
-            // xul:toolbarbutton ("Close current tab")
-            role: ROLE_PUSHBUTTON,
-            children: []
-          }
-          );
-      } else {
-        SimpleTest.ok(true, "Testing Firefox tabbrowser UI.");
+      // Load documents into tabs and wait for docLoadComplete events caused by these
+      // documents load before we start the test.
+      gQueue = new eventQueue();
 
-        // NB: The (3) buttons are not visible, unless manually hovered,
-        //     probably due to size reduction in this test.
-        tabsAccTree.children.splice(0, 0,
-          {
-            // xul:tab ("about:")
-            role: ROLE_PAGETAB,
-            children: [
-              {
-                // xul:toolbarbutton ("Close Tab")
-                role: ROLE_PUSHBUTTON,
-                children: []
-              }
-            ]
-          },
-          {
-            // tab ("about:mozilla")
-            role: ROLE_PAGETAB,
-            children: [
-              {
-                // xul:toolbarbutton ("Close Tab")
-                role: ROLE_PUSHBUTTON,
-                children: []
-              }
-            ]
-          },
-          {
-            // xul:toolbarbutton ("Open a new tab")
-            role: ROLE_PUSHBUTTON,
-            children: []
-          }
-          // "List all tabs" dropdown
-          // XXX: This child(?) is not present in this test.
-          //      I'm not sure why (though probably expected).
-          );
-      }
-
-      testAccessibleTree(tabBrowser.tabContainer, tabsAccTree);
-
-      ////////////////////
-      // Tab contents
-      ////////////////////
-      var tabboxAccTree = {
-        // xul:tabpanels
-        role: ROLE_PANE,
-        children: [
-          {
-            // xul:notificationbox
-            role: ROLE_PROPERTYPAGE,
-            children: [
-              {
-                // xul:browser
-                role: ROLE_INTERNAL_FRAME,
-                children: [
-                  {
-                    // #document ("about:")
-                    role: ROLE_DOCUMENT
-                    // children: [ ... ] // Ignore document content.
-                  }
-                ]
-              }
-            ]
-          },
-          {
-            // notificationbox
-            role: ROLE_PROPERTYPAGE,
-            children: [
-              {
-                // browser
-                role: ROLE_INTERNAL_FRAME,
-                children: [
-                  {
-                    // #document ("about:mozilla")
-                    role: ROLE_DOCUMENT
-                    // children: [ ... ] // Ignore document content.
-                  }
-                ]
-              }
-            ]
-          }
-        ]
-      };
-
-      testAccessibleTree(tabBrowser.mTabBox.tabpanels, tabboxAccTree);
-
-      SimpleTest.finish();
+      gQueue.push(new testTabHierarchy());
+      gQueue.onFinish = function() { closeBrowserWindow(); }
+      gQueue.invoke(); // Will call SimpleTest.finish();
     }
 
     SimpleTest.waitForExplicitFinish();
-    addA11yLoadEvent(doTest);
+    openBrowserWindow(doTest);
   ]]>
   </script>
 
   <vbox flex="1" style="overflow: auto;">
     <body xmlns="http://www.w3.org/1999/xhtml">
       <a target="_blank"
          href="https://bugzilla.mozilla.org/show_bug.cgi?id=540389"
          title=" WARNING: Bad accessible tree!: [tabbrowser tab] ">
@@ -237,31 +215,12 @@
       </a><br/>
       <p id="display"></p>
       <div id="content" style="display: none">
       </div>
       <pre id="test">
       </pre>
     </body>
 
-    <!-- Hack to make xul:tabbrowser work -->
-    <menubar>
-      <menu label="menu">
-        <menupopup>
-          <menuitem label="close window hook" id="menu_closeWindow"/>
-          <menuitem label="close hook" id="menu_close"/>
-        </menupopup>
-      </menu>
-    </menubar>
-
-    <tabs id="tabbrowser-tabs" class="tabbrowser-tabs"
-          tabbrowser="tabbrowser"
-          setfocus="false">
-      <tab class="tabbrowser-tab" selected="true" fadein="true"/>
-    </tabs>
-    <tabbrowser id="tabbrowser"
-                type="content-primary"
-                tabcontainer="tabbrowser-tabs"
-                flex="1"/>
-    <toolbar id="addon-bar"/>
+    <vbox id="eventdump"></vbox>
   </vbox>
 
 </window>
--- a/b2g/app/b2g.js
+++ b/b2g/app/b2g.js
@@ -33,17 +33,16 @@
  * 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 ***** */
 
 #filter substitution
 
 pref("toolkit.defaultChromeURI", "chrome://browser/content/shell.xul");
-pref("general.useragent.compatMode.firefox", true);
 pref("browser.chromeURL", "chrome://browser/content/");
 #ifdef MOZ_OFFICIAL_BRANDING
 pref("browser.homescreenURL", "file:///system/home/homescreen.html");
 #else
 pref("browser.homescreenURL", "file:///data/local/homescreen.html,file:///system/home/homescreen.html");
 #endif
 
 // URL for the dialer application.
@@ -389,26 +388,39 @@ pref("security.fileuri.strict_origin_pol
 // deep within the bowels of the widgetry system.  Remove me when GL
 // compositing isn't default disabled in widget/android.
 pref("layers.acceleration.force-enabled", true);
 
 // screen.enabled and screen.brightness properties.
 pref("dom.screenEnabledProperty.enabled", true);
 pref("dom.screenBrightnessProperty.enabled", true);
 
+// handle links targeting new windows
+// 1=current window/tab, 2=new window, 3=new tab in most recent window
+pref("browser.link.open_newwindow", 3);
+
+// 0: no restrictions - divert everything
+// 1: don't divert window.open at all
+// 2: don't divert window.open with features
+pref("browser.link.open_newwindow.restriction", 0);
+
 // Enable browser frame
 pref("dom.mozBrowserFramesEnabled", true);
 pref("dom.mozBrowserFramesWhitelist", "http://localhost:6666");
 
 // Temporary permission hack for WebSMS
 pref("dom.sms.enabled", true);
 pref("dom.sms.whitelist", "file://,http://localhost:6666");
+
 // Ignore X-Frame-Options headers.
 pref("b2g.ignoreXFrameOptions", true);
 
 // "Preview" landing of bug 710563, which is bogged down in analysis
 // of talos regression.  This is a needed change for higher-framerate
 // CSS animations, and incidentally works around an apparent bug in
 // our handling of requestAnimationFrame() listeners, which are
 // supposed to enable this REPEATING_PRECISE_CAN_SKIP behavior.  The
 // secondary bug isn't really worth investigating since it's obseleted
 // by bug 710563.
 pref("layout.frame_rate.precise", true);
+
+// Screen timeout in minutes
+pref("power.screen.timeout", 60);
--- a/b2g/chrome/content/shell.js
+++ b/b2g/chrome/content/shell.js
@@ -42,27 +42,29 @@ const Cu = Components.utils;
 const CC = Components.Constructor;
 
 const LocalFile = CC('@mozilla.org/file/local;1',
                      'nsILocalFile',
                      'initWithPath');
 
 Cu.import('resource://gre/modules/XPCOMUtils.jsm');
 Cu.import('resource://gre/modules/Services.jsm');
+
 XPCOMUtils.defineLazyGetter(Services, 'env', function() {
   return Cc['@mozilla.org/process/environment;1']
            .getService(Ci.nsIEnvironment);
 });
+
 XPCOMUtils.defineLazyGetter(Services, 'ss', function() {
   return Cc['@mozilla.org/content/style-sheet-service;1']
            .getService(Ci.nsIStyleSheetService);
 });
-XPCOMUtils.defineLazyGetter(Services, 'fm', function() {
-  return Cc['@mozilla.org/focus-manager;1']
-           .getService(Ci.nsIFocusManager);
+XPCOMUtils.defineLazyGetter(Services, 'idle', function() {
+  return Cc['@mozilla.org/widget/idleservice;1']
+           .getService(Ci.nsIIdleService);
 });
 
 // In order to use http:// scheme instead of file:// scheme
 // (that is much more restricted) the following code kick-off
 // a local http server listening on http://127.0.0.1:7777 and
 // http://localhost:7777.
 function startupHttpd(baseDir, port) {
   const httpdURL = 'chrome://browser/content/httpd.js';
@@ -71,39 +73,39 @@ function startupHttpd(baseDir, port) {
   let server = new httpd.nsHttpServer();
   server.registerDirectory('/', new LocalFile(baseDir));
   server.registerContentType('appcache', 'text/cache-manifest');
   server.start(port);
 }
 
 // FIXME Bug 707625
 // until we have a proper security model, add some rights to
-// the pre-installed web applications 
+// the pre-installed web applications
 function addPermissions(urls) {
   let permissions = [
     'indexedDB', 'indexedDB-unlimited', 'webapps-manage', 'offline-app'
   ];
   urls.forEach(function(url) {
     let uri = Services.io.newURI(url, null, null);
     let allow = Ci.nsIPermissionManager.ALLOW_ACTION;
-    
+
     permissions.forEach(function(permission) {
       Services.perms.add(uri, permission, allow);
     });
   });
 }
 
 
 var shell = {
   // FIXME/bug 678695: this should be a system setting
   preferredScreenBrightness: 1.0,
 
-  get home() {
-    delete this.home;
-    return this.home = document.getElementById('homescreen');
+  get contentBrowser() {
+    delete this.contentBrowser;
+    return this.contentBrowser = document.getElementById('homescreen');
   },
 
   get homeURL() {
     try {
       let homeSrc = Services.env.get('B2G_HOMESCREEN');
       if (homeSrc)
         return homeSrc;
     } catch (e) {}
@@ -126,17 +128,17 @@ var shell = {
     if (!homeURL) {
       let msg = 'Fatal error during startup: [No homescreen found]';
       return alert(msg);
     }
 
     window.controllers.appendController(this);
     window.addEventListener('keypress', this);
     window.addEventListener('MozApplicationManifest', this);
-    this.home.addEventListener('load', this, true);
+    this.contentBrowser.addEventListener('load', this, true);
 
     try {
       Services.io.offline = false;
 
       let fileScheme = 'file://';
       if (homeURL.substring(0, fileScheme.length) == fileScheme) {
         homeURL = homeURL.replace(fileScheme, '');
 
@@ -151,17 +153,25 @@ var shell = {
         homeURL = homeURL.replace(baseDir, baseHost + ':' + SERVER_PORT);
       }
       addPermissions([homeURL]);
     } catch (e) {
       let msg = 'Fatal error during startup: [' + e + '[' + homeURL + ']';
       return alert(msg);
     }
 
-    let browser = this.home;
+    // Load webapi+apps.js as a frame script
+    let frameScriptUrl = 'chrome://browser/content/webapi.js';
+    try {
+      messageManager.loadFrameScript(frameScriptUrl, true);
+    } catch (e) {
+      dump('Error when loading ' + frameScriptUrl + ' as a frame script: ' + e + '\n');
+    }
+
+    let browser = this.contentBrowser;
     browser.homePage = homeURL;
     browser.goHome();
   },
 
   stop: function shell_stop() {
     window.controllers.removeController(this);
     window.removeEventListener('keypress', this);
     window.removeEventListener('MozApplicationManifest', this);
@@ -182,59 +192,63 @@ var shell = {
 
   isCommandEnabled: function shell_isCommandEnabled(cmd) {
     return true;
   },
 
   doCommand: function shell_doCommand(cmd) {
     switch (cmd) {
       case 'cmd_close':
-        this.home.contentWindow.postMessage('appclose', '*');
+        content.postMessage('appclose', '*');
         break;
     }
   },
 
   handleEvent: function shell_handleEvent(evt) {
     switch (evt.type) {
       case 'keypress':
         switch (evt.keyCode) {
           case evt.DOM_VK_HOME:
-            this.sendEvent(this.home.contentWindow, 'home');
+            this.sendEvent(content, 'home');
             break;
           case evt.DOM_VK_SLEEP:
             this.toggleScreen();
 
             let details = {
               'enabled': screen.mozEnabled
             };
-            this.sendEvent(this.home.contentWindow, 'sleep', details);
+            this.sendEvent(content, 'sleep', details);
             break;
           case evt.DOM_VK_ESCAPE:
             if (evt.defaultPrevented)
               return;
             this.doCommand('cmd_close');
             break;
         }
         break;
       case 'load':
-        this.home.removeEventListener('load', this, true);
+        this.contentBrowser.removeEventListener('load', this, true);
         this.turnScreenOn();
+
+        let chromeWindow = window.QueryInterface(Ci.nsIDOMChromeWindow);
+        chromeWindow.browserDOMWindow = new nsBrowserAccess();
+
         this.sendEvent(window, 'ContentStart');
         break;
       case 'MozApplicationManifest':
         try {
           if (!Services.prefs.getBoolPref('browser.cache.offline.enable'))
             return;
 
           let contentWindow = evt.originalTarget.defaultView;
           let documentElement = contentWindow.document.documentElement;
           if (!documentElement)
             return;
 
-          let manifest = documentElement.getAttribute("manifest");
+          let manifest = documentElement.getAttribute('manifest');
           if (!manifest)
             return;
 
           let documentURI = contentWindow.document.documentURIObject;
           if (!Services.perms.testPermission(documentURI, 'offline-app')) {
             if (Services.prefs.getBoolPref('browser.offline-apps.notify')) {
               // FIXME Bug 710729 - Add a UI for offline cache notifications
               return;
@@ -268,83 +282,65 @@ var shell = {
   },
   turnScreenOff: function shell_turnScreenOff() {
     screen.mozEnabled = false;
     screen.mozBrightness = 0.0;
   },
   turnScreenOn: function shell_turnScreenOn() {
     screen.mozEnabled = true;
     screen.mozBrightness = this.preferredScreenBrightness;
-  },
-};
-
-(function VirtualKeyboardManager() {
-  let activeElement = null;
-  let isKeyboardOpened = false;
-
-  let constructor = {
-    handleEvent: function vkm_handleEvent(evt) {
-      let contentWindow = shell.home.contentWindow.wrappedJSObject;
-
-      switch (evt.type) {
-        case 'ContentStart':
-          contentWindow.navigator.mozKeyboard = new MozKeyboard();
-          break;
-        case 'keypress':
-          if (evt.keyCode != evt.DOM_VK_ESCAPE || !isKeyboardOpened)
-            return;
-
-          shell.sendEvent(contentWindow, 'hideime');
-          isKeyboardOpened = false;
-
-          evt.preventDefault();
-          evt.stopPropagation();
-          break;
-        case 'mousedown':
-          if (evt.target != activeElement || isKeyboardOpened)
-            return;
-
-          let type = activeElement.type;
-          shell.sendEvent(contentWindow, 'showime', { type: type });
-          isKeyboardOpened = true;
-          break;
-      }
-    },
-    observe: function vkm_observe(subject, topic, data) {
-      let contentWindow = shell.home.contentWindow;
-
-      let shouldOpen = parseInt(data);
-      if (shouldOpen && !isKeyboardOpened) {
-        activeElement = Services.fm.focusedElement;
-        if (!activeElement)
-          return;
-
-        let type = activeElement.type;
-        shell.sendEvent(contentWindow, 'showime', { type: type });
-      } else if (!shouldOpen && isKeyboardOpened) {
-        shell.sendEvent(contentWindow, 'hideime');
-      }
-      isKeyboardOpened = shouldOpen;
-    }
-  };
-
-  Services.obs.addObserver(constructor, 'ime-enabled-state-changed', false);
-  ['ContentStart', 'keypress', 'mousedown'].forEach(function vkm_events(type) {
-    window.addEventListener(type, constructor, true);
-  });
-})();
-
-
-function MozKeyboard() {
-}
-
-MozKeyboard.prototype = {
-  sendKey: function mozKeyboardSendKey(keyCode, charCode) {
-    charCode = (charCode == undefined) ? keyCode : charCode;
-
-    var utils = window.QueryInterface(Ci.nsIInterfaceRequestor)
-                      .getInterface(Ci.nsIDOMWindowUtils);
-    ['keydown', 'keypress', 'keyup'].forEach(function sendKeyEvents(type) {
-      utils.sendKeyEvent(type, keyCode, charCode, null);
-    });
   }
 };
 
+(function PowerManager() {
+  let idleHandler = {
+    observe: function(subject, topic, time) {
+      if (topic === "idle") {
+        // TODO: Check wakelock status. See bug 697132.
+        shell.turnScreenOff();
+      }
+    },
+  }
+  let idleTimeout = Services.prefs.getIntPref("power.screen.timeout");
+  if (idleTimeout) {
+    Services.idle.addIdleObserver(idleHandler, idleTimeout);
+  }
+})();
+
+function nsBrowserAccess() {
+}
+
+nsBrowserAccess.prototype = {
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIBrowserDOMWindow]),
+
+  openURI: function openURI(uri, opener, where, context) {
+    // TODO This should be replaced by an 'open-browser-window' intent
+    let contentWindow = content.wrappedJSObject;
+    if (!('getApplicationManager' in contentWindow))
+      return null;
+
+    let applicationManager = contentWindow.getApplicationManager();
+    if (!applicationManager)
+      return null;
+
+    let url = uri ? uri.spec : 'about:blank';
+    let window = applicationManager.launch(url, where);
+    return window.contentWindow;
+  },
+
+  openURIInFrame: function openURIInFrame(uri, opener, where, context) {
+    throw new Error('Not Implemented');
+  },
+
+  isTabContentWindow: function isTabContentWindow(contentWindow) {
+    return contentWindow == window;
+  }
+};
+
+// Pipe `console` log messages to the nsIConsoleService which writes them
+// to logcat.
+Services.obs.addObserver(function onConsoleAPILogEvent(subject, topic, data) {
+  let message = subject.wrappedJSObject;
+  let prefix = "Content JS " + message.level.toUpperCase() +
+               " at " + message.filename + ":" + message.lineNumber +
+               " in " + (message.functionName || "anonymous") + ": ";
+  Services.console.logStringMessage(prefix + Array.join(message.arguments, " "));
+}, "console-api-log-event", false);
--- a/b2g/chrome/content/touch.js
+++ b/b2g/chrome/content/touch.js
@@ -61,22 +61,22 @@
   // This can be turned on if canPreventMouseEvents is true and the consumer
   // application call evt.preventDefault();
   let preventMouseEvents = false;
 
   let TouchEventHandler = {
     events: ['mousedown', 'mousemove', 'mouseup', 'click', 'unload'],
     start: function teh_start() {
       this.events.forEach((function(evt) {
-        shell.home.addEventListener(evt, this, true);
+        shell.contentBrowser.addEventListener(evt, this, true);
       }).bind(this));
     },
     stop: function teh_stop() {
       this.events.forEach((function(evt) {
-        shell.home.removeEventListener(evt, this, true);
+        shell.contentBrowser.removeEventListener(evt, this, true);
       }).bind(this));
     },
     handleEvent: function teh_handleEvent(evt) {
       if (evt.button || ignoreEvents)
         return;
 
       let eventTarget = this.target;
       let type = '';
@@ -134,22 +134,17 @@
             return;
 
           window.clearTimeout(contextMenuTimeout);
           this.target = null;
           TouchEventHandler.stop();
           return;
 
         case 'click':
-          if (!isNewTouchAction) {
-            debug('click: cancel');
-
-            evt.preventDefault();
-            evt.stopPropagation();
-          } else {
+          if (isNewTouchAction) {
             // Mouse events has been cancelled so dispatch a sequence
             // of events to where touchend has been fired
             if (preventMouseEvents) {
               evt.preventDefault();
               evt.stopPropagation();
 
               let target = evt.target;
               ignoreEvents = true;
new file mode 100644
--- /dev/null
+++ b/b2g/chrome/content/webapi.js
@@ -0,0 +1,1791 @@
+/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- /
+/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
+/* 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/. */
+
+dump('======================= webapi+apps.js ======================= \n');
+
+'use strict';
+
+let { classes: Cc, interfaces: Ci, utils: Cu }  = Components;
+Cu.import('resource://gre/modules/XPCOMUtils.jsm');
+Cu.import('resource://gre/modules/Services.jsm');
+
+XPCOMUtils.defineLazyGetter(Services, 'fm', function() {
+  return Cc['@mozilla.org/focus-manager;1']
+           .getService(Ci.nsIFocusManager);
+});
+
+(function() {
+  function generateAPI(window) {
+    let navigator = window.navigator;
+    XPCOMUtils.defineLazyGetter(navigator, 'mozSettings', function() {
+      return new Settings();
+    });
+
+    XPCOMUtils.defineLazyGetter(navigator, 'mozContacts', function() {
+      return ContactsManager;
+    });
+
+    XPCOMUtils.defineLazyGetter(navigator, 'mozApps', function() {
+      let mozApps = {
+        enumerate: function mozAppsEnumerate(callback) {
+          callback(webapps);
+        }
+      };
+      return mozApps;
+    });
+
+    XPCOMUtils.defineLazyGetter(navigator, 'mozKeyboard', function() {
+      return new MozKeyboard();
+    });
+
+    updateApplicationCache(window);
+  };
+
+  let progressListener = {
+    onStateChange: function onStateChange(progress, request,
+                                          flags, status) {
+    },
+
+    onProgressChange: function onProgressChange(progress, request,
+                                                curSelf, maxSelf,
+                                                curTotal, maxTotal) {
+    },
+
+    onLocationChange: function onLocationChange(progress, request,
+                                                locationURI, flags) {
+      if (locationURI.spec.indexOf('/apps/') == -1)
+        return;
+
+      content.addEventListener('appwillopen', function(evt) {
+        let appManager = content.wrappedJSObject.Gaia.AppManager;
+        let topWindow = appManager.foregroundWindow.contentWindow;
+        generateAPI(topWindow);
+      });
+
+      generateAPI(content.wrappedJSObject);
+    },
+
+    onStatusChange: function onStatusChange(progress, request,
+                                            status, message) {
+    },
+
+    onSecurityChange: function onSecurityChange(progress, request,
+                                                state) {
+    },
+
+    QueryInterface: function QueryInterface(aIID) {
+      if (aIID.equals(Ci.nsIWebProgressListener) ||
+          aIID.equals(Ci.nsISupportsWeakReference) ||
+          aIID.equals(Ci.nsISupports)) {
+          return this;
+      }
+
+      throw Components.results.NS_ERROR_NO_INTERFACE;
+    }
+  };
+
+  let flags = Ci.nsIWebProgress.NOTIFY_LOCATION;
+  let webProgress = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
+                            .getInterface(Ci.nsIWebProgress);
+  flags = Ci.nsIWebProgress.NOTIFY_ALL;
+  webProgress.addProgressListener(progressListener, flags);
+})();
+
+
+// WebApps - Application Cache
+function updateApplicationCache(window) {
+  try {
+    var cache = window.applicationCache;
+    cache.update();
+
+    cache.addEventListener('updateready', function updateReady(evt) {
+      // XXX Add a nice UI when an update is ready asking if the user
+      // want to reload the application now.
+      cache.swapCache();
+      window.document.location.reload();
+    });
+  } catch (e) {}
+}
+
+// MozKeyboard
+(function VirtualKeyboardManager() {
+  let activeElement = null;
+  let isKeyboardOpened = false;
+  
+  function fireEvent(type, details) {
+    let event = content.document.createEvent('CustomEvent');
+    event.initCustomEvent(type, true, true, details ? details : {});
+    content.dispatchEvent(event);
+  }
+
+  let constructor = {
+    handleEvent: function vkm_handleEvent(evt) {
+      switch (evt.type) {
+        case 'keypress':
+          if (evt.keyCode != evt.DOM_VK_ESCAPE || !isKeyboardOpened)
+            return;
+
+          fireEvent('hideime');
+          isKeyboardOpened = false;
+
+          evt.preventDefault();
+          evt.stopPropagation();
+          break;
+
+        case 'mousedown':
+          if (evt.target != activeElement || isKeyboardOpened)
+            return;
+
+          let type = activeElement.type;
+          fireEvent('showime', { type: type });
+          isKeyboardOpened = true;
+          break;
+      }
+    },
+    observe: function vkm_observe(subject, topic, data) {
+      let shouldOpen = parseInt(data);
+      if (shouldOpen && !isKeyboardOpened) {
+        activeElement = Services.fm.focusedElement;
+        if (!activeElement)
+          return;
+
+        let type = activeElement.type;
+        fireEvent('showime', { type: type });
+      } else if (!shouldOpen && isKeyboardOpened) {
+        fireEvent('hideime');
+      }
+      isKeyboardOpened = shouldOpen;
+    }
+  };
+
+  Services.obs.addObserver(constructor, 'ime-enabled-state-changed', false);
+  ['keypress', 'mousedown'].forEach(function vkm_events(type) {
+    addEventListener(type, constructor, true);
+  });
+})();
+
+
+function MozKeyboard() {
+}
+
+MozKeyboard.prototype = {
+  sendKey: function mozKeyboardSendKey(keyCode, charCode) {
+    charCode = (charCode == undefined) ? keyCode : charCode;
+
+    let utils = content.QueryInterface(Ci.nsIInterfaceRequestor)
+                       .getInterface(Ci.nsIDOMWindowUtils);
+    ['keydown', 'keypress', 'keyup'].forEach(function sendKey(type) {
+      utils.sendKeyEvent(type, keyCode, charCode, null);
+    });
+  }
+};
+
+// MozSettings - Bug 678695
+function Settings() {
+  content.addEventListener('message', this);
+}
+
+Settings.prototype = {
+  ERROR_SETTING_UNKNOWN: 0x0001,
+
+  _requests: [],
+  handleEvent: function settings_handleEvent(event) {
+    var data = event.data;
+    if (typeof data !== 'string')
+      return;
+
+    var cmd = data.split(':');
+    if (cmd.length < 4 || cmd[0] != 'settings')
+      return;
+
+    var method = cmd[1], id = cmd[2], key = cmd[3], value = cmd[4];
+    var request = this._requests[id];
+    switch (method) {
+      case 'error':
+        request.error = this.ERROR_SETTING_UNKNOWN;
+        this._dispatchEvent(request, request.TYPE_ERROR);
+        break;
+
+      case 'success':
+        request.result = new SettingsMessage(key, value);
+        this._dispatchEvent(request, request.TYPE_SUCCESS);
+        break;
+    }
+  },
+
+  get: function settings_get(key) {
+    var request = new SettingsRequest();
+    var id = this._requests.length;
+    this._requests.push(request);
+
+    var msg = 'settings:get:' + id + ':' + key;
+    content.top.wrappedJSObject.postMessage(msg, '*');
+    return request;
+  },
+
+  set: function settings_set(key, value) {
+    var request = new SettingsRequest();
+    var id = this._requests.length;
+    this._requests.push(request);
+
+    var msg = 'settings:set:' + id + ':' + key + ':' + value;
+    content.top.wrappedJSObject.postMessage(msg, '*');
+    return request;
+  },
+
+  _dispatchEvent: function(target, type) {
+    var evt = content.document.createEvent('CustomEvent');
+    evt.initCustomEvent(type, true, false, null);
+    target.dispatchEvent(evt);
+  }
+};
+
+
+/* ========== nsIDOMMozSettingsRequest ========== */
+function SettingsRequest() {
+  this.readyState = this.STATE_PROCESSING;
+
+  this.error = null;
+  this.onerror = null;
+  // XXX should be an array
+  this._errorCallback = null;
+
+  this.result = null;
+  this.onsuccess = null;
+  // XXX should be an array
+  this._successCallback = null;
+}
+
+SettingsRequest.prototype = {
+  // States of the request
+  STATE_PROCESSING: 'processing',
+  STATE_DONE: 'done',
+
+  // Types of events
+  TYPE_SUCCESS: 'success',
+  TYPE_ERROR: 'error',
+
+  addEventListener: function sr_addEventListener(type, callback) {
+    switch (type) {
+      case this.TYPE_SUCCESS:
+        this._successCallback = callback;
+        break;
+      case this.TYPE_ERROR:
+        this._errorCallback = callback;
+        break;
+    }
+  },
+
+  removeEventListener: function sr_removeEventListener(type, callback) {
+    switch (type) {
+      case this.TYPE_SUCCESS:
+        this._successCallback = null;
+        break;
+      case this.TYPE_ERROR:
+        this._errorCallback = null;
+        break;
+    }
+  },
+
+  dispatchEvent: function sr_dispatchEvent(event) {
+    this.readyState = this.STATE_DONE;
+
+    switch (event.type) {
+      case this.TYPE_SUCCESS:
+        if (this._successCallback)
+          this._successCallback(event);
+
+        if (this.onsuccess)
+          this.onsuccess(event);
+        break;
+      case this.TYPE_ERROR:
+        if (this._errorCallback)
+          this._errorCallback(event);
+
+        if (this.onerror)
+          this.onerror(event);
+        break;
+    }
+  }
+};
+
+/* ========== nsIDOMMozSettingsMessage ========== */
+function SettingsMessage(name, value) {
+  this.name = name;
+  this.value = value;
+}
+
+
+// MozApps - Bug 709015
+var webapps = [
+  { // browser
+    installOrigin: 'http://gaiamobile.org:8888',
+    origin: '../browser',
+    receipt: null,
+    installTime: 1323339869000,
+    manifest: {
+      'name': 'Browser',
+      'description': 'Gaia Web Browser',
+      'launch_path': '/browser.html',
+      'developer': {
+        'name': 'The Gaia Team',
+        'url': 'https://github.com/andreasgal/gaia'
+      },
+      'icons': {
+        '120': '/style/icons/Browser.png'
+      }
+    }
+  },
+  { // camera
+    'installOrigin': 'http://gaiamobile.org:8888',
+    'origin': '../camera',
+    'receipt': null,
+    'installTime': 1323339869000,
+    manifest: {
+      'name': 'Camera',
+      'description': 'Gaia Camera',
+      'launch_path': '/camera.html',
+      'developer': {
+        'name': 'The Gaia Team',
+        'url': 'https://github.com/andreasgal/gaia'
+      },
+      'icons': {
+        '120': '/style/icons/Camera.png'
+      }
+    }
+  },
+  { // dialer
+    'installOrigin': 'http://gaiamobile.org:8888',
+    'origin': '../dialer',
+    'receipt': null,
+    'installTime': 1323339869000,
+    manifest: {
+      'name': 'Dialer',
+      'description': 'Gaia Dialer',
+      'launch_path': '/dialer.html',
+      'developer': {
+        'name': 'The Gaia Team',
+        'url': 'https://github.com/andreasgal/gaia'
+      },
+      'icons': {
+        '120': '/style/icons/Phone.png'
+      }
+    }
+  },
+  { // gallery
+    'installOrigin': 'http://gaiamobile.org:8888',
+    'origin': '../gallery',
+    'receipt': null,
+    'installTime': 1323339869000,
+    manifest: {
+      'name': 'Gallery',
+      'description': 'Gaia Gallery',
+      'launch_path': '/gallery.html',
+      'developer': {
+        'name': 'The Gaia Team',
+        'url': 'https://github.com/andreasgal/gaia'
+      },
+      'icons': {
+        '120': '/style/icons/Gallery.png'
+      }
+    }
+  },
+  { // music
+    'installOrigin': 'http://gaiamobile.org:8888',
+    'origin': '../music',
+    'receipt': null,
+    'installTime': 1323339869000,
+    manifest: {
+      'name': 'Music',
+      'description': 'Gaia Music',
+      'launch_path': '/music.html',
+      'developer': {
+        'name': 'The Gaia Team',
+        'url': 'https://github.com/andreasgal/gaia'
+      },
+      'icons': {
+        '120': '/style/icons/Music.png'
+      }
+    }
+  },
+  { // market
+    'installOrigin': 'http://gaiamobile.org:8888',
+    'origin': '../market',
+    'receipt': null,
+    'installTime': 1323339869000,
+    manifest: {
+      'name': 'Market',
+      'description': 'Market for downloading and installing apps',
+      'launch_path': '/market.html',
+      'developer': {
+        'name': 'The Gaia Team',
+        'url': 'https://github.com/andreasgal/gaia'
+      },
+      'icons': {
+        '120': '/style/icons/Market.png'
+      }
+    }
+  },
+  { // settings
+    'installOrigin': 'http://gaiamobile.org:8888',
+    'origin': '../settings',
+    'receipt': null,
+    'installTime': 1323339869000,
+    manifest: {
+      'name': 'Settings',
+      'description': 'Gaia Settings',
+      'launch_path': '/settings.html',
+      'developer': {
+        'name': 'The Gaia Team',
+        'url': 'https://github.com/andreasgal/gaia'
+      },
+      'icons': {
+        '120': '/style/icons/Settings.png'
+      }
+    }
+  },
+  { // sms
+    'installOrigin': 'http://gaiamobile.org:8888',
+    'origin': '../sms',
+    'receipt': null,
+    'installTime': 1323339869000,
+    manifest: {
+      'name': 'Messages',
+      'description': 'Gaia Messages',
+      'launch_path': '/sms.html',
+      'developer': {
+        'name': 'The Gaia Team',
+        'url': 'https://github.com/andreasgal/gaia'
+      },
+      'icons': {
+        '120': '/style/icons/Messages.png'
+      }
+    }
+  }
+];
+
+
+// Bug 674720 - webContacts
+var contacts = [
+  {
+    id: '3',
+    displayName: 'Coby Newman',
+    name: {
+      familyName: ['Coby'],
+      givenName: ['Newman']
+    },
+    phones: ['1-823-949-7735'],
+    emails: ['posuere.at@hendreritaarcu.com']
+  },
+  {
+    id: '6',
+    displayName: 'Caesar Velasquez',
+    name: {
+      familyName: ['Caesar'],
+      givenName: ['Velasquez']
+    },
+    phones: ['1-355-185-5419'],
+    emails: ['fames@Duis.org']
+  },
+  {
+    id: '9',
+    displayName: 'Hamilton Farrell',
+    name: {
+      familyName: ['Hamilton'],
+      givenName: ['Farrell']
+    },
+    phones: ['1-682-456-9186'],
+    emails: ['sem@Uttinciduntvehicula.com']
+  },
+  {
+    id: '12',
+    displayName: 'Emery Livingston',
+    name: {
+      familyName: ['Emery'],
+      givenName: ['Livingston']
+    },
+    phones: ['1-510-151-9801'],
+    emails: ['orci.luctus.et@massaInteger.com']
+  },
+  {
+    id: '15',
+    displayName: 'Griffith Heath',
+    name: {
+      familyName: ['Griffith'],
+      givenName: ['Heath']
+    },
+    phones: ['1-800-719-3201'],
+    emails: ['dapibus@Inlorem.ca']
+  },
+  {
+    id: '18',
+    displayName: 'Luke Stuart',
+    name: {
+      familyName: ['Luke'],
+      givenName: ['Stuart']
+    },
+    phones: ['1-120-910-1976'],
+    emails: ['congue@nibh.ca']
+  },
+  {
+    id: '21',
+    displayName: 'Brennan Love',
+    name: {
+      familyName: ['Brennan'],
+      givenName: ['Love']
+    },
+    phones: ['1-724-155-2807'],
+    emails: ['interdum.libero.dui@cursusvestibulum.edu']
+  },
+  {
+    id: '24',
+    displayName: 'Lamar Meadows',
+    name: {
+      familyName: ['Lamar'],
+      givenName: ['Meadows']
+    },
+    phones: ['1-976-164-8769'],
+    emails: ['tincidunt@non.com']
+  },
+  {
+    id: '27',
+    displayName: 'Erasmus Flynn',
+    name: {
+      familyName: ['Erasmus'],
+      givenName: ['Flynn']
+    },
+    phones: ['1-488-678-3487'],
+    emails: ['lorem.ut.aliquam@eu.ca']
+  },
+  {
+    id: '30',
+    displayName: 'Aladdin Ellison',
+    name: {
+      familyName: ['Aladdin'],
+      givenName: ['Ellison']
+    },
+    phones: ['1-977-743-6797'],
+    emails: ['sociosqu.ad@sollicitudin.org']
+  },
+  {
+    id: '33',
+    displayName: 'Valentine Rasmussen',
+    name: {
+      familyName: ['Valentine'],
+      givenName: ['Rasmussen']
+    },
+    phones: ['1-265-504-2025'],
+    emails: ['ultrices.iaculis@acsem.edu']
+  },
+  {
+    id: '36',
+    displayName: 'Deacon Murphy',
+    name: {
+      familyName: ['Deacon'],
+      givenName: ['Murphy']
+    },
+    phones: ['1-770-450-1221'],
+    emails: ['varius@erat.edu']
+  },
+  {
+    id: '39',
+    displayName: 'Paul Kennedy',
+    name: {
+      familyName: ['Paul'],
+      givenName: ['Kennedy']
+    },
+    phones: ['1-689-891-3529'],
+    emails: ['ac.arcu@vitae.edu']
+  },
+  {
+    id: '42',
+    displayName: 'Aaron Chase',
+    name: {
+      familyName: ['Aaron'],
+      givenName: ['Chase']
+    },
+    phones: ['1-451-574-7937'],
+    emails: ['tempor.bibendum.Donec@pharetraQuisque.edu']
+  },
+  {
+    id: '45',
+    displayName: 'Geoffrey Dunn',
+    name: {
+      familyName: ['Geoffrey'],
+      givenName: ['Dunn']
+    },
+    phones: ['1-924-387-2395'],
+    emails: ['a.malesuada@tellusPhasellus.com']
+  },
+  {
+    id: '48',
+    displayName: 'Ashton Russo',
+    name: {
+      familyName: ['Ashton'],
+      givenName: ['Russo']
+    },
+    phones: ['1-182-776-5600'],
+    emails: ['Aliquam.vulputate.ullamcorper@faucibusorci.edu']
+  },
+  {
+    id: '51',
+    displayName: 'Owen Noble',
+    name: {
+      familyName: ['Owen'],
+      givenName: ['Noble']
+    },
+    phones: ['1-463-693-1336'],
+    emails: ['et@vulputateveliteu.ca']
+  },
+  {
+    id: '54',
+    displayName: 'Kamal Blake',
+    name: {
+      familyName: ['Kamal'],
+      givenName: ['Blake']
+    },
+    phones: ['1-636-197-1985'],
+    emails: ['tempor@malesuada.edu']
+  },
+  {
+    id: '57',
+    displayName: 'Tyrone Delaney',
+    name: {
+      familyName: ['Tyrone'],
+      givenName: ['Delaney']
+    },
+    phones: ['1-886-920-6283'],
+    emails: ['est@aliquetsemut.com']
+  },
+  {
+    id: '60',
+    displayName: 'Ciaran Sellers',
+    name: {
+      familyName: ['Ciaran'],
+      givenName: ['Sellers']
+    },
+    phones: ['1-315-414-0323'],
+    emails: ['Etiam@Nulla.com']
+  },
+  {
+    id: '63',
+    displayName: 'Bernard Alford',
+    name: {
+      familyName: ['Bernard'],
+      givenName: ['Alford']
+    },
+    phones: ['1-430-958-2651'],
+    emails: ['elementum.lorem.ut@sociisnatoque.edu']
+  },
+  {
+    id: '66',
+    displayName: 'Kamal Cote',
+    name: {
+      familyName: ['Kamal'],
+      givenName: ['Cote']
+    },
+    phones: ['1-666-609-9141'],
+    emails: ['eleifend.egestas@cursus.edu']
+  },
+  {
+    id: '69',
+    displayName: 'Lucius Mckee',
+    name: {
+      familyName: ['Lucius'],
+      givenName: ['Mckee']
+    },
+    phones: ['1-224-590-6780'],
+    emails: ['Fusce.dolor@tellusnon.org']
+  },
+  {
+    id: '72',
+    displayName: 'Dale Coleman',
+    name: {
+      familyName: ['Dale'],
+      givenName: ['Coleman']
+    },
+    phones: ['1-320-245-3036'],
+    emails: ['dapibus.rutrum@ametlorem.org']
+  },
+  {
+    id: '75',
+    displayName: 'Kermit Nguyen',
+    name: {
+      familyName: ['Kermit'],
+      givenName: ['Nguyen']
+    },
+    phones: ['1-247-825-8563'],
+    emails: ['per@risusMorbi.org']
+  },
+  {
+    id: '78',
+    displayName: 'Timon Horton',
+    name: {
+      familyName: ['Timon'],
+      givenName: ['Horton']
+    },
+    phones: ['1-739-233-8981'],
+    emails: ['Etiam@nonummyultriciesornare.ca']
+  },
+  {
+    id: '81',
+    displayName: 'Dale Lamb',
+    name: {
+      familyName: ['Dale'],
+      givenName: ['Lamb']
+    },
+    phones: ['1-640-507-8295'],
+    emails: ['dapibus.id@pedeac.edu']
+  },
+  {
+    id: '84',
+    displayName: 'Owen Acevedo',
+    name: {
+      familyName: ['Owen'],
+      givenName: ['Acevedo']
+    },
+    phones: ['1-403-201-3170'],
+    emails: ['porttitor.tellus.non@dolorFusce.edu']
+  },
+  {
+    id: '87',
+    displayName: 'Richard Mckee',
+    name: {
+      familyName: ['Richard'],
+      givenName: ['Mckee']
+    },
+    phones: ['1-783-513-0684'],
+    emails: ['senectus.et.netus@Vestibulum.com']
+  },
+  {
+    id: '90',
+    displayName: 'Elijah Bass',
+    name: {
+      familyName: ['Elijah'],
+      givenName: ['Bass']
+    },
+    phones: ['1-632-950-0553'],
+    emails: ['erat@sapien.com']
+  },
+  {
+    id: '93',
+    displayName: 'Barrett Wells',
+    name: {
+      familyName: ['Barrett'],
+      givenName: ['Wells']
+    },
+    phones: ['1-112-180-5617'],
+    emails: ['interdum.ligula@varius.edu']
+  },
+  {
+    id: '96',
+    displayName: 'Herman Meyer',
+    name: {
+      familyName: ['Herman'],
+      givenName: ['Meyer']
+    },
+    phones: ['1-296-252-5507'],
+    emails: ['urna@vitaealiquameros.org']
+  },
+  {
+    id: '99',
+    displayName: 'Ashton Hinton',
+    name: {
+      familyName: ['Ashton'],
+      givenName: ['Hinton']
+    },
+    phones: ['1-695-256-8929'],
+    emails: ['lorem@mattisornare.org']
+  },
+  {
+    id: '102',
+    displayName: 'Harrison Marsh',
+    name: {
+      familyName: ['Harrison'],
+      givenName: ['Marsh']
+    },
+    phones: ['1-897-458-1730'],
+    emails: ['pharetra.felis.eget@auctor.com']
+  },
+  {
+    id: '105',
+    displayName: 'Benedict Santana',
+    name: {
+      familyName: ['Benedict'],
+      givenName: ['Santana']
+    },
+    phones: ['1-565-457-4828'],
+    emails: ['amet.metus.Aliquam@Maecenas.org']
+  },
+  {
+    id: '108',
+    displayName: 'David Church',
+    name: {
+      familyName: ['David'],
+      givenName: ['Church']
+    },
+    phones: ['1-179-353-3314'],
+    emails: ['Nullam.enim@Utsagittis.edu']
+  },
+  {
+    id: '111',
+    displayName: 'Colt Wolfe',
+    name: {
+      familyName: ['Colt'],
+      givenName: ['Wolfe']
+    },
+    phones: ['1-587-970-8581'],
+    emails: ['hendrerit.Donec.porttitor@tinciduntaliquam.org']
+  },
+  {
+    id: '114',
+    displayName: 'Carlos Bishop',
+    name: {
+      familyName: ['Carlos'],
+      givenName: ['Bishop']
+    },
+    phones: ['1-963-305-6702'],
+    emails: ['Nam@cursusNunc.org']
+  },
+  {
+    id: '117',
+    displayName: 'Dominic Ware',
+    name: {
+      familyName: ['Dominic'],
+      givenName: ['Ware']
+    },
+    phones: ['1-609-458-5449'],
+    emails: ['Fusce.aliquet@Etiam.ca']
+  },
+  {
+    id: '120',
+    displayName: 'Phillip Whitley',
+    name: {
+      familyName: ['Phillip'],
+      givenName: ['Whitley']
+    },
+    phones: ['1-284-955-1766'],
+    emails: ['per.inceptos.hymenaeos@nequesedsem.ca']
+  },
+  {
+    id: '123',
+    displayName: 'Valentine Sargent',
+    name: {
+      familyName: ['Valentine'],
+      givenName: ['Sargent']
+    },
+    phones: ['1-346-890-6417'],
+    emails: ['nec@dolorFusce.com']
+  },
+  {
+    id: '126',
+    displayName: 'Gabriel Huber',
+    name: {
+      familyName: ['Gabriel'],
+      givenName: ['Huber']
+    },
+    phones: ['1-399-465-0589'],
+    emails: ['pretium.neque@nislsemconsequat.ca']
+  },
+  {
+    id: '129',
+    displayName: 'George Tyler',
+    name: {
+      familyName: ['George'],
+      givenName: ['Tyler']
+    },
+    phones: ['1-739-571-2737'],
+    emails: ['blandit.viverra.Donec@dictum.ca']
+  },
+  {
+    id: '132',
+    displayName: 'Asher Carey',
+    name: {
+      familyName: ['Asher'],
+      givenName: ['Carey']
+    },
+    phones: ['1-477-425-4723'],
+    emails: ['torquent.per.conubia@blanditNamnulla.edu']
+  },
+  {
+    id: '135',
+    displayName: 'Anthony Solomon',
+    name: {
+      familyName: ['Anthony'],
+      givenName: ['Solomon']
+    },
+    phones: ['1-570-753-4296'],
+    emails: ['risus.Nunc@hendreritconsectetuercursus.com']
+  },
+  {
+    id: '138',
+    displayName: 'Griffith Fuller',
+    name: {
+      familyName: ['Griffith'],
+      givenName: ['Fuller']
+    },
+    phones: ['1-779-242-5342'],
+    emails: ['Suspendisse@aliquam.ca']
+  },
+  {
+    id: '141',
+    displayName: 'Beau Brewer',
+    name: {
+      familyName: ['Beau'],
+      givenName: ['Brewer']
+    },
+    phones: ['1-664-184-7334'],
+    emails: ['magna.tellus.faucibus@ultricesposuerecubilia.com']
+  },
+  {
+    id: '144',
+    displayName: 'Jordan Campbell',
+    name: {
+      familyName: ['Jordan'],
+      givenName: ['Campbell']
+    },
+    phones: ['1-593-938-2525'],
+    emails: ['Curae;.Phasellus@Morbiquis.ca']
+  },
+  {
+    id: '147',
+    displayName: 'Cyrus Cabrera',
+    name: {
+      familyName: ['Cyrus'],
+      givenName: ['Cabrera']
+    },
+    phones: ['1-915-748-1349'],
+    emails: ['lorem.tristique@acmetus.edu']
+  },
+  {
+    id: '150',
+    displayName: 'Hamilton Boone',
+    name: {
+      familyName: ['Hamilton'],
+      givenName: ['Boone']
+    },
+    phones: ['1-278-421-9845'],
+    emails: ['non.sapien@quamdignissimpharetra.edu']
+  },
+  {
+    id: '153',
+    displayName: 'Wallace Donovan',
+    name: {
+      familyName: ['Wallace'],
+      givenName: ['Donovan']
+    },
+    phones: ['1-940-175-9334'],
+    emails: ['justo@lacusMaurisnon.org']
+  },
+  {
+    id: '156',
+    displayName: 'Kirk Buckley',
+    name: {
+      familyName: ['Kirk'],
+      givenName: ['Buckley']
+    },
+    phones: ['1-283-177-6304'],
+    emails: ['Cras@Morbinon.edu']
+  },
+  {
+    id: '159',
+    displayName: 'Simon Hall',
+    name: {
+      familyName: ['Simon'],
+      givenName: ['Hall']
+    },
+    phones: ['1-269-202-5174'],
+    emails: ['mus.Proin@dolor.org']
+  },
+  {
+    id: '162',
+    displayName: 'Trevor Rush',
+    name: {
+      familyName: ['Trevor'],
+      givenName: ['Rush']
+    },
+    phones: ['1-865-595-9074'],
+    emails: ['Fusce@Donec.edu']
+  },
+  {
+    id: '165',
+    displayName: 'Todd Mccormick',
+    name: {
+      familyName: ['Todd'],
+      givenName: ['Mccormick']
+    },
+    phones: ['1-398-916-3514'],
+    emails: ['at@ornareelit.org']
+  },
+  {
+    id: '168',
+    displayName: 'Yuli Gay',
+    name: {
+      familyName: ['Yuli'],
+      givenName: ['Gay']
+    },
+    phones: ['1-198-196-4256'],
+    emails: ['Sed.congue.elit@Inornare.edu']
+  },
+  {
+    id: '171',
+    displayName: 'Joseph Frazier',
+    name: {
+      familyName: ['Joseph'],
+      givenName: ['Frazier']
+    },
+    phones: ['1-969-410-7180'],
+    emails: ['faucibus.ut.nulla@massa.org']
+  },
+  {
+    id: '174',
+    displayName: 'Ali Chase',
+    name: {
+      familyName: ['Ali'],
+      givenName: ['Chase']
+    },
+    phones: ['1-598-924-6112'],
+    emails: ['eu.elit@necanteMaecenas.edu']
+  },
+  {
+    id: '177',
+    displayName: 'Guy Simpson',
+    name: {
+      familyName: ['Guy'],
+      givenName: ['Simpson']
+    },
+    phones: ['1-558-377-3714'],
+    emails: ['in@mauriselit.edu']
+  },
+  {
+    id: '180',
+    displayName: 'Ivan Wynn',
+    name: {
+      familyName: ['Ivan'],
+      givenName: ['Wynn']
+    },
+    phones: ['1-274-885-0477'],
+    emails: ['lobortis.quis@Sed.com']
+  },
+  {
+    id: '183',
+    displayName: 'Preston Carpenter',
+    name: {
+      familyName: ['Preston'],
+      givenName: ['Carpenter']
+    },
+    phones: ['1-758-120-5270'],
+    emails: ['elit.Curabitur@vehiculaaliquet.edu']
+  },
+  {
+    id: '186',
+    displayName: 'Demetrius Santos',
+    name: {
+      familyName: ['Demetrius'],
+      givenName: ['Santos']
+    },
+    phones: ['1-913-961-7009'],
+    emails: ['id@magnaPhasellusdolor.com']
+  },
+  {
+    id: '189',
+    displayName: 'Dale Franklin',
+    name: {
+      familyName: ['Dale'],
+      givenName: ['Franklin']
+    },
+    phones: ['1-443-971-0116'],
+    emails: ['velit.Pellentesque@IntegerurnaVivamus.com']
+  },
+  {
+    id: '192',
+    displayName: 'Abraham Randolph',
+    name: {
+      familyName: ['Abraham'],
+      givenName: ['Randolph']
+    },
+    phones: ['1-368-169-0957'],
+    emails: ['egestas@maurisidsapien.com']
+  },
+  {
+    id: '195',
+    displayName: 'Hu Avila',
+    name: {
+      familyName: ['Hu'],
+      givenName: ['Avila']
+    },
+    phones: ['1-311-333-8877'],
+    emails: ['metus@adipiscinglacusUt.com']
+  },
+  {
+    id: '198',
+    displayName: 'Garth Trujillo',
+    name: {
+      familyName: ['Garth'],
+      givenName: ['Trujillo']
+    },
+    phones: ['1-409-494-1231'],
+    emails: ['commodo.hendrerit.Donec@etnunc.ca']
+  },
+  {
+    id: '201',
+    displayName: 'Quamar Buchanan',
+    name: {
+      familyName: ['Quamar'],
+      givenName: ['Buchanan']
+    },
+    phones: ['1-114-992-7225'],
+    emails: ['tellus@consequatpurusMaecenas.ca']
+  },
+  {
+    id: '204',
+    displayName: 'Ulysses Bishop',
+    name: {
+      familyName: ['Ulysses'],
+      givenName: ['Bishop']
+    },
+    phones: ['1-485-518-5941'],
+    emails: ['fermentum.fermentum.arcu@amalesuadaid.com']
+  },
+  {
+    id: '207',
+    displayName: 'Avram Knapp',
+    name: {
+      familyName: ['Avram'],
+      givenName: ['Knapp']
+    },
+    phones: ['1-307-139-5554'],
+    emails: ['est.ac.mattis@ultricesmauris.ca']
+  },
+  {
+    id: '210',
+    displayName: 'Conan Grant',
+    name: {
+      familyName: ['Conan'],
+      givenName: ['Grant']
+    },
+    phones: ['1-331-936-0280'],
+    emails: ['turpis@odio.com']
+  },
+  {
+    id: '213',
+    displayName: 'Chester Kemp',
+    name: {
+      familyName: ['Chester'],
+      givenName: ['Kemp']
+    },
+    phones: ['1-554-119-4848'],
+    emails: ['Aenean.gravida.nunc@eu.org']
+  },
+  {
+    id: '216',
+    displayName: 'Hedley Dudley',
+    name: {
+      familyName: ['Hedley'],
+      givenName: ['Dudley']
+    },
+    phones: ['1-578-607-6287'],
+    emails: ['Nunc@dignissimtemporarcu.ca']
+  },
+  {
+    id: '219',
+    displayName: 'Jermaine Avila',
+    name: {
+      familyName: ['Jermaine'],
+      givenName: ['Avila']
+    },
+    phones: ['1-860-455-2283'],
+    emails: ['accumsan@ametdapibusid.ca']
+  },
+  {
+    id: '222',
+    displayName: 'Kamal Hamilton',
+    name: {
+      familyName: ['Kamal'],
+      givenName: ['Hamilton']
+    },
+    phones: ['1-650-389-0920'],
+    emails: ['Fusce.dolor@nuncsed.ca']
+  },
+  {
+    id: '225',
+    displayName: 'Castor Maxwell',
+    name: {
+      familyName: ['Castor'],
+      givenName: ['Maxwell']
+    },
+    phones: ['1-260-489-7135'],
+    emails: ['diam.lorem@a.ca']
+  },
+  {
+    id: '228',
+    displayName: 'Lyle Burris',
+    name: {
+      familyName: ['Lyle'],
+      givenName: ['Burris']
+    },
+    phones: ['1-250-343-2038'],
+    emails: ['eget.lacus@tempordiamdictum.com']
+  },
+  {
+    id: '231',
+    displayName: 'Merrill Dalton',
+    name: {
+      familyName: ['Merrill'],
+      givenName: ['Dalton']
+    },
+    phones: ['1-851-675-1381'],
+    emails: ['eu.tempor@blanditmattisCras.edu']
+  },
+  {
+    id: '234',
+    displayName: 'Ezekiel Medina',
+    name: {
+      familyName: ['Ezekiel'],
+      givenName: ['Medina']
+    },
+    phones: ['1-389-582-3443'],
+    emails: ['lectus.sit@interdum.ca']
+  },
+  {
+    id: '237',
+    displayName: 'Len Tran',
+    name: {
+      familyName: ['Len'],
+      givenName: ['Tran']
+    },
+    phones: ['1-434-573-6114'],
+    emails: ['turpis.Aliquam.adipiscing@montesnasceturridiculus.com']
+  },
+  {
+    id: '240',
+    displayName: 'Len Dominguez',
+    name: {
+      familyName: ['Len'],
+      givenName: ['Dominguez']
+    },
+    phones: ['1-144-489-7487'],
+    emails: ['augue@Innec.ca']
+  },
+  {
+    id: '243',
+    displayName: 'Paul Lane',
+    name: {
+      familyName: ['Paul'],
+      givenName: ['Lane']
+    },
+    phones: ['1-448-169-4312'],
+    emails: ['lectus.Cum.sociis@dolornonummyac.org']
+  },
+  {
+    id: '246',
+    displayName: 'Eric Horne',
+    name: {
+      familyName: ['Eric'],
+      givenName: ['Horne']
+    },
+    phones: ['1-124-862-6890'],
+    emails: ['commodo.tincidunt.nibh@eleifendnuncrisus.com']
+  },
+  {
+    id: '249',
+    displayName: 'Elton Ellis',
+    name: {
+      familyName: ['Elton'],
+      givenName: ['Ellis']
+    },
+    phones: ['1-492-834-0019'],
+    emails: ['lorem.eu.metus@felis.ca']
+  },
+  {
+    id: '252',
+    displayName: 'Jameson Snyder',
+    name: {
+      familyName: ['Jameson'],
+      givenName: ['Snyder']
+    },
+    phones: ['1-811-590-5893'],
+    emails: ['fermentum@Nuncmaurissapien.org']
+  },
+  {
+    id: '255',
+    displayName: 'Micah Shelton',
+    name: {
+      familyName: ['Micah'],
+      givenName: ['Shelton']
+    },
+    phones: ['1-402-504-4026'],
+    emails: ['Nunc.mauris@malesuada.ca']
+  },
+  {
+    id: '258',
+    displayName: 'Evan Lester',
+    name: {
+      familyName: ['Evan'],
+      givenName: ['Lester']
+    },
+    phones: ['1-535-915-3570'],
+    emails: ['libero@adipiscingfringillaporttitor.org']
+  },
+  {
+    id: '261',
+    displayName: 'Reuben Dalton',
+    name: {
+      familyName: ['Reuben'],
+      givenName: ['Dalton']
+    },
+    phones: ['1-296-598-2504'],
+    emails: ['tincidunt.vehicula.risus@Craseutellus.com']
+  },
+  {
+    id: '264',
+    displayName: 'Beau Baird',
+    name: {
+      familyName: ['Beau'],
+      givenName: ['Baird']
+    },
+    phones: ['1-525-882-9957'],
+    emails: ['urna.suscipit.nonummy@facilisisvitae.com']
+  },
+  {
+    id: '267',
+    displayName: 'Hedley Olsen',
+    name: {
+      familyName: ['Hedley'],
+      givenName: ['Olsen']
+    },
+    phones: ['1-945-295-5863'],
+    emails: ['vulputate.ullamcorper@Vivamusnisi.org']
+  },
+  {
+    id: '270',
+    displayName: 'Oliver Todd',
+    name: {
+      familyName: ['Oliver'],
+      givenName: ['Todd']
+    },
+    phones: ['1-551-447-1296'],
+    emails: ['Donec.egestas@rutrum.edu']
+  },
+  {
+    id: '273',
+    displayName: 'Keegan Mayo',
+    name: {
+      familyName: ['Keegan'],
+      givenName: ['Mayo']
+    },
+    phones: ['1-351-848-2796'],
+    emails: ['ridiculus@Nuncsed.ca']
+  },
+  {
+    id: '276',
+    displayName: 'Wang Cote',
+    name: {
+      familyName: ['Wang'],
+      givenName: ['Cote']
+    },
+    phones: ['1-439-568-2013'],
+    emails: ['Morbi@tinciduntduiaugue.org']
+  },
+  {
+    id: '279',
+    displayName: 'Hyatt Rowe',
+    name: {
+      familyName: ['Hyatt'],
+      givenName: ['Rowe']
+    },
+    phones: ['1-596-765-3807'],
+    emails: ['eu.erat.semper@enimnonnisi.com']
+  },
+  {
+    id: '282',
+    displayName: 'Cade Wyatt',
+    name: {
+      familyName: ['Cade'],
+      givenName: ['Wyatt']
+    },
+    phones: ['1-988-289-5924'],
+    emails: ['erat.nonummy@sedpedeCum.com']
+  },
+  {
+    id: '285',
+    displayName: 'Stephen Vincent',
+    name: {
+      familyName: ['Stephen'],
+      givenName: ['Vincent']
+    },
+    phones: ['1-954-435-1259'],
+    emails: ['nec.euismod@ultricies.ca']
+  },
+  {
+    id: '288',
+    displayName: 'Tobias Cherry',
+    name: {
+      familyName: ['Tobias'],
+      givenName: ['Cherry']
+    },
+    phones: ['1-270-763-1111'],
+    emails: ['Nulla.aliquet@sit.com']
+  },
+  {
+    id: '291',
+    displayName: 'Keane Trevino',
+    name: {
+      familyName: ['Keane'],
+      givenName: ['Trevino']
+    },
+    phones: ['1-794-929-8599'],
+    emails: ['sem.semper.erat@Aliquamnecenim.edu']
+  },
+  {
+    id: '294',
+    displayName: 'Kennedy Cooley',
+    name: {
+      familyName: ['Kennedy'],
+      givenName: ['Cooley']
+    },
+    phones: ['1-725-946-1901'],
+    emails: ['urna.justo@Duismienim.edu']
+  },
+  {
+    id: '297',
+    displayName: 'Lucian Pope',
+    name: {
+      familyName: ['Lucian'],
+      givenName: ['Pope']
+    },
+    phones: ['1-186-946-8356'],
+    emails: ['justo.Proin@dis.com']
+  },
+  {
+    id: '300',
+    displayName: 'Hu Combs',
+    name: {
+      familyName: ['Hu'],
+      givenName: ['Combs']
+    },
+    phones: ['1-398-488-5222'],
+    emails: ['faucibus.lectus@nuncsedpede.com']
+  }
+];
+
+var ContactsManager = {
+  contacts: contacts,
+  find: function contactsManager(fields, successCallback, errorCallback) {
+    var contacts = this.contacts.slice();
+    successCallback(contacts);
+  },
+  create: function contactsCreate(successCallback, errorCallback, contact) {
+    this.contacts.push(contact);
+    successCallback();
+  },
+  delete: function contactsDelete(successCallback, errorCallback, id) {
+    var count = contacts.length;
+    for (var i = 0; i < count; i++) {
+      if (contacts[i].id != id)
+        continue;
+      var oldContact = contacts.slice(i, 1);
+      successCallback(oldContact);
+      return;
+    }
+    errorCallback();
+  }
+};
+
+let { classes: Cc, interfaces: Ci, utils: Cu } = Components;
+
+Cu.import('resource://gre/modules/Geometry.jsm');
+Cu.import('resource://gre/modules/Services.jsm');
+
+const ContentPanning = {
+  init: function cp_init() {
+    ['mousedown', 'mouseup', 'mousemove'].forEach(function(type) {
+      addEventListener(type, ContentPanning, true);
+    });
+  },
+
+  handleEvent: function cp_handleEvent(evt) {
+    switch (evt.type) {
+      case 'mousedown':
+        this.onTouchStart(evt);
+        break;
+      case 'mousemove':
+        this.onTouchMove(evt);
+        break;
+      case 'mouseup':
+        this.onTouchEnd(evt);
+        break;
+      case 'click':
+        evt.stopPropagation();
+        evt.preventDefault();
+        evt.target.removeEventListener('click', this, true);
+        break;
+    }
+  },
+
+  position: {
+    origin: new Point(0, 0),
+    current: new Point(0 , 0)
+  },
+
+  onTouchStart: function cp_onTouchStart(evt) {
+    this.dragging = true;
+    KineticPanning.stop();
+
+    this.scrollCallback = this.getPannable(evt.originalTarget);
+    this.position.origin.set(evt.screenX, evt.screenY);
+    this.position.current.set(evt.screenX, evt.screenY);
+    KineticPanning.record(new Point(0, 0));
+  },
+
+  onTouchEnd: function cp_onTouchEnd(evt) {
+    if (!this.dragging)
+      return;
+    this.dragging = false;
+
+    if (this.isPan()) {
+      if (evt.detail) // The event will generate a click
+        evt.target.addEventListener('click', this, true);
+
+      KineticPanning.start(this);
+    }
+  },
+
+  onTouchMove: function cp_onTouchMove(evt) {
+    if (!this.dragging || !this.scrollCallback)
+      return;
+
+    let current = this.position.current;
+    let delta = new Point(evt.screenX - current.x, evt.screenY - current.y);
+    current.set(evt.screenX, evt.screenY);
+
+    if (this.isPan()) {
+      KineticPanning.record(delta);
+      this.scrollCallback(delta.scale(-1));
+    }
+  },
+
+
+  onKineticBegin: function cp_onKineticBegin(evt) {
+  },
+
+  onKineticPan: function cp_onKineticPan(delta) {
+    return !this.scrollCallback(delta);
+  },
+
+  onKineticEnd: function cp_onKineticEnd() {
+    if (!this.dragging)
+      this.scrollCallback = null;
+  },
+
+  isPan: function cp_isPan() {
+    let dpi = content.QueryInterface(Ci.nsIInterfaceRequestor)
+                     .getInterface(Ci.nsIDOMWindowUtils)
+                     .displayDPI;
+
+    let threshold = Services.prefs.getIntPref('ui.dragThresholdX') / 240 * dpi;
+
+    let deltaX = this.position.origin.x - this.position.current.x;
+    let deltaY = this.position.origin.y - this.position.current.y;
+    return (Math.abs(deltaX) > threshold || Math.abs(deltaY) > threshold);
+  },
+
+  getPannable: function cp_getPannable(node) {
+    if (!(node instanceof Ci.nsIDOMHTMLElement) || node.tagName == 'HTML')
+      return null;
+
+    let content = node.ownerDocument.defaultView;
+
+    while (!(node instanceof Ci.nsIDOMHTMLBodyElement)) {
+      let style = content.getComputedStyle(node, null);
+
+      let overflow = [style.getPropertyValue('overflow'),
+                      style.getPropertyValue('overflow-x'),
+                      style.getPropertyValue('overflow-y')];
+
+      let rect = node.getBoundingClientRect();
+      let isAuto = (overflow.indexOf('auto') != -1 &&
+                   (rect.height < node.scrollHeight ||
+                    rect.width < node.scrollWidth));
+
+      let isScroll = (overflow.indexOf('scroll') != -1);
+      if (isScroll || isAuto)
+        return this._generateCallback(node);
+
+      node = node.parentNode;
+    }
+
+    return this._generateCallback(content);
+  },
+
+  _generateCallback: function cp_generateCallback(content) {
+    function scroll(delta) {
+      if (content instanceof Ci.nsIDOMHTMLElement) {
+        let oldX = content.scrollLeft, oldY = content.scrollTop;
+        content.scrollLeft += delta.x;
+        content.scrollTop += delta.y;
+        let newX = content.scrollLeft, newY = content.scrollTop;
+        return (newX != oldX) || (newY != oldY);
+      } else {
+        let oldX = content.scrollX, oldY = content.scrollY;
+        content.scrollBy(delta.x, delta.y);
+        let newX = content.scrollX, newY = content.scrollY;
+        return (newX != oldX) || (newY != oldY);
+      }
+    }
+    return scroll;
+  }
+};
+
+ContentPanning.init();
+
+
+// Min/max velocity of kinetic panning. This is in pixels/millisecond.
+const kMinVelocity = 0.4;
+const kMaxVelocity = 6;
+
+// Constants that affect the "friction" of the scroll pane.
+const kExponentialC = 1400;
+const kPolynomialC = 100 / 1000000;
+
+// How often do we change the position of the scroll pane?
+// Too often and panning may jerk near the end.
+// Too little and panning will be choppy. In milliseconds.
+const kUpdateInterval = 16;
+
+const KineticPanning = {
+  _position: new Point(0, 0),
+  _velocity: new Point(0, 0),
+  _acceleration: new Point(0, 0),
+
+  _target: null,
+  start: function kp_start(target) {
+    this.target = target;
+
+    // Calculate the initial velocity of the movement based on user input
+    let momentums = this.momentums;
+
+    let distance = new Point(0, 0);
+    momentums.forEach(function(momentum) {
+      distance.add(momentum.dx, momentum.dy);
+    });
+
+    let elapsed = momentums[momentums.length - 1].time - momentums[0].time;
+
+    function clampFromZero(x, min, max) {
+      if (x >= 0)
+        return Math.max(min, Math.min(max, x));
+      return Math.min(-min, Math.max(-max, x));
+    }
+
+    let velocityX = clampFromZero(distance.x / elapsed, 0, kMaxVelocity);
+    let velocityY = clampFromZero(distance.y / elapsed, 0, kMaxVelocity);
+
+    let velocity = this._velocity;
+    velocity.set(Math.abs(velocityX) < kMinVelocity ? 0 : velocityX,
+                 Math.abs(velocityY) < kMinVelocity ? 0 : velocityY);
+
+    // Set acceleration vector to opposite signs of velocity
+    function sign(x) {
+      return x ? (x > 0 ? 1 : -1) : 0;
+    }
+
+    this._acceleration.set(velocity.clone().map(sign).scale(-kPolynomialC));
+
+    // Reset the position
+    this._position.set(0, 0);
+
+    this._startAnimation();
+
+    this.target.onKineticBegin();
+  },
+
+  stop: function kp_stop() {
+    if (!this.target)
+      return;
+
+    this.momentums.splice(0);
+
+    this.target.onKineticEnd();
+    this.target = null;
+  },
+
+  momentums: [],
+  record: function kp_record(delta) {
+    // If the panning direction has changed, stop the current activity.
+    if (this.target && ((delta.x * this._velocity.x < 0) ||
+                        (delta.y * this._velocity.y < 0)))
+      this.stop();
+
+    this.momentums.push({ 'time': Date.now(), 'dx' : delta.x, 'dy' : delta.y });
+  },
+
+  _startAnimation: function kp_startAnimation() {
+    let c = kExponentialC;
+    function getNextPosition(position, v, a, t) {
+      // Important traits for this function:
+      //   p(t=0) is 0
+      //   p'(t=0) is v0
+      //
+      // We use exponential to get a smoother stop, but by itself exponential
+      // is too smooth at the end. Adding a polynomial with the appropriate
+      // weight helps to balance
+      position.set(v.x * Math.exp(-t / c) * -c + a.x * t * t + v.x * c,
+                   v.y * Math.exp(-t / c) * -c + a.y * t * t + v.y * c);
+    }
+
+    let startTime = content.mozAnimationStartTime;
+    let elapsedTime = 0, targetedTime = 0, averageTime = 0;
+
+    let velocity = this._velocity;
+    let acceleration = this._acceleration;
+
+    let position = this._position;
+    let nextPosition = new Point(0, 0);
+    let delta = new Point(0, 0);
+
+    let callback = (function(timestamp) {
+      if (!this.target)
+        return;
+
+      // To make animation end fast enough but to keep smoothness, average the
+      // ideal time frame (smooth animation) with the actual time lapse
+      // (end fast enough).
+      // Animation will never take longer than 2 times the ideal length of time.
+      elapsedTime = timestamp - startTime;
+      targetedTime += kUpdateInterval;
+      averageTime = (targetedTime + elapsedTime) / 2;
+
+      // Calculate new position.
+      getNextPosition(nextPosition, velocity, acceleration, averageTime);
+      delta.set(Math.round(nextPosition.x - position.x),
+                Math.round(nextPosition.y - position.y));
+
+      // Test to see if movement is finished for each component.
+      if (delta.x * acceleration.x > 0)
+        delta.x = position.x = velocity.x = acceleration.x = 0;
+
+      if (delta.y * acceleration.y > 0)
+        delta.y = position.y = velocity.y = acceleration.y = 0;
+
+      if (velocity.equals(0, 0) || delta.equals(0, 0)) {
+        this.stop();
+        return;
+      }
+
+      position.add(delta);
+      if (this.target.onKineticPan(delta.scale(-1))) {
+        this.stop();
+        return;
+      }
+
+      content.mozRequestAnimationFrame(callback);
+    }).bind(this);
+
+    content.mozRequestAnimationFrame(callback);
+  }
+};
+
--- a/b2g/chrome/jar.mn
+++ b/b2g/chrome/jar.mn
@@ -4,12 +4,13 @@ chrome.jar:
 % content branding %content/branding/
 % content browser %content/
 
 * content/shell.xul                     (content/shell.xul)
   content/shell.js                      (content/shell.js)
   content/touch.js                      (content/touch.js)
   content/commandUtil.js                (content/commandUtil.js)
   content/httpd.js                      (content/httpd.js)
+  content/webapi.js                     (content/webapi.js)
   content/content.css                   (content/content.css)
 
 % override chrome://global/content/netError.xhtml chrome://browser/content/netError.xhtml
   content/netError.xhtml                (content/netError.xhtml)
--- a/b2g/confvars.sh
+++ b/b2g/confvars.sh
@@ -33,25 +33,27 @@
 # 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 *****
 
 MOZ_APP_BASENAME=B2G
 MOZ_APP_VENDOR=Mozilla
 
-MOZ_APP_VERSION=11.0a1
+MOZ_APP_VERSION=13.0a1
+MOZ_APP_UA_NAME=Firefox
 
 MOZ_BRANDING_DIRECTORY=b2g/branding/unofficial
 MOZ_OFFICIAL_BRANDING_DIRECTORY=b2g/branding/official
 # MOZ_APP_DISPLAYNAME is set by branding/configure.sh
 
 MOZ_SAFE_BROWSING=
 MOZ_SERVICES_SYNC=
 
+MOZ_WEBSMS_BACKEND=1
 MOZ_DISABLE_DOMCRYPTO=1
 MOZ_APP_STATIC_INI=1
 
 if test "$OS_TARGET" = "Android"; then
 MOZ_CAPTURE=1
 MOZ_RAW=1
 fi
 
--- a/b2g/installer/package-manifest.in
+++ b/b2g/installer/package-manifest.in
@@ -157,16 +157,17 @@
 @BINPATH@/components/dom_events.xpt
 @BINPATH@/components/dom_geolocation.xpt
 @BINPATH@/components/dom_network.xpt
 @BINPATH@/components/dom_notification.xpt
 @BINPATH@/components/dom_html.xpt
 @BINPATH@/components/dom_indexeddb.xpt
 @BINPATH@/components/dom_offline.xpt
 @BINPATH@/components/dom_json.xpt
+@BINPATH@/components/dom_power.xpt
 @BINPATH@/components/dom_range.xpt
 @BINPATH@/components/dom_sidebar.xpt
 @BINPATH@/components/dom_sms.xpt
 @BINPATH@/components/dom_storage.xpt
 @BINPATH@/components/dom_stylesheets.xpt
 @BINPATH@/components/dom_threads.xpt
 @BINPATH@/components/dom_traversal.xpt
 @BINPATH@/components/dom_views.xpt
--- a/browser/app/Makefile.in
+++ b/browser/app/Makefile.in
@@ -103,21 +103,16 @@ include $(topsrcdir)/config/config.mk
 
 ifdef _MSC_VER
 # Always enter a Windows program through wmain, whether or not we're
 # a console application.
 WIN32_EXE_LDFLAGS += -ENTRY:wmainCRTStartup
 endif
 
 ifeq ($(OS_ARCH),WINNT)
-OS_LIBS += $(call EXPAND_LIBNAME,comctl32 comdlg32 uuid shell32 ole32 oleaut32 version winspool)
-OS_LIBS += $(call EXPAND_LIBNAME,usp10 msimg32)
-endif
-
-ifeq ($(OS_ARCH),WINNT)
 RCINCLUDE = splash.rc
 ifndef GNU_CC
 RCFLAGS += -DMOZ_PHOENIX -I$(srcdir)
 else
 RCFLAGS += -DMOZ_PHOENIX --include-dir $(srcdir)
 endif
 ifdef DEBUG
 RCFLAGS += -DDEBUG
--- a/browser/app/blocklist.xml
+++ b/browser/app/blocklist.xml
@@ -1,11 +1,15 @@
 <?xml version="1.0"?>
-<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist" lastupdate="1327685994000">
+<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist" lastupdate="1328289666000">
   <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>
                   </emItem>
       <emItem  blockID="i8" id="{B13721C7-F507-4982-B2E5-502A71474FED}">
                         <versionRange  minVersion=" " severity="1">
                     </versionRange>
                   </emItem>
@@ -38,18 +42,20 @@
       <emItem  blockID="i39" id="{c2d64ff7-0ab8-4263-89c9-ea3b0f8f050c}">
                         <versionRange  minVersion="0.1" maxVersion="4.3.1.00" severity="1">
                     </versionRange>
                   </emItem>
       <emItem  blockID="i42" id="{D19CA586-DD6C-4a0a-96F8-14644F340D60}">
                         <versionRange  minVersion="0.1" maxVersion="14.4.0" severity="1">
                     </versionRange>
                   </emItem>
-      <emItem  blockID="i53" id="{a3a5c777-f583-4fef-9380-ab4add1bc2a8}">
-                        <versionRange  minVersion="2.0.3" maxVersion="2.0.3">
+      <emItem  blockID="i61" id="youtube@youtube3.com">
+                        <versionRange  minVersion="0" maxVersion="*">
+                    </versionRange>
+                                <versionRange  minVersion="0" maxVersion="*">
                     </versionRange>
                   </emItem>
       <emItem  blockID="i10" id="{8CE11043-9A15-4207-A565-0C94C42D590D}">
                         </emItem>
       <emItem  blockID="i1" id="mozilla_cc@internetdownloadmanager.com">
                         <versionRange  minVersion="2.1" maxVersion="3.3">
                       <targetApplication  id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
                               <versionRange  minVersion="3.0a1" maxVersion="*" />
@@ -80,40 +86,54 @@
                               <versionRange  minVersion="9.0a1" maxVersion="9.*" />
                           </targetApplication>
                     </versionRange>
                   </emItem>
       <emItem  blockID="i40" id="{28387537-e3f9-4ed7-860c-11e69af4a8a0}">
                         <versionRange  minVersion="0.1" maxVersion="4.3.1.00" severity="1">
                     </versionRange>
                   </emItem>
+      <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="*">
                     </versionRange>
                   </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="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="*" />
-                          </targetApplication>
+      <emItem  blockID="i60" id="youtb3@youtb3.com">
+                        <versionRange  minVersion="0" maxVersion="*">
+                    </versionRange>
+                  </emItem>
+      <emItem  blockID="i56" id="flash@adobe.com">
+                        <versionRange  minVersion="0" maxVersion="*">
                     </versionRange>
                   </emItem>
       <emItem  blockID="i55" id="youtube@youtube7.com">
                         <versionRange  minVersion="0" maxVersion="*">
                     </versionRange>
                   </emItem>
-      <emItem  blockID="i47" id="youtube@youtube2.com">
-                        </emItem>
+      <emItem  blockID="i11" id="yslow@yahoo-inc.com">
+                        <versionRange  minVersion="2.0.5" maxVersion="2.0.5">
+                      <targetApplication  id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
+                              <versionRange  minVersion="3.5.7" maxVersion="*" />
+                          </targetApplication>
+                    </versionRange>
+                  </emItem>
       <emItem  blockID="i22" id="ShopperReports@ShopperReports.com">
                         <versionRange  minVersion="3.1.22.0" maxVersion="3.1.22.0">
                     </versionRange>
                   </emItem>
       <emItem  blockID="i2" id="fdm_ffext@freedownloadmanager.org">
                         <versionRange  minVersion="1.0" maxVersion="1.3.1">
                       <targetApplication  id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
                               <versionRange  minVersion="3.0a1" maxVersion="*" />
@@ -135,25 +155,27 @@
                     </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="i11" id="yslow@yahoo-inc.com">
-                        <versionRange  minVersion="2.0.5" maxVersion="2.0.5">
-                      <targetApplication  id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
-                              <versionRange  minVersion="3.5.7" maxVersion="*" />
-                          </targetApplication>
+      <emItem  blockID="i47" id="youtube@youtube2.com">
+                        </emItem>
+      <emItem  blockID="i17" id="{3252b9ae-c69a-4eaf-9502-dc9c1f6c009e}">
+                        <versionRange  minVersion="2.2" maxVersion="2.2">
                     </versionRange>
                   </emItem>
-      <emItem  blockID="i17" id="{3252b9ae-c69a-4eaf-9502-dc9c1f6c009e}">
-                        <versionRange  minVersion="2.2" maxVersion="2.2">
+      <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="*" />
+                          </targetApplication>
                     </versionRange>
                   </emItem>
       <emItem  blockID="i45" id="{22119944-ED35-4ab1-910B-E619EA06A115}">
                         <versionRange  minVersion="0.1" maxVersion="7.6.1">
                       <targetApplication  id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
                               <versionRange  minVersion="8.0a1" maxVersion="*" />
                           </targetApplication>
                     </versionRange>
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -60,16 +60,17 @@ pref("extensions.strictCompatibility", f
 // Specifies a minimum maxVersion an addon needs to say it's compatible with
 // for it to be compatible by default.
 pref("extensions.minCompatibleAppVersion", "4.0");
 
 // Preferences for AMO integration
 pref("extensions.getAddons.cache.enabled", true);
 pref("extensions.getAddons.maxResults", 15);
 pref("extensions.getAddons.get.url", "https://services.addons.mozilla.org/%LOCALE%/firefox/api/%API_VERSION%/search/guid:%IDS%?src=firefox&appOS=%OS%&appVersion=%VERSION%");
+pref("extensions.getAddons.getWithPerformance.url", "https://services.addons.mozilla.org/%LOCALE%/firefox/api/%API_VERSION%/search/guid:%IDS%?src=firefox&appOS=%OS%&appVersion=%VERSION%&tMain=%TIME_MAIN%&tFirstPaint=%TIME_FIRST_PAINT%&tSessionRestored=%TIME_SESSION_RESTORED%");
 pref("extensions.getAddons.search.browseURL", "https://addons.mozilla.org/%LOCALE%/firefox/search?q=%TERMS%");
 pref("extensions.getAddons.search.url", "https://services.addons.mozilla.org/%LOCALE%/firefox/api/%API_VERSION%/search/%TERMS%/all/%MAX_RESULTS%/%OS%/%VERSION%/%COMPATIBILITY_MODE%?src=firefox");
 pref("extensions.webservice.discoverURL", "https://services.addons.mozilla.org/%LOCALE%/firefox/discovery/pane/%VERSION%/%OS%/%COMPATIBILITY_MODE%");
 
 // Blocklist preferences
 pref("extensions.blocklist.enabled", true);
 pref("extensions.blocklist.interval", 86400);
 // Controls what level the blocklist switches from warning about items to forcibly
@@ -275,30 +276,30 @@ pref("browser.urlbar.clickSelectsAll", f
 #else
 pref("browser.urlbar.clickSelectsAll", true);
 #endif
 #ifdef UNIX_BUT_NOT_MAC
 pref("browser.urlbar.doubleClickSelectsAll", true);
 #else
 pref("browser.urlbar.doubleClickSelectsAll", false);
 #endif
-pref("browser.urlbar.autoFill", true);
+pref("browser.urlbar.autoFill", false);
 // 0: Match anywhere (e.g., middle of words)
 // 1: Match on word boundaries and then try matching anywhere
 // 2: Match only on word boundaries (e.g., after / or .)
 // 3: Match at the beginning of the url or title
 pref("browser.urlbar.matchBehavior", 1);
 pref("browser.urlbar.filter.javascript", true);
 
 // the maximum number of results to show in autocomplete when doing richResults
 pref("browser.urlbar.maxRichResults", 12);
 // The amount of time (ms) to wait after the user has stopped typing
 // before starting to perform autocomplete.  50 is the default set in
 // autocomplete.xml.
-pref("browser.urlbar.delay", 0);
+pref("browser.urlbar.delay", 50);
 
 // The special characters below can be typed into the urlbar to either restrict
 // the search to visited history, bookmarked, tagged pages; or force a match on
 // just the title text or url.
 pref("browser.urlbar.restrict.history", "^");
 pref("browser.urlbar.restrict.bookmark", "*");
 pref("browser.urlbar.restrict.tag", "+");
 pref("browser.urlbar.restrict.openpage", "%");
@@ -1106,15 +1107,15 @@ pref("devtools.editor.component", "orion
 pref("browser.menu.showCharacterEncoding", "chrome://browser/locale/browser.properties");
 
 // Allow using tab-modal prompts when possible.
 pref("prompts.tab_modal.enabled", true);
 // Whether the Panorama should animate going in/out of tabs
 pref("browser.panorama.animate_zoom", true);
 
 // Defines the url to be used for new tabs.
-pref("browser.newtab.url", "about:blank");
+pref("browser.newtab.url", "about:newtab");
 
 // Toggles the content of 'about:newtab'. Shows the grid when enabled.
-pref("browser.newtabpage.enabled", false);
+pref("browser.newtabpage.enabled", true);
 
 // Enable the DOM full-screen API.
 pref("full-screen-api.enabled", true);
--- a/browser/base/Makefile.in
+++ b/browser/base/Makefile.in
@@ -44,19 +44,17 @@ VPATH   = @srcdir@
 include $(DEPTH)/config/autoconf.mk
 
 include $(topsrcdir)/config/config.mk
 
 abs_srcdir = $(call core_abspath,$(srcdir))
 
 CHROME_DEPS += $(abs_srcdir)/content/overrides/app-license.html
 
-ifdef ENABLE_TESTS
-DIRS += content/test
-endif
+TEST_DIRS += content/test
 
 include $(topsrcdir)/config/rules.mk
 
 PRE_RELEASE_SUFFIX := ""
 
 DEFINES += \
 	-DMOZ_APP_VERSION=$(MOZ_APP_VERSION) \
 	-DAPP_LICENSE_BLOCK=$(abs_srcdir)/content/overrides/app-license.html \
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -132,18 +132,19 @@ XPCOMUtils.defineLazyGetter(window, "gFi
 });
 
 __defineGetter__("gPrefService", function() {
   delete this.gPrefService;
   return this.gPrefService = Services.prefs;
 });
 
 __defineGetter__("AddonManager", function() {
-  Cu.import("resource://gre/modules/AddonManager.jsm");
-  return this.AddonManager;
+  let tmp = {};
+  Cu.import("resource://gre/modules/AddonManager.jsm", tmp);
+  return this.AddonManager = tmp.AddonManager;
 });
 __defineSetter__("AddonManager", function (val) {
   delete this.AddonManager;
   return this.AddonManager = val;
 });
 
 __defineGetter__("PluralForm", function() {
   Cu.import("resource://gre/modules/PluralForm.jsm");
@@ -1498,17 +1499,19 @@ function prepareForStartup() {
   gBrowser.addEventListener("MozApplicationManifest",
                             OfflineApps, false);
 
   // setup simple gestures support
   gGestureSupport.init(true);
 }
 
 function delayedStartup(isLoadingBlank, mustLoadSidebar) {
-  Cu.import("resource:///modules/TelemetryTimestamps.jsm");
+  let tmp = {};
+  Cu.import("resource:///modules/TelemetryTimestamps.jsm", tmp);
+  let TelemetryTimestamps = tmp.TelemetryTimestamps;
   TelemetryTimestamps.add("delayedStartupStarted");
   gDelayedStartupTimeoutId = null;
 
   Services.obs.addObserver(gSessionHistoryObserver, "browser:purge-session-history", false);
   Services.obs.addObserver(gXPInstallObserver, "addon-install-disabled", false);
   Services.obs.addObserver(gXPInstallObserver, "addon-install-started", false);
   Services.obs.addObserver(gXPInstallObserver, "addon-install-blocked", false);
   Services.obs.addObserver(gXPInstallObserver, "addon-install-failed", false);
@@ -1761,16 +1764,33 @@ function delayedStartup(isLoadingBlank, 
   // If the user (or the locale) hasn't enabled the top-level "Character
   // Encoding" menu via the "browser.menu.showCharacterEncoding" preference,
   // hide it.
   if ("true" != gPrefService.getComplexValue("browser.menu.showCharacterEncoding",
                                              Ci.nsIPrefLocalizedString).data)
     document.getElementById("appmenu_charsetMenu").hidden = true;
 #endif
 
+  let appMenuButton = document.getElementById("appmenu-button");
+  let appMenuPopup = document.getElementById("appmenu-popup");
+  if (appMenuButton && appMenuPopup) {
+    let appMenuOpening = null;
+    appMenuButton.addEventListener("mousedown", function(event) {
+      if (event.button == 0)
+        appMenuOpening = new Date();
+    }, false);
+    appMenuPopup.addEventListener("popupshown", function(event) {
+      if (event.target != appMenuPopup || !appMenuOpening)
+        return;
+      let duration = new Date() - appMenuOpening;
+      appMenuOpening = null;
+      Services.telemetry.getHistogramById("FX_APP_MENU_OPEN_MS").add(duration);
+    }, false);
+  }
+
   window.addEventListener("mousemove", MousePosTracker, false);
   window.addEventListener("dragover", MousePosTracker, false);
 
   Services.obs.notifyObservers(window, "browser-delayed-startup-finished", "");
   TelemetryTimestamps.add("delayedStartupFinished");
 }
 
 function BrowserShutdown() {
@@ -3131,23 +3151,27 @@ var browserDragAndDrop = {
 
   dragOver: function (aEvent)
   {
     if (this.canDropLink(aEvent)) {
       aEvent.preventDefault();
     }
   },
 
-  drop: function (aEvent, aName) Services.droppedLinkHandler.dropLink(aEvent, aName)
+  drop: function (aEvent, aName, aDisallowInherit) {
+    return Services.droppedLinkHandler.dropLink(aEvent, aName, aDisallowInherit);
+  }
 };
 
 var homeButtonObserver = {
   onDrop: function (aEvent)
     {
-      setTimeout(openHomeDialog, 0, browserDragAndDrop.drop(aEvent, { }));
+      // disallow setting home pages that inherit the principal
+      let url = browserDragAndDrop.drop(aEvent, {}, true);
+      setTimeout(openHomeDialog, 0, url);
     },
 
   onDragOver: function (aEvent)
     {
       browserDragAndDrop.dragOver(aEvent);
       aEvent.dropEffect = "link";
     },
   onDragExit: function (aEvent)
@@ -5450,17 +5474,17 @@ var TabsInTitlebar = {
       titlebar.style.marginBottom = - Math.min(tabsToolbarRect.top - titlebarTop,
                                                tabsToolbarRect.height) + "px";
 
       document.documentElement.setAttribute("tabsintitlebar", "true");
 
       if (!this._draghandle) {
         let tmp = {};
         Components.utils.import("resource://gre/modules/WindowDraggingUtils.jsm", tmp);
-        this._draghandle = new tmp.WindowDraggingElement(tabsToolbar, window);
+        this._draghandle = new tmp.WindowDraggingElement(tabsToolbar);
         this._draghandle.mouseDownCheck = function () {
           return !this._dragBindingAlive && TabsInTitlebar.enabled;
         };
       }
     } else {
       document.documentElement.removeAttribute("tabsintitlebar");
 
       titlebar.style.marginBottom = "";
@@ -8154,21 +8178,23 @@ var gIdentityHandler = {
     this._identityPopupContentSupp.textContent = supplemental;
     this._identityPopupContentVerif.textContent = verifier;
   },
 
   hideIdentityPopup : function() {
     this._identityPopup.hidePopup();
   },
 
+  _popupOpenTime : null,
+
   /**
    * Click handler for the identity-box element in primary chrome.
    */
   handleIdentityButtonEvent : function(event) {
-
+    this._popupOpenTime = new Date();
     event.stopPropagation();
 
     if ((event.type == "click" && event.button != 0) ||
         (event.type == "keypress" && event.charCode != KeyEvent.DOM_VK_SPACE &&
          event.keyCode != KeyEvent.DOM_VK_RETURN))
       return; // Left click, space or enter only
 
     // Revert the contents of the location bar, see bug 406779
@@ -8195,16 +8221,27 @@ var gIdentityHandler = {
       e.currentTarget.removeEventListener("popuphidden", arguments.callee, false);
       self._identityBox.removeAttribute("open");
     }, false);
 
     // Now open the popup, anchored off the primary chrome element
     this._identityPopup.openPopup(this._identityBox, "bottomcenter topleft");
   },
 
+  onPopupShown : function(event) {
+    let openingDuration = new Date() - this._popupOpenTime;
+    this._popupOpenTime = null;
+    try {
+      Services.telemetry.getHistogramById("FX_IDENTITY_POPUP_OPEN_MS").add(openingDuration);
+    } catch (ex) {
+      Components.utils.reportError("Unable to report telemetry for FX_IDENTITY_POPUP_OPEN_MS.");
+    }
+    document.getElementById('identity-popup-more-info-button').focus();
+  },
+
   onDragStart: function (event) {
     if (gURLBar.getAttribute("pageproxystate") != "valid")
       return;
 
     var value = content.location.href;
     var urlString = value + "\n" + content.document.title;
     var htmlString = "<a href=\"" + value + "\">" + value + "</a>";
 
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -299,17 +299,17 @@
            orient="vertical"
            role="alert"/>
 
     <!-- Popup for site identity information -->
     <panel id="identity-popup"
            type="arrow"
            hidden="true"
            noautofocus="true"
-           onpopupshown="document.getElementById('identity-popup-more-info-button').focus();"
+           onpopupshown="gIdentityHandler.onPopupShown(event);"
            level="top">
       <hbox id="identity-popup-container" align="top">
         <image id="identity-popup-icon"/>
         <vbox id="identity-popup-content-box">
           <label id="identity-popup-connectedToLabel"
                  class="identity-popup-label"
                  value="&identity.connectedTo;"/>
           <label id="identity-popup-connectedToLabel2"
@@ -845,21 +845,16 @@
 
 # Update primaryToolbarButtons in browser/themes/browserShared.inc when adding
 # or removing default items with the toolbarbutton-1 class.
 
       <toolbarbutton id="print-button" class="toolbarbutton-1 chromeclass-toolbar-additional"
                      label="&printButton.label;" command="cmd_print"
                      tooltiptext="&printButton.tooltip;"/>
 
-      <toolbaritem id="navigator-throbber" title="&throbberItem.title;" align="center" pack="center"
-                   mousethrough="always">
-        <image/>
-      </toolbaritem>
-
       <toolbarbutton id="downloads-button" class="toolbarbutton-1 chromeclass-toolbar-additional"
                      observes="Tools:Downloads"
                      ondrop="DownloadsButtonDNDObserver.onDrop(event)"
                      ondragover="DownloadsButtonDNDObserver.onDragOver(event)"
                      ondragenter="DownloadsButtonDNDObserver.onDragOver(event)"
                      ondragexit="DownloadsButtonDNDObserver.onDragExit(event)"
                      label="&downloads.label;"
                      tooltiptext="&downloads.tooltip;"/>
@@ -880,31 +875,16 @@
                      label="&newNavigatorCmd.label;"
                      command="key_newNavigator"
                      tooltiptext="&newWindowButton.tooltip;"
                      ondrop="newWindowButtonObserver.onDrop(event)"
                      ondragover="newWindowButtonObserver.onDragOver(event)"
                      ondragenter="newWindowButtonObserver.onDragOver(event)"
                      ondragexit="newWindowButtonObserver.onDragExit(event)"/>
 
-      <toolbarbutton id="cut-button" class="toolbarbutton-1 chromeclass-toolbar-additional"
-                     label="&cutCmd.label;"
-                     command="cmd_cut"
-                     tooltiptext="&cutButton.tooltip;"/>
-
-      <toolbarbutton id="copy-button" class="toolbarbutton-1 chromeclass-toolbar-additional"
-                     label="&copyCmd.label;"
-                     command="cmd_copy"
-                     tooltiptext="&copyButton.tooltip;"/>
-
-      <toolbarbutton id="paste-button" class="toolbarbutton-1 chromeclass-toolbar-additional"
-                     label="&pasteCmd.label;"
-                     command="cmd_paste"
-                     tooltiptext="&pasteButton.tooltip;"/>
-
       <toolbarbutton id="fullscreen-button" class="toolbarbutton-1 chromeclass-toolbar-additional"
                      observes="View:FullScreen"
                      type="checkbox"
                      label="&fullScreenCmd.label;"
                      tooltiptext="&fullScreenButton.tooltip;"/>
 
       <toolbaritem id="zoom-controls" class="chromeclass-toolbar-additional"
                    title="&zoomControls.label;">
@@ -912,36 +892,58 @@
                        label="&fullZoomReduceCmd.label;"
                        command="cmd_fullZoomReduce"
                        tooltiptext="&zoomOutButton.tooltip;"/>
         <toolbarbutton id="zoom-in-button" class="toolbarbutton-1"
                        label="&fullZoomEnlargeCmd.label;"
                        command="cmd_fullZoomEnlarge"
                        tooltiptext="&zoomInButton.tooltip;"/>
       </toolbaritem>
-#ifdef MOZ_SERVICES_SYNC
-      <toolbarbutton id="sync-button"
-                     class="toolbarbutton-1 chromeclass-toolbar-additional"
-                     label="&syncToolbarButton.label;"
-                     oncommand="gSyncUI.handleToolbarButton()"/>
-#endif
+
       <toolbarbutton id="feed-button"
                      type="menu"
                      class="toolbarbutton-1 chromeclass-toolbar-additional"
                      disabled="true"
                      label="&feedButton.label;"
                      tooltiptext="&feedButton.tooltip;"
                      onclick="return FeedHandler.onFeedButtonClick(event);">
         <menupopup position="after_end"
                    id="feed-menu"
                    onpopupshowing="return FeedHandler.buildFeedList(this);"
                    oncommand="return FeedHandler.subscribeToFeed(null, event);"
                    onclick="checkForMiddleClick(this, event);"/>
       </toolbarbutton>
 
+      <toolbarbutton id="cut-button" class="toolbarbutton-1 chromeclass-toolbar-additional"
+                     label="&cutCmd.label;"
+                     command="cmd_cut"
+                     tooltiptext="&cutButton.tooltip;"/>
+
+      <toolbarbutton id="copy-button" class="toolbarbutton-1 chromeclass-toolbar-additional"
+                     label="&copyCmd.label;"
+                     command="cmd_copy"
+                     tooltiptext="&copyButton.tooltip;"/>
+
+      <toolbarbutton id="paste-button" class="toolbarbutton-1 chromeclass-toolbar-additional"
+                     label="&pasteCmd.label;"
+                     command="cmd_paste"
+                     tooltiptext="&pasteButton.tooltip;"/>
+
+#ifdef MOZ_SERVICES_SYNC
+      <toolbarbutton id="sync-button"
+                     class="toolbarbutton-1 chromeclass-toolbar-additional"
+                     label="&syncToolbarButton.label;"
+                     oncommand="gSyncUI.handleToolbarButton()"/>
+#endif
+
+      <toolbaritem id="navigator-throbber" title="&throbberItem.title;" align="center" pack="center"
+                   mousethrough="always">
+        <image/>
+      </toolbaritem>
+
       <toolbarbutton id="tabview-button" class="toolbarbutton-1 chromeclass-toolbar-additional"
                      label="&tabGroupsButton.label;"
                      command="Browser:ToggleTabView"
                      tooltiptext="&tabGroupsButton.tooltip;"
                      observes="tabviewGroupsNumber"/>
     </toolbarpalette>
   </toolbox>
 
--- a/browser/base/content/newtab/newTab.js
+++ b/browser/base/content/newtab/newTab.js
@@ -40,8 +40,11 @@ const THUMB_HEIGHT = 127;
 #include grid.js
 #include cells.js
 #include sites.js
 #include drag.js
 #include drop.js
 #include dropTargetShim.js
 #include dropPreview.js
 #include updater.js
+
+// Everything is loaded. Initialize the New Tab Page.
+gPage.init("#toolbar", "#grid");
--- a/browser/base/content/newtab/newTab.xul
+++ b/browser/base/content/newtab/newTab.xul
@@ -28,15 +28,13 @@
       </div>
 
       <ul id="grid">
         <li class="cell"/><li class="cell"/><li class="cell"/>
         <li class="cell"/><li class="cell"/><li class="cell"/>
         <li class="cell"/><li class="cell"/><li class="cell"/>
       </ul>
 
-      <xul:script type="text/javascript;version=1.8" src="chrome://browser/content/newtab/newTab.js"/>
-      <xul:script type="text/javascript;version=1.8">
-        gPage.init("#toolbar", "#grid");
-      </xul:script>
+      <xul:script type="text/javascript;version=1.8"
+                  src="chrome://browser/content/newtab/newTab.js"/>
     </body>
   </xul:vbox>
 </xul:window>
--- a/browser/base/content/newtab/page.js
+++ b/browser/base/content/newtab/page.js
@@ -17,18 +17,18 @@ let gPage = {
   init: function Page_init(aToolbarSelector, aGridSelector) {
     gToolbar.init(aToolbarSelector);
     this._gridSelector = aGridSelector;
 
     // Add ourselves to the list of pages to receive notifications.
     gAllPages.register(this);
 
     // Listen for 'unload' to unregister this page.
-    function unload() gAllPages.unregister(self);
-    addEventListener("unload", unload, false);
+    function unload() { gAllPages.unregister(this); }
+    addEventListener("unload", unload.bind(this), false);
 
     // Check if the new tab feature is enabled.
     if (gAllPages.enabled)
       this._init();
     else
       this._updateAttributes(false);
   },
 
--- a/browser/base/content/pageinfo/pageInfo.js
+++ b/browser/base/content/pageinfo/pageInfo.js
@@ -336,16 +336,19 @@ var onUnloadRegistry = [ ];
  */
 function onLoadPageInfo()
 {
   gBundle = document.getElementById("pageinfobundle");
   gStrings.unknown = gBundle.getString("unknown");
   gStrings.notSet = gBundle.getString("notset");
   gStrings.mediaImg = gBundle.getString("mediaImg");
   gStrings.mediaBGImg = gBundle.getString("mediaBGImg");
+  gStrings.mediaBorderImg = gBundle.getString("mediaBorderImg");
+  gStrings.mediaListImg = gBundle.getString("mediaListImg");
+  gStrings.mediaCursor = gBundle.getString("mediaCursor");
   gStrings.mediaObject = gBundle.getString("mediaObject");
   gStrings.mediaEmbed = gBundle.getString("mediaEmbed");
   gStrings.mediaLink = gBundle.getString("mediaLink");
   gStrings.mediaInput = gBundle.getString("mediaInput");
 #ifdef MOZ_MEDIA
   gStrings.mediaVideo = gBundle.getString("mediaVideo");
   gStrings.mediaAudio = gBundle.getString("mediaAudio");
 #endif
@@ -658,23 +661,45 @@ function addImage(url, type, alt, elem, 
     gImageView.data[i][COL_IMAGE_COUNT]++;
     if (elem == gImageElement)
       gImageView.data[i][COL_IMAGE_NODE] = elem;
   }
 }
 
 function grabAll(elem)
 {
-  // check for background images, any node may have multiple
+  // check for images defined in CSS (e.g. background, borders), any node may have multiple
   var computedStyle = elem.ownerDocument.defaultView.getComputedStyle(elem, "");
+
   if (computedStyle) {
-    Array.forEach(computedStyle.getPropertyCSSValue("background-image"), function (url) {
-      if (url.primitiveType == CSSPrimitiveValue.CSS_URI)
-        addImage(url.getStringValue(), gStrings.mediaBGImg, gStrings.notSet, elem, true);
-    });
+    var addImgFunc = function (label, val) {
+      if (val.primitiveType == CSSPrimitiveValue.CSS_URI) {
+        addImage(val.getStringValue(), label, gStrings.notSet, elem, true);
+      }
+      else if (val.primitiveType == CSSPrimitiveValue.CSS_STRING) {
+        // This is for -moz-image-rect.
+        // TODO: Reimplement once bug 714757 is fixed
+        var strVal = val.getStringValue();
+        if (strVal.search(/^.*url\(\"?/) > -1) {
+          url = strVal.replace(/^.*url\(\"?/,"").replace(/\"?\).*$/,"");
+          addImage(url, label, gStrings.notSet, elem, true);
+        }
+      }
+      else if (val.cssValueType == CSSValue.CSS_VALUE_LIST) {
+        // recursively resolve multiple nested CSS value lists
+        for (var i = 0; i < val.length; i++)
+          addImgFunc(label, val.item(i));
+      }
+    };
+
+    addImgFunc(gStrings.mediaBGImg, computedStyle.getPropertyCSSValue("background-image"));
+    addImgFunc(gStrings.mediaBorderImg, computedStyle.getPropertyCSSValue("-moz-border-image-source"));
+    // TODO: support unprefixed "border-image" once bug 713643 is fixed.
+    addImgFunc(gStrings.mediaListImg, computedStyle.getPropertyCSSValue("list-style-image"));
+    addImgFunc(gStrings.mediaCursor, computedStyle.getPropertyCSSValue("cursor"));
   }
 
   // one swi^H^H^Hif-else to rule them all
   if (elem instanceof HTMLImageElement)
     addImage(elem.src, gStrings.mediaImg,
              (elem.hasAttribute("alt")) ? elem.alt : gStrings.notSet, elem, false);
   else if (elem instanceof SVGImageElement) {
     try {
--- a/browser/base/content/test/Makefile.in
+++ b/browser/base/content/test/Makefile.in
@@ -88,16 +88,17 @@ endif
 #
 # browser_sanitizeDialog_treeView.js is disabled until the tree view is added
 # back to the clear recent history dialog (sanitize.xul), if it ever is (bug
 # 480169)
 
 # browser_drag.js is disabled, as it needs to be updated for the new behavior from bug 320638.
 
 # browser_bug321000.js is disabled because newline handling is shaky (bug 592528)
+# browser_urlbarAutoFillTrimURLs.js is disabled till bug 720792 is fixed
 
 _BROWSER_FILES = \
                  head.js \
                  browser_typeAheadFind.js \
                  browser_keywordSearch.js \
                  browser_allTabsPanel.js \
                  browser_alltabslistener.js \
                  browser_bug304198.js \
@@ -117,16 +118,17 @@ endif
                  browser_bug422590.js \
                  browser_bug424101.js \
                  browser_bug427559.js \
                  browser_bug432599.js \
                  browser_bug435035.js \
                  browser_bug441778.js \
                  browser_popupNotification.js \
                  browser_bug455852.js \
+                 browser_bug460146.js \
                  browser_bug462673.js \
                  browser_bug477014.js \
                  browser_bug479408.js \
                  browser_bug479408_sample.html \
                  browser_bug481560.js \
                  browser_bug484315.js \
                  browser_bug491431.js \
                  browser_bug495058.js \
@@ -176,16 +178,17 @@ endif
                  browser_bug624734.js \
                  browser_bug647886.js \
                  browser_bug655584.js \
                  browser_bug664672.js \
                  browser_bug710878.js \
                  browser_bug719271.js \
                  browser_canonizeURL.js \
                  browser_findbarClose.js \
+                 browser_homeDrop.js \
                  browser_keywordBookmarklets.js \
                  browser_contextSearchTabPosition.js \
                  browser_ctrlTab.js \
                  browser_customize_popupNotification.js \
                  browser_disablechrome.js \
                  browser_discovery.js \
                  browser_duplicateIDs.js \
                  browser_gestureSupport.js \
@@ -215,17 +218,16 @@ endif
                  browser_scope.js \
                  browser_selectTabAtIndex.js \
                  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_urlbarAutoFillTrimURLs.js \
                  browser_urlbarCopying.js \
                  browser_urlbarEnter.js \
                  browser_urlbarTrimURLs.js \
                  browser_urlHighlight.js \
                  browser_visibleFindSelection.js \
                  browser_visibleTabs.js \
                  browser_visibleTabs_contextMenu.js \
                  browser_visibleTabs_bookmarkAllPages.js \
@@ -258,17 +260,17 @@ endif
                  browser_addon_bar_close_button.js \
                  browser_addon_bar_shortcut.js \
                  browser_addon_bar_aomlistener.js \
                  test_bug628179.html \
                  browser_wyciwyg_urlbarCopying.js \
                  test_wyciwyg_copying.html \
                  authenticate.sjs \
                  browser_minimize.js \
-								 browser_aboutSyncProgress.js \
+                 browser_aboutSyncProgress.js \
                  browser_middleMouse_inherit.js \
                  redirect_bug623155.sjs \
                  $(NULL)
 
 ifneq (cocoa,$(MOZ_WIDGET_TOOLKIT))
 _BROWSER_FILES += \
 		browser_bug462289.js \
 		$(NULL)
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/browser_bug460146.js
@@ -0,0 +1,51 @@
+/* Check proper image url retrieval from all kinds of elements/styles */
+
+function test() {
+  waitForExplicitFinish();
+
+  gBrowser.selectedTab = gBrowser.addTab();
+  
+  gBrowser.selectedBrowser.addEventListener("load", function () {
+    gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true);
+
+    var doc = gBrowser.contentDocument;
+    var pageInfo = BrowserPageInfo(doc, "mediaTab");
+
+    pageInfo.addEventListener("load", function () {
+      pageInfo.removeEventListener("load", arguments.callee, true);
+      pageInfo.onFinished.push(function () {
+        executeSoon(function () {
+          var imageTree = pageInfo.document.getElementById("imagetree");
+          var imageRowsNum = imageTree.view.rowCount;
+
+          ok(imageTree, "Image tree is null (media tab is broken)");
+
+          ok(imageRowsNum == 7, "Number of images listed: " +
+                                imageRowsNum + ", should be 7");
+
+          pageInfo.close();
+          gBrowser.removeCurrentTab();
+          finish();
+        });
+      });
+    }, true);
+  }, true);
+
+  content.location =
+    "data:text/html," +
+    "<html>" +
+    "  <head>" +
+    "    <title>Test for media tab</title>" +
+    "    <link rel='shortcut icon' href='file:///dummy_icon.ico'>" + // Icon
+    "  </head>" +
+    "  <body style='background-image:url(about:logo?a);'>" + // Background
+    "    <img src='file:///dummy_image.gif'>" + // Image
+    "    <ul>" +
+    "      <li style='list-style:url(about:logo?b);'>List Item 1</li>" + // Bullet
+    "    </ul>  " +
+    "    <div style='-moz-border-image: url(about:logo?c) 20 20 20 20;'>test</div>" + // Border
+    "    <a href='' style='cursor: url(about:logo?d),default;'>test link</a>" + // Cursor
+    "    <object type='image/svg+xml' width=20 height=20 data='file:///dummy_object.svg'></object>" + // Object
+    "  </body>" +
+    "</html>";
+}
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/browser_homeDrop.js
@@ -0,0 +1,77 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+function test() {
+  waitForExplicitFinish();
+
+  // Open a new tab, since starting a drag from the home button activates it and
+  // we don't want to interfere with future tests by loading the home page.
+  let newTab = gBrowser.selectedTab = gBrowser.addTab();
+  registerCleanupFunction(function () {
+    gBrowser.removeTab(newTab);
+  });
+
+  let scriptLoader = Cc["@mozilla.org/moz/jssubscript-loader;1"].
+                     getService(Ci.mozIJSSubScriptLoader);
+  let chromeUtils = {};
+  scriptLoader.loadSubScript("chrome://mochikit/content/tests/SimpleTest/ChromeUtils.js", chromeUtils);
+
+  let homeButton = document.getElementById("home-button");
+  ok(homeButton, "home button present");
+
+  let dialogListener = new WindowListener("chrome://global/content/commonDialog.xul", function (domwindow) {
+    ok(true, "dialog appeared in response to home button drop");
+    domwindow.document.documentElement.cancelDialog();
+    Services.wm.removeListener(dialogListener);
+
+    // Now trigger the invalid URI test
+    executeSoon(function () {
+      let consoleListener = {
+        observe: function (m) {
+          if (m.message.indexOf("NS_ERROR_DOM_BAD_URI") > -1) {
+            Services.console.unregisterListener(consoleListener);
+            ok(true, "drop was blocked");
+            executeSoon(finish);
+          }
+        }
+      }
+      Services.console.registerListener(consoleListener);
+
+      // The drop handler throws an exception when dragging URIs that inherit
+      // principal, e.g. javascript:
+      expectUncaughtException();
+      chromeUtils.synthesizeDrop(homeButton, homeButton, [[{type: "text/plain", data: "javascript:8888"}]], "copy", window, EventUtils);
+    })
+  });
+
+  Services.wm.addListener(dialogListener);
+
+  chromeUtils.synthesizeDrop(homeButton, homeButton, [[{type: "text/plain", data: "http://mochi.test:8888/"}]], "copy", window, EventUtils);
+}
+
+function WindowListener(aURL, aCallback) {
+  this.callback = aCallback;
+  this.url = aURL;
+}
+WindowListener.prototype = {
+  onOpenWindow: function(aXULWindow) {
+    var domwindow = aXULWindow.QueryInterface(Ci.nsIInterfaceRequestor)
+                              .getInterface(Ci.nsIDOMWindow);
+    var self = this;
+    domwindow.addEventListener("load", function() {
+      domwindow.removeEventListener("load", arguments.callee, false);
+
+      ok(true, "domwindow.document.location.href: " + domwindow.document.location.href);
+      if (domwindow.document.location.href != self.url)
+        return;
+
+      // Allow other window load listeners to execute before passing to callback
+      executeSoon(function() {
+        self.callback(domwindow);
+      });
+    }, false);
+  },
+  onCloseWindow: function(aXULWindow) {},
+  onWindowTitleChange: function(aXULWindow, aNewTitle) {}
+}
+
--- a/browser/base/content/test/browser_locationBarCommand.js
+++ b/browser/base/content/test/browser_locationBarCommand.js
@@ -125,18 +125,21 @@ let gTests = [
   }
 ]
 
 let gGoButton = document.getElementById("urlbar-go-button");
 function triggerCommand(aClick, aEvent) {
   gURLBar.value = TEST_VALUE;
   gURLBar.focus();
 
-  if (aClick)
+  if (aClick) {
+    is(gURLBar.getAttribute("pageproxystate"), "invalid",
+       "page proxy state must be invalid for go button to be visible");
     EventUtils.synthesizeMouseAtCenter(gGoButton, aEvent); 
+  }
   else
     EventUtils.synthesizeKey("VK_RETURN", aEvent);
 }
 
 /* Checks that the URL was loaded in the current tab */
 function checkCurrent(aTab) {
   info("URL should be loaded in the current tab");
   is(gURLBar.value, TEST_VALUE, "Urlbar still has the value we entered");
--- a/browser/base/content/test/newtab/Makefile.in
+++ b/browser/base/content/test/newtab/Makefile.in
@@ -15,13 +15,14 @@ include $(topsrcdir)/config/rules.mk
 	browser_newtab_block.js \
 	browser_newtab_disable.js \
 	browser_newtab_drag_drop.js \
 	browser_newtab_drop_preview.js \
 	browser_newtab_private_browsing.js \
 	browser_newtab_reset.js \
 	browser_newtab_tabsync.js \
 	browser_newtab_unpin.js \
+	browser_newtab_bug723102.js \
 	head.js \
 	$(NULL)
 
 libs::	$(_BROWSER_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/newtab/browser_newtab_bug723102.js
@@ -0,0 +1,17 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+function runTests() {
+  // create a new tab page and hide it.
+  setLinks("0,1,2,3,4,5,6,7,8");
+  setPinnedLinks("");
+
+  yield addNewTabPageTab();
+  let firstTab = gBrowser.selectedTab;
+
+  yield addNewTabPageTab();
+  gBrowser.removeTab(firstTab);
+
+  cw.gToolbar.hide();
+  ok(cw.gGrid.node.hasAttribute("page-disabled"), "page is disabled");
+}
--- a/browser/base/content/test/newtab/head.js
+++ b/browser/base/content/test/newtab/head.js
@@ -1,16 +1,18 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 const PREF_NEWTAB_ENABLED = "browser.newtabpage.enabled";
 
 Services.prefs.setBoolPref(PREF_NEWTAB_ENABLED, true);
 
-Cu.import("resource:///modules/NewTabUtils.jsm");
+let tmp = {};
+Cu.import("resource:///modules/NewTabUtils.jsm", tmp);
+let NewTabUtils = tmp.NewTabUtils;
 
 registerCleanupFunction(function () {
   reset();
 
   while (gBrowser.tabs.length > 1)
     gBrowser.removeTab(gBrowser.tabs[1]);
 
   Services.prefs.clearUserPref(PREF_NEWTAB_ENABLED);
--- a/browser/components/certerror/Makefile.in
+++ b/browser/components/certerror/Makefile.in
@@ -38,13 +38,11 @@
 
 DEPTH     = ../../..
 topsrcdir = @top_srcdir@
 srcdir    = @srcdir@
 VPATH     = @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
-ifdef ENABLE_TESTS
-DIRS += test
-endif
+TEST_DIRS += test
 
 include $(topsrcdir)/config/rules.mk
--- a/browser/components/dirprovider/Makefile.in
+++ b/browser/components/dirprovider/Makefile.in
@@ -40,19 +40,17 @@ topsrcdir = @top_srcdir@
 srcdir    = @srcdir@
 VPATH     = @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 MODULE = browserdir
 LIBRARY_NAME = browserdir_s
 
-ifdef ENABLE_TESTS
-DIRS = tests
-endif
+TEST_DIRS += tests
 
 FORCE_STATIC_LIB = 1
 
 # Because we are an application component, link against the CRT statically
 # (on Windows, but only if we're not building our own CRT for jemalloc)
 ifndef MOZ_MEMORY
 USE_STATIC_LIBS      = 1
 endif
--- a/browser/components/feeds/Makefile.in
+++ b/browser/components/feeds/Makefile.in
@@ -38,13 +38,11 @@ DEPTH		= ../../..
 topsrcdir	= @top_srcdir@
 srcdir		= @srcdir@
 VPATH		= @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 DIRS = public src
 
-ifdef ENABLE_TESTS
-DIRS += test
-endif
+TEST_DIRS += test
 
 include $(topsrcdir)/config/rules.mk
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -410,16 +410,19 @@ BrowserGlue.prototype = {
       aAddons.forEach(function(aAddon) {
         // If the add-on isn't user disabled or can't be enabled then skip it
         if (!aAddon.userDisabled || !(aAddon.permissions & AddonManager.PERM_CAN_ENABLE))
           return;
 
         browser.selectedTab = browser.addTab("about:newaddon?id=" + aAddon.id);
       })
     });
+
+    let keywordURLUserSet = Services.prefs.prefHasUserValue("keyword.URL");
+    Services.telemetry.getHistogramById("FX_KEYWORD_URL_USERSET").add(keywordURLUserSet);
   },
 
   _onQuitRequest: function BG__onQuitRequest(aCancelQuit, aQuitType) {
     // If user has already dismissed quit request, then do nothing
     if ((aCancelQuit instanceof Ci.nsISupportsPRBool) && aCancelQuit.data)
       return;
 
     // There are several cases where we won't show a dialog here:
--- a/browser/components/places/Makefile.in
+++ b/browser/components/places/Makefile.in
@@ -40,15 +40,13 @@ DEPTH		= ../../..
 topsrcdir	= @top_srcdir@
 srcdir		= @srcdir@
 VPATH		= @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 DIRS = src
 
-ifdef ENABLE_TESTS
-	DIRS += tests
-endif
+TEST_DIRS += tests
 
 include $(topsrcdir)/config/rules.mk
 
 XPIDL_FLAGS += -I$(topsrcdir)/browser/components/
--- a/browser/components/preferences/Makefile.in
+++ b/browser/components/preferences/Makefile.in
@@ -38,19 +38,17 @@
 
 DEPTH		= ../../..
 topsrcdir	= @top_srcdir@
 srcdir		= @srcdir@
 VPATH		= @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
-ifdef ENABLE_TESTS
-DIRS += tests
-endif
+TEST_DIRS += tests
 
 include $(topsrcdir)/config/rules.mk
 
 DEFINES += \
 	-DMOZ_APP_NAME=$(MOZ_APP_NAME) \
 	-DMOZ_MACBUNDLE_NAME=$(MOZ_MACBUNDLE_NAME) \
 	$(NULL)
 
--- a/browser/components/preferences/cookies.js
+++ b/browser/components/preferences/cookies.js
@@ -84,17 +84,17 @@ var gCookiesWindow = {
           window.arguments[0].filterString)
         this.setFilter(window.arguments[0].filterString);
     }
     else {
       if (document.getElementById("filter").value != "")
         this.filter();
     }
 
-    document.getElementById("removeAllCookies").disabled = this._view._rowCount == 0;
+    this._updateRemoveAllButton();
 
     this._saveState();
   },
 
   _cookieEquals: function (aCookieA, aCookieB, aStrippedHost) {
     return aCookieA.rawHost == aStrippedHost &&
            aCookieA.name == aCookieB.name &&
            aCookieA.path == aCookieB.path;
@@ -114,17 +114,17 @@ var gCookiesWindow = {
     else if (aData == "cleared") {
       this._hosts = {};
       this._hostOrder = [];
 
       var oldRowCount = this._view._rowCount;
       this._view._rowCount = 0;
       this._tree.treeBoxObject.rowCountChanged(0, -oldRowCount);
       this._view.selection.clearSelection();
-      document.getElementById("removeAllCookies").disabled = true;
+      this._updateRemoveAllButton();
     }
     else if (aData == "reload") {
       // first, clear any existing entries
       this.observe(aCookie, aTopic, "cleared");
 
       // then, reload the list
       this._populateList(false);
     }
@@ -205,20 +205,17 @@ var gCookiesWindow = {
       }
     }
     // Now update the tree display at the end (we could/should re run the sort
     // if any to get the position correct.)
     var oldRowCount = this._rowCount;
     this._view._rowCount += rowCountImpact;
     this._tree.treeBoxObject.rowCountChanged(oldRowCount - 1, rowCountImpact);
 
-    if (this._view._rowCount > 0 && !this._view._filtered)
-      document.getElementById("removeAllCookies").disabled = false;
-    else
-      document.getElementById("removeAllCookies").disabled = true;
+    this._updateRemoveAllButton();
   },
 
   _view: {
     _filtered   : false,
     _filterSet  : [],
     _filterValue: "",
     _rowCount   : 0,
     _cacheValid : 0,
@@ -588,20 +585,31 @@ var gCookiesWindow = {
     if (item && seln.count == 1 && item.container && item.open)
       selectedCookieCount += 2;
 
     var removeCookie = document.getElementById("removeCookie");
     var removeCookies = document.getElementById("removeCookies");
     removeCookie.parentNode.selectedPanel =
       selectedCookieCount == 1 ? removeCookie : removeCookies;
 
-    document.getElementById("removeAllCookies").disabled = this._view._filtered;
     removeCookie.disabled = removeCookies.disabled = !(seln.count > 0);
   },
 
+  performDeletion: function gCookiesWindow_performDeletion(deleteItems) {
+    var psvc = Components.classes["@mozilla.org/preferences-service;1"]
+                         .getService(Components.interfaces.nsIPrefBranch);
+    var blockFutureCookies = false;
+    if (psvc.prefHasUserValue("network.cookie.blockFutureCookies"))
+      blockFutureCookies = psvc.getBoolPref("network.cookie.blockFutureCookies");
+    for (var i = 0; i < deleteItems.length; ++i) {
+      var item = deleteItems[i];
+      this._cm.remove(item.host, item.name, item.path, blockFutureCookies);
+    }
+  },
+
   deleteCookie: function () {
 #   // Selection Notes
 #   // - Selection always moves to *NEXT* adjacent item unless item
 #   //   is last child at a given level in which case it moves to *PREVIOUS*
 #   //   item
 #   //
 #   // Selection Cases (Somewhat Complicated)
 #   //
@@ -688,54 +696,63 @@ var gCookiesWindow = {
       }
       this._view._rowCount += rowCountImpact;
       tbo.rowCountChanged(ci, rowCountImpact);
       if (invalidateRow != -1)
         tbo.invalidateRow(invalidateRow);
     }
     else {
       var rangeCount = seln.getRangeCount();
-      for (var i = 0; i < rangeCount; ++i) {
+      // Traverse backwards through selections to avoid messing 
+      // up the indices when they are deleted.
+      // See bug 388079.
+      for (var i = rangeCount - 1; i >= 0; --i) {
         var min = {}; var max = {};
         seln.getRangeAt(i, min, max);
         nextSelected = min.value;
         for (var j = min.value; j <= max.value; ++j) {
           deleteItems.push(this._view._getItemAtIndex(j));
           if (!this._view.hasNextSibling(-1, max.value))
             --nextSelected;
         }
         var delta = max.value - min.value + 1;
         this._view._removeItemAtIndex(min.value, delta);
         rowCountImpact = -1 * delta;
         this._view._rowCount += rowCountImpact;
         tbo.rowCountChanged(min.value, rowCountImpact);
       }
     }
 
-    var psvc = Components.classes["@mozilla.org/preferences-service;1"]
-                         .getService(Components.interfaces.nsIPrefBranch);
-    var blockFutureCookies = false;
-    if (psvc.prefHasUserValue("network.cookie.blockFutureCookies"))
-      blockFutureCookies = psvc.getBoolPref("network.cookie.blockFutureCookies");
-    for (i = 0; i < deleteItems.length; ++i) {
-      var item = deleteItems[i];
-      this._cm.remove(item.host, item.name, item.path, blockFutureCookies);
-    }
+    this.performDeletion(deleteItems);
 
     if (nextSelected < 0)
       seln.clearSelection();
     else {
       seln.select(nextSelected);
       this._tree.focus();
     }
   },
 
   deleteAllCookies: function () {
-    this._cm.removeAll();
-    this._tree.focus();
+    if (this._view._filtered) {
+      var rowCount = this._view.rowCount;
+      var deleteItems = [];
+      for (var index = 0; index < rowCount; index++) {
+        deleteItems.push(this._view._getItemAtIndex(index));
+      }
+      this._view._removeItemAtIndex(0, rowCount);
+      this._view._rowCount = 0;
+      this._tree.treeBoxObject.rowCountChanged(0, -rowCount);
+      this.performDeletion(deleteItems);
+    }
+    else {
+      this._cm.removeAll();
+    }
+    this._updateRemoveAllButton();
+    this.focusFilterBox();
   },
 
   onCookieKeyPress: function (aEvent) {
     if (aEvent.keyCode == 46)
       this.deleteCookie();
   },
 
   _lastSortProperty : "",
@@ -825,16 +842,17 @@ var gCookiesWindow = {
     this._view.selection.clearSelection();
     for (i = 0; i < this._lastSelectedRanges.length; ++i) {
       var range = this._lastSelectedRanges[i];
       this._view.selection.rangedSelect(range.min, range.max, true);
     }
     this._lastSelectedRanges = [];
 
     document.getElementById("cookiesIntro").value = this._bundle.getString("cookiesAll");
+    this._updateRemoveAllButton();
   },
 
   _cookieMatchesFilter: function (aCookie) {
     return aCookie.rawHost.indexOf(this._view._filterValue) != -1 ||
            aCookie.name.indexOf(this._view._filterValue) != -1 ||
            aCookie.value.indexOf(this._view._filterValue) != -1;
   },
 
@@ -870,16 +888,20 @@ var gCookiesWindow = {
     this._openIndices = [];
     for (i = 0; i < this._view.rowCount; ++i) {
       var item = this._view._getItemAtIndex(i);
       if (item && item.container && item.open)
         this._openIndices.push(i);
     }
   },
 
+  _updateRemoveAllButton: function gCookiesWindow__updateRemoveAllButton() {
+    document.getElementById("removeAllCookies").disabled = this._view._rowCount == 0;
+  },
+
   filter: function () {
     var filter = document.getElementById("filter").value;
     if (filter == "") {
       gCookiesWindow.clearFilter();
       return;
     }
     var view = gCookiesWindow._view;
     view._filterSet = gCookiesWindow._filterCookies(filter);
@@ -900,16 +922,17 @@ var gCookiesWindow = {
     view._rowCount = view._filterSet.length;
     gCookiesWindow._tree.treeBoxObject.rowCountChanged(0, view.rowCount);
 
     // if the view is not empty then select the first item
     if (view.rowCount > 0)
       view.selection.select(0);
 
     document.getElementById("cookiesIntro").value = gCookiesWindow._bundle.getString("cookiesFiltered");
+    this._updateRemoveAllButton();
   },
 
   setFilter: function (aFilterString) {
     document.getElementById("filter").value = aFilterString;
     this.filter();
   },
 
   focusFilterBox: function () {
--- a/browser/components/preferences/tests/Makefile.in
+++ b/browser/components/preferences/tests/Makefile.in
@@ -41,16 +41,17 @@ VPATH		= @srcdir@
 relativesrcdir  = browser/components/preferences/tests
 
 include $(DEPTH)/config/autoconf.mk
 include $(topsrcdir)/config/rules.mk
 
 _BROWSER_FILES = \
     browser_bug410900.js \
     browser_bug567487.js \
+    browser_bug705422.js \
     privacypane_tests.js \
     browser_privacypane_1.js \
     browser_privacypane_2.js \
     browser_privacypane_3.js \
     browser_privacypane_4.js \
     browser_privacypane_5.js \
     browser_privacypane_6.js \
     browser_privacypane_7.js \
new file mode 100644
--- /dev/null
+++ b/browser/components/preferences/tests/browser_bug705422.js
@@ -0,0 +1,140 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+function test() {
+    waitForExplicitFinish();
+    const searchTerm = "example";
+    const dummyTerm = "elpmaxe";
+
+    var cm =  Components.classes["@mozilla.org/cookiemanager;1"]
+                        .getService(Components.interfaces.nsICookieManager);
+
+    // delete all cookies (might be left over from other tests)
+    cm.removeAll();
+
+    // data for cookies
+    var vals = [[searchTerm+".com", dummyTerm, dummyTerm],          // match
+                [searchTerm+".org", dummyTerm, dummyTerm],          // match
+                [dummyTerm+".com", searchTerm, dummyTerm],          // match
+                [dummyTerm+".edu", searchTerm+dummyTerm, dummyTerm],// match
+                [dummyTerm+".net", dummyTerm, searchTerm],          // match
+                [dummyTerm+".org", dummyTerm, searchTerm+dummyTerm],// match
+                [dummyTerm+".int", dummyTerm, dummyTerm]];          // no match
+
+    // matches must correspond to above data
+    const matches = 6;
+
+    var ios = Components.classes["@mozilla.org/network/io-service;1"]
+                        .getService(Components.interfaces.nsIIOService);
+    var cookieSvc = Components.classes["@mozilla.org/cookieService;1"]
+                              .getService(Components.interfaces.nsICookieService);
+    var v;
+    // inject cookies
+    for (v in vals) {
+        let [host, name, value] = vals[v];    
+        var cookieUri = ios.newURI("http://"+host, null, null);
+        cookieSvc.setCookieString(cookieUri, null, name+"="+value+";", null);
+    }
+
+    // open cookie manager
+    var cmd = window.openDialog("chrome://browser/content/preferences/cookies.xul",
+                                "Browser:Cookies", "", {});
+    
+    // when it has loaded, run actual tests
+    cmd.addEventListener("load", function() {executeSoon(function() {runTest(cmd, searchTerm, vals.length, matches);});}, false);
+}
+
+function isDisabled(win, expectation) {
+    var disabled = win.document.getElementById("removeAllCookies").disabled;
+    is(disabled, expectation, "Remove all cookies button has correct state: "+(expectation?"disabled":"enabled"));
+}
+
+function runTest(win, searchTerm, cookies, matches) {
+    var cm =  Components.classes["@mozilla.org/cookiemanager;1"]
+                        .getService(Components.interfaces.nsICookieManager);
+
+
+    // number of cookies should match injected cookies
+    var cnt = 0,
+        enumerator = cm.enumerator;
+    while (enumerator.hasMoreElements()) {
+        cnt++;
+        enumerator.getNext();
+    }
+    is(cnt, cookies, "Number of cookies match injected cookies");
+
+    // "delete all cookies" should be enabled
+    isDisabled(win, false);
+
+    // filter cookies and count matches
+    win.gCookiesWindow.setFilter(searchTerm);
+    is(win.gCookiesWindow._view.rowCount, matches, "Correct number of cookies shown after filter is applied");
+
+    // "delete all cookies" should be enabled
+    isDisabled(win, false);
+
+
+    // select first cookie and delete
+    var tree = win.document.getElementById("cookiesList");
+    var deleteButton = win.document.getElementById("removeCookie");
+    var x = {}, y = {}, width = {}, height = {};
+    tree.treeBoxObject.getCoordsForCellItem(0, tree.columns[0], "cell", x, y, width, height);
+    EventUtils.synthesizeMouse(tree.body, x.value + width.value / 2, y.value + height.value / 2, {}, win);
+    EventUtils.synthesizeMouseAtCenter(deleteButton, {}, win);
+
+    // count cookies should be matches-1
+    is(win.gCookiesWindow._view.rowCount, matches-1, "Deleted selected cookie");
+
+    // select two adjacent cells and delete
+    EventUtils.synthesizeMouse(tree.body, x.value + width.value / 2, y.value + height.value / 2, {}, win);
+    deleteButton = win.document.getElementById("removeCookies"); 
+    var eventObj = {};
+    if (navigator.platform.indexOf("Mac") >= 0)
+        eventObj.metaKey = true;
+    else
+        eventObj.ctrlKey = true;
+    tree.treeBoxObject.getCoordsForCellItem(1, tree.columns[0], "cell", x, y, width, height);
+    EventUtils.synthesizeMouse(tree.body, x.value + width.value / 2, y.value + height.value / 2, eventObj, win);
+    EventUtils.synthesizeMouseAtCenter(deleteButton, {}, win);
+
+    // count cookies should be matches-3
+    is(win.gCookiesWindow._view.rowCount, matches-3, "Deleted selected two adjacent cookies");
+
+    // "delete all cookies" should be enabled
+    isDisabled(win, false);
+
+    // delete all cookies and count
+    var deleteAllButton = win.document.getElementById("removeAllCookies");
+    EventUtils.synthesizeMouseAtCenter(deleteAllButton, {}, win);
+    is(win.gCookiesWindow._view.rowCount, 0, "Deleted all matching cookies");
+
+    // "delete all cookies" should be disabled
+    isDisabled(win, true);
+
+    // clear filter and count should be cookies-matches
+    win.gCookiesWindow.setFilter("");
+    is(win.gCookiesWindow._view.rowCount, cookies-matches, "Unmatched cookies remain");
+
+    // "delete all cookies" should be enabled
+    isDisabled(win, false);
+
+    // delete all cookies and count should be 0
+    EventUtils.synthesizeMouseAtCenter(deleteAllButton, {}, win);
+    is(win.gCookiesWindow._view.rowCount, 0, "Deleted all cookies");
+
+    // check that datastore is also at 0
+    var cnt = 0,
+        enumerator = cm.enumerator;
+    while (enumerator.hasMoreElements()) {
+        cnt++;
+        enumerator.getNext();
+    }
+    is(cnt, 0, "Zero cookies remain");
+
+    // "delete all cookies" should be disabled
+    isDisabled(win, true);
+
+    // clean up
+    win.close();
+    finish();
+}
--- a/browser/components/privatebrowsing/Makefile.in
+++ b/browser/components/privatebrowsing/Makefile.in
@@ -41,13 +41,11 @@ srcdir		= @srcdir@
 VPATH		= @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 MODULE = privatebrowsing
 
 DIRS = src
 
-ifdef ENABLE_TESTS
-DIRS += test
-endif
+TEST_DIRS += test
 
 include $(topsrcdir)/config/rules.mk
--- a/browser/components/privatebrowsing/src/nsPrivateBrowsingService.js
+++ b/browser/components/privatebrowsing/src/nsPrivateBrowsingService.js
@@ -32,16 +32,17 @@
 # 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 *****
 
 Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
+Components.utils.import("resource://gre/modules/Services.jsm");
 
 #ifndef XP_WIN
 #define BROKEN_WM_Z_ORDER
 #endif
 
 ////////////////////////////////////////////////////////////////////////////////
 //// Utilities
 
@@ -171,37 +172,47 @@ PrivateBrowsingService.prototype = {
           else // no open browser windows, just restore a blank state on exit
             this._savedBrowserState = blankState;
         }
       }
 
       this._closePageInfoWindows();
 
       // save view-source windows URIs and close them
-      let viewSrcWindowsEnum = Cc["@mozilla.org/appshell/window-mediator;1"].
-                               getService(Ci.nsIWindowMediator).
-                               getEnumerator("navigator:view-source");
+      let viewSrcWindowsEnum = Services.wm.getEnumerator("navigator:view-source");
       while (viewSrcWindowsEnum.hasMoreElements()) {
         let win = viewSrcWindowsEnum.getNext();
         if (this._inPrivateBrowsing) {
           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 
+	// if there are open browser windows, load a dummy session to get a distinct 
         // separation between private and non-private sessions
-        if (browserWindow) {
+	if (browserWindow) {
           // set an empty session to transition from/to pb mode, see bug 476463
           ss.setBrowserState(blankState);
 
           // just in case the only remaining window after setBrowserState is different.
           // it probably shouldn't be with the current sessionstore impl, but we shouldn't
           // rely on behaviour the API doesn't guarantee
           browserWindow = this._getBrowserWindow();
           let browser = browserWindow.gBrowser;
--- a/browser/components/safebrowsing/Makefile.in
+++ b/browser/components/safebrowsing/Makefile.in
@@ -38,19 +38,17 @@
 
 DEPTH     = ../../..
 topsrcdir = @top_srcdir@
 srcdir    = @srcdir@
 VPATH     = @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
-ifdef ENABLE_TESTS
-DIRS += content/test
-endif
+TEST_DIRS += content/test
 
 ifdef MOZILLA_OFFICIAL
 DEFINES += -DOFFICIAL_BUILD=1
 endif
 
 EXTRA_COMPONENTS = \
   src/nsSafebrowsingApplication.manifest \
   $(NULL)
--- a/browser/components/search/Makefile.in
+++ b/browser/components/search/Makefile.in
@@ -38,13 +38,11 @@
 
 DEPTH     = ../../..
 topsrcdir = @top_srcdir@
 srcdir    = @srcdir@
 VPATH     = @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
-ifdef ENABLE_TESTS
-DIRS = test
-endif
+TEST_DIRS += test
 
 include $(topsrcdir)/config/rules.mk
--- a/browser/components/sessionstore/test/Makefile.in
+++ b/browser/components/sessionstore/test/Makefile.in
@@ -155,16 +155,17 @@ include $(topsrcdir)/config/rules.mk
 	browser_659591.js \
 	browser_662812.js \
 	browser_665702-state_session.js \
 	browser_682507.js \
 	browser_687710.js \
 	browser_687710_2.js \
 	browser_694378.js \
 	browser_705597.js \
+	browser_707862.js \
 	$(NULL)
 
 ifneq ($(OS_ARCH),Darwin)
 _BROWSER_TEST_FILES += \
 	browser_597071.js \
 	browser_625016.js \
 	$(NULL)
 endif
--- a/browser/components/sessionstore/test/browser_463205.js
+++ b/browser/components/sessionstore/test/browser_463205.js
@@ -34,17 +34,17 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 function test() {
   /** Test for Bug 463205 **/
   
   waitForExplicitFinish();
   
-  let rootDir = getRootDirectory(gTestPath);
+  let rootDir = "http://mochi.test:8888/browser/browser/components/sessionstore/test/";
   let testURL = rootDir + "browser_463205_sample.html";
 
   let doneURL = "done";
 
   let mainURL = testURL;
   let frame1URL = "data:text/html,<input%20id='original'>";
   let frame2URL = rootDir + "browser_463205_helper.html";
   let frame3URL = "data:text/html,mark2";
new file mode 100644
--- /dev/null
+++ b/browser/components/sessionstore/test/browser_707862.js
@@ -0,0 +1,56 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+let tabState = {
+  entries: [{url: "about:home", children: [{url: "about:mozilla"}]}]
+};
+
+function test() {
+  waitForExplicitFinish();
+
+  let tab = gBrowser.addTab("about:blank");
+  registerCleanupFunction(function () gBrowser.removeTab(tab));
+
+  let browser = tab.linkedBrowser;
+
+  whenBrowserLoaded(browser, function () {
+    ss.setTabState(tab, JSON.stringify(tabState));
+
+    let sessionHistory = browser.sessionHistory;
+    let entry = sessionHistory.getEntryAtIndex(0, false);
+
+    whenChildCount(entry, 1, function () {
+      whenChildCount(entry, 2, function () {
+        whenBrowserLoaded(browser, function () {
+          let sessionHistory = browser.sessionHistory;
+          let entry = sessionHistory.getEntryAtIndex(0, false);
+
+          whenChildCount(entry, 0, finish);
+        });
+
+        // reload the browser to deprecate the subframes
+        browser.reload();
+      });
+
+      // create a dynamic subframe
+      let doc = browser.contentDocument;
+      let iframe = doc.createElement("iframe");
+      iframe.setAttribute("src", "about:mozilla");
+      doc.body.appendChild(iframe);
+    });
+  });
+}
+
+function whenBrowserLoaded(aBrowser, aCallback) {
+  aBrowser.addEventListener("load", function onLoad() {
+    aBrowser.removeEventListener("load", onLoad, true);
+    executeSoon(aCallback);
+  }, true);
+}
+
+function whenChildCount(aEntry, aChildCount, aCallback) {
+  if (aEntry.childCount == aChildCount)
+    aCallback();
+  else
+    executeSoon(function () whenChildCount(aEntry, aChildCount, aCallback));
+}
--- a/browser/components/shell/Makefile.in
+++ b/browser/components/shell/Makefile.in
@@ -39,13 +39,11 @@ DEPTH		= ../../..
 topsrcdir	= @top_srcdir@
 srcdir		= @srcdir@
 VPATH		= @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 DIRS = public src
 
-ifdef ENABLE_TESTS
-DIRS += test
-endif
+TEST_DIRS += test
 
 include $(topsrcdir)/config/rules.mk
--- a/browser/components/shell/content/setDesktopBackground.js
+++ b/browser/components/shell/content/setDesktopBackground.js
@@ -63,16 +63,28 @@ var gSetBackground = {
     this._screenWidth = screen.width;
     this._screenHeight = screen.height;
 #ifdef XP_MACOSX
     document.documentElement.getButton("accept").hidden = true;
 #endif
     if (this._screenWidth / this._screenHeight >= 1.6)
       document.getElementById("monitor").setAttribute("aspectratio", "16:10");
 
+#ifdef XP_WIN
+    // hide fill + fit options if <win7 since don't work 
+    var version = Components.classes["@mozilla.org/system-info;1"]
+                  .getService(Ci.nsIPropertyBag2)
+                  .getProperty("version");
+    var isWindows7OrHigher = (parseFloat(version) >= 6.1);
+    if (!isWindows7OrHigher) {
+      document.getElementById("fillPosition").hidden = true;
+      document.getElementById("fitPosition").hidden = true;
+    }
+#endif
+
     // make sure that the correct dimensions will be used
     setTimeout(function(self) {
       self.init(window.arguments[0]);
     }, 0, this);
   },
 
   init: function (aImage)
   {
@@ -193,11 +205,44 @@ var gSetBackground = {
         break;
       case "STRETCH":
         ctx.drawImage(this._image, 0, 0, this._screenWidth, this._screenHeight);
         break;
       case "CENTER":
         var x = (this._screenWidth - this._image.naturalWidth) / 2;
         var y = (this._screenHeight - this._image.naturalHeight) / 2;
         ctx.drawImage(this._image, x, y);
+        break;
+      case "FILL":
+        //Try maxing width first, overflow height
+        var widthRatio = this._screenWidth / this._image.naturalWidth;
+        var width = this._image.naturalWidth * widthRatio;
+        var height = this._image.naturalHeight * widthRatio;
+        if (height < this._screenHeight) {
+          //height less than screen, max height and overflow width
+          var heightRatio = this._screenHeight / this._image.naturalHeight;
+          width = this._image.naturalWidth * heightRatio;
+          height = this._image.naturalHeight * heightRatio;
+        }
+        var x = (this._screenWidth - width) / 2;
+        var y = (this._screenHeight - height) / 2;
+        ctx.drawImage(this._image, x, y, width, height);
+        break;
+      case "FIT":
+        //Try maxing width first, top and bottom borders
+        var widthRatio = this._screenWidth / this._image.naturalWidth;
+        var width = this._image.naturalWidth * widthRatio;
+        var height = this._image.naturalHeight * widthRatio;
+        var x = 0;
+        var y = (this._screenHeight - height) / 2;
+        if (height > this._screenHeight) {
+          //height overflow, maximise height, side borders
+          var heightRatio = this._screenHeight / this._image.naturalHeight;
+          width = this._image.naturalWidth * heightRatio;
+          height = this._image.naturalHeight * heightRatio;
+          x = (this._screenWidth - width) / 2;
+          y = 0;
+        }
+        ctx.drawImage(this._image, x, y, width, height);
+        break;      
     }
   }
 };
--- a/browser/components/shell/content/setDesktopBackground.xul
+++ b/browser/components/shell/content/setDesktopBackground.xul
@@ -73,16 +73,18 @@
       <label value="&position.label;"/>
       <menulist id="menuPosition"
                 label="&position.label;" 
                 oncommand="gSetBackground.updatePosition();">
         <menupopup>
           <menuitem label="&center.label;"  value="CENTER"/>
           <menuitem label="&tile.label;"    value="TILE"/>
           <menuitem label="&stretch.label;" value="STRETCH"/>
+          <menuitem label="&fill.label;"    value="FILL" id="fillPosition"/>
+          <menuitem label="&fit.label;"     value="FIT"  id="fitPosition"/>
         </menupopup>
       </menulist>
       <spacer flex="1"/>
       <label value="&color.label;"/>
       <colorpicker id="desktopColor"
                    type="button" 
                    onchange="gSetBackground.updateColor(this.color);"/> 
     </hbox>
--- a/browser/components/shell/public/nsIShellService.idl
+++ b/browser/components/shell/public/nsIShellService.idl
@@ -76,16 +76,17 @@ interface nsIShellService : nsISupports
 
   /** 
    * Flags for positioning/sizing of the Desktop Background image.
    */
   const long BACKGROUND_TILE      = 1;
   const long BACKGROUND_STRETCH   = 2;
   const long BACKGROUND_CENTER    = 3;
   const long BACKGROUND_FILL      = 4;
+  const long BACKGROUND_FIT       = 5;
 
     /**
      * Sets the desktop background image using either the HTML <IMG> 
      * element supplied or the background image of the element supplied.
      *
      * @param aImageElement Either a HTML <IMG> element or an element with
      *                      a background image from which to source the
      *                      background image. 
--- a/browser/components/shell/src/nsGNOMEShellService.cpp
+++ b/browser/components/shell/src/nsGNOMEShellService.cpp
@@ -422,16 +422,20 @@ nsGNOMEShellService::SetDesktopBackgroun
   if (!container) return rv;
 
   // Set desktop wallpaper filling style
   nsCAutoString options;
   if (aPosition == BACKGROUND_TILE)
     options.Assign("wallpaper");
   else if (aPosition == BACKGROUND_STRETCH)
     options.Assign("stretched");
+  else if (aPosition == BACKGROUND_FILL)
+    options.Assign("zoom");
+  else if (aPosition == BACKGROUND_FIT)
+    options.Assign("scaled");
   else
     options.Assign("centered");
 
   // Write the background file to the home directory.
   nsCAutoString filePath(PR_GetEnv("HOME"));
 
   // get the product brand name from localized strings
   nsString brandName;
--- a/browser/components/shell/src/nsWindowsShellService.cpp
+++ b/browser/components/shell/src/nsWindowsShellService.cpp
@@ -635,16 +635,24 @@ nsWindowsShellService::SetDesktopBackgro
       case BACKGROUND_CENTER:
         style.AssignLiteral("0");
         tile.AssignLiteral("0");
         break;
       case BACKGROUND_STRETCH:
         style.AssignLiteral("2");
         tile.AssignLiteral("0");
         break;
+      case BACKGROUND_FILL:
+        style.AssignLiteral("10");
+        tile.AssignLiteral("0");
+        break;
+      case BACKGROUND_FIT:
+        style.AssignLiteral("6");
+        tile.AssignLiteral("0");
+        break;
     }
 
     rv = regKey->WriteStringValue(NS_LITERAL_STRING("TileWallpaper"), tile);
     NS_ENSURE_SUCCESS(rv, rv);
     rv = regKey->WriteStringValue(NS_LITERAL_STRING("WallpaperStyle"), style);
     NS_ENSURE_SUCCESS(rv, rv);
     rv = regKey->Close();
     NS_ENSURE_SUCCESS(rv, rv);
--- a/browser/components/shell/test/browser_420786.js
+++ b/browser/components/shell/test/browser_420786.js
@@ -47,17 +47,18 @@ function onPageLoad() {
        "Wallpaper file GConf key is correct");
     is(gconf.getString(DG_OPTION_KEY), expectedGConfPosition,
        "Wallpaper position GConf key is correct");
   }
 
   checkWallpaper(Ci.nsIShellService.BACKGROUND_TILE, "wallpaper");
   checkWallpaper(Ci.nsIShellService.BACKGROUND_STRETCH, "stretched");
   checkWallpaper(Ci.nsIShellService.BACKGROUND_CENTER, "centered");
-  checkWallpaper(Ci.nsIShellService.BACKGROUND_FILL, "centered");
+  checkWallpaper(Ci.nsIShellService.BACKGROUND_FILL, "zoom");
+  checkWallpaper(Ci.nsIShellService.BACKGROUND_FIT, "scaled");
 
   // Restore GConf and wallpaper
 
   gconf.setString(DG_IMAGE_KEY, prevImageKey);
   gconf.setString(DG_OPTION_KEY, prevOptionKey);
   gconf.setBool(DG_DRAW_BG_KEY, prevDrawBgKey);
 
   wpFile.remove(false);
--- a/browser/components/tabview/Makefile.in
+++ b/browser/components/tabview/Makefile.in
@@ -38,16 +38,14 @@
 
 DEPTH		= ../../..
 topsrcdir	= @top_srcdir@
 srcdir		= @srcdir@
 VPATH		= @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
-ifdef ENABLE_TESTS
-	DIRS += test
-endif
+TEST_DIRS += test
 
 include $(topsrcdir)/config/rules.mk
 
 libs::
 	$(NSINSTALL) $(srcdir)/modules/* $(FINAL_TARGET)/modules/tabview
--- a/browser/components/tabview/groupitems.js
+++ b/browser/components/tabview/groupitems.js
@@ -1851,17 +1851,17 @@ GroupItem.prototype = Utils.extend(new I
   //    closedLastTab - boolean indicates the last tab has just been closed
   newTab: function GroupItem_newTab(url, options) {
     if (options && options.closedLastTab)
       UI.closedLastTabInTabView = true;
 
     UI.setActive(this, { dontSetActiveTabInGroup: true });
 
     let dontZoomIn = !!(options && options.dontZoomIn);
-    return gBrowser.loadOneTab(url || "about:blank", { inBackground: dontZoomIn });
+    return gBrowser.loadOneTab(url || gWindow.BROWSER_NEW_TAB_URL, { inBackground: dontZoomIn });
   },
 
   // ----------
   // Function: reorderTabItemsBasedOnTabOrder
   // Reorders the tabs in a groupItem based on the arrangment of the tabs
   // shown in the tab bar. It does it by sorting the children
   // of the groupItem by the positions of their respective tabs in the
   // tab bar.
--- a/browser/components/tabview/iq.js
+++ b/browser/components/tabview/iq.js
@@ -787,16 +787,17 @@ let events = [
   'keydown',
   'keypress',
   'mouseup',
   'mousedown',
   'mouseover',
   'mouseout',
   'mousemove',
   'click',
+  'dblclick',
   'resize',
   'change',
   'blur',
   'focus'
 ];
 
 events.forEach(function(event) {
   iQClass.prototype[event] = function(func) {
--- a/browser/components/tabview/tabitems.js
+++ b/browser/components/tabview/tabitems.js
@@ -98,17 +98,17 @@ function TabItem(tab, options) {
   this.isStacked = false;
   this.url = "";
 
   // Read off the total vertical and horizontal padding on the tab container
   // and cache this value, as it must be the same for every TabItem.
   if (Utils.isEmptyObject(TabItems.tabItemPadding)) {
     TabItems.tabItemPadding.x = parseInt($div.css('padding-left'))
         + parseInt($div.css('padding-right'));
-  
+
     TabItems.tabItemPadding.y = parseInt($div.css('padding-top'))
         + parseInt($div.css('padding-bottom'));
   }
   
   this.bounds = new Rect(0,0,1,1);
 
   this._lastTabUpdateTime = Date.now();
 
--- a/browser/components/tabview/test/browser_tabview_bug604098.js
+++ b/browser/components/tabview/test/browser_tabview_bug604098.js
@@ -25,21 +25,17 @@ function test1() {
   is(groupItems.length, 1, "there is one groupItem");
 
   whenTabViewIsHidden(function() {
     gBrowser.selectedTab = gBrowser.tabs[0];
     is(groupItems.length, 2, "there are two groupItems");
     closeGroupItem(groupItems[1], finish);
   });
 
-  // first click
-  mouseClick(contentElement, 0);
-  // second click
-  mouseClick(contentElement, 0);
+  // double click
+  doubleClick(contentElement, 0);
 }
 
-function mouseClick(targetElement, buttonCode) {
+function doubleClick(targetElement, buttonCode) {
   EventUtils.sendMouseEvent(
-    { type: "mousedown", button: buttonCode }, targetElement, contentWindow);
-  EventUtils.sendMouseEvent(
-    { type: "mouseup", button: buttonCode }, targetElement, contentWindow);
+    { type: "dblclick", button: buttonCode }, targetElement, contentWindow);
 }
 
--- a/browser/components/tabview/test/browser_tabview_bug626455.js
+++ b/browser/components/tabview/test/browser_tabview_bug626455.js
@@ -75,17 +75,17 @@ function testLeavePage() {
 
 function finishTest() {
   is(gBrowser.tabs.length, 1,
      "The total number of tab is 1 after leaving the page");
   is(contentWindow.TabItems.getItems().length, 1,
      "The total number of tab items is 1 after leaving the page");
 
   let location = gBrowser.browsers[0].currentURI.spec;
-  is(location, "about:blank", "The open tab is the expected one");
+  is(location, BROWSER_NEW_TAB_URL, "The open tab is the expected one");
 
   isnot(contentWindow.GroupItems.getActiveGroupItem(), activeGroup,
      "Active group is no longer the same");
 
   is(contentWindow.GroupItems.groupItems.length, 1,
      "Only one group is open");
 
   finish();
--- a/browser/components/tabview/test/browser_tabview_bug626791.js
+++ b/browser/components/tabview/test/browser_tabview_bug626791.js
@@ -74,18 +74,17 @@ function test() {
     prefix = 'create-orphan';
     assertNumberOfTabs(1);
     assertToolbarButtonNotExists();
 
     let width = cw.innerWidth;
     let height = cw.innerHeight;
 
     let body = cw.document.body;
-    EventUtils.synthesizeMouse(body, width - 10, height - 10, {}, cw);
-    EventUtils.synthesizeMouse(body, width - 10, height - 10, {}, cw);
+    EventUtils.synthesizeMouse(body, width - 10, height - 10, { clickCount: 2 }, cw);
 
     whenTabViewIsHidden(function () {
       assertNumberOfTabs(2);
       assertToolbarButtonExists();
 
       next();
     }, win);
   }
--- a/browser/components/tabview/test/browser_tabview_snapping.js
+++ b/browser/components/tabview/test/browser_tabview_snapping.js
@@ -29,24 +29,17 @@ function onTabViewWindowLoaded(win) {
   ok(secondGroup.getBounds().equals(secondBox), "This second group got its bounds");
   
   // A third group is created later, but multiple functions need access to it.
   let thirdGroup = null;
 
   is(secondGroup.getBounds().top - firstGroup.getBounds().bottom, 40,
     "There's currently 40 px between the first group and second group");
 
-  // set double click interval to negative so quick drag and drop doesn't 
-  // trigger the double click code.
-  let origDBlClickInterval = contentWindow.UI.DBLCLICK_INTERVAL;
-  contentWindow.UI.DBLCLICK_INTERVAL = -1;
-
   let endGame = function() {
-    contentWindow.UI.DBLCLICK_INTERVAL = origDBlClickInterval;
-
     firstGroup.container.parentNode.removeChild(firstGroup.container);
     firstGroup.close();
     thirdGroup.container.parentNode.removeChild(thirdGroup.container);
     thirdGroup.close();
 
     win.close();
     ok(win.closed, "new window is closed");
     finish();
--- a/browser/components/tabview/ui.js
+++ b/browser/components/tabview/ui.js
@@ -46,26 +46,16 @@
 // Title: ui.js
 
 let Keys = { meta: false };
 
 // ##########
 // Class: UI
 // Singleton top-level UI manager.
 let UI = {
-  // Constant: DBLCLICK_INTERVAL
-  // Defines the maximum time (in ms) between two clicks for it to count as
-  // a double click.
-  DBLCLICK_INTERVAL: 500,
-
-  // Constant: DBLCLICK_OFFSET
-  // Defines the maximum offset (in pixels) between two clicks for it to count as
-  // a double click.
-  DBLCLICK_OFFSET: 5,
-
   // Variable: _frameInitialized
   // True if the Tab View UI frame has been initialized.
   _frameInitialized: false,
 
   // Variable: _pageBounds
   // Stores the page bounds.
   _pageBounds: null,
 
@@ -95,21 +85,16 @@ let UI = {
   // TabView UI and re-orders the tabs when switcing back to main browser.
   _reorderTabsOnHide: [],
 
   // Variable: _currentTab
   // Keeps track of which xul:tab we are currently on.
   // Used to facilitate zooming down from a previous tab.
   _currentTab: null,
 
-  // Variable: _lastClick
-  // Keeps track of the time of last click event to detect double click.
-  // Used to create tabs on double-click since we cannot attach 'dblclick'
-  _lastClick: 0,
-
   // Variable: _eventListeners
   // Keeps track of event listeners added to the AllTabs object.
   _eventListeners: {},
 
   // Variable: _cleanupFunctions
   // An array of functions to be called at uninit time
   _cleanupFunctions: [],
   
@@ -207,48 +192,36 @@ let UI = {
       iQ(gTabViewFrame.contentDocument).mousedown(function(e) {
         if (iQ(":focus").length > 0) {
           iQ(":focus").each(function(element) {
             // don't fire blur event if the same input element is clicked.
             if (e.target != element && element.nodeName == "INPUT")
               element.blur();
           });
         }
-        if (e.originalTarget.id == "content") {
-          if (!Utils.isLeftClick(e)) {
-            self._lastClick = 0;
-            self._lastClickPositions = null;
-          } else {
-            // Create a group with one tab on double click
-            if (Date.now() - self._lastClick <= self.DBLCLICK_INTERVAL && 
-                (self._lastClickPositions.x - self.DBLCLICK_OFFSET) <= e.clientX &&
-                (self._lastClickPositions.x + self.DBLCLICK_OFFSET) >= e.clientX &&
-                (self._lastClickPositions.y - self.DBLCLICK_OFFSET) <= e.clientY &&
-                (self._lastClickPositions.y + self.DBLCLICK_OFFSET) >= e.clientY) {
+        if (e.originalTarget.id == "content" &&
+            Utils.isLeftClick(e) &&
+            e.detail == 1) {
+          self._createGroupItemOnDrag(e);
+        }
+      });
 
-              let box =
-                new Rect(e.clientX - Math.floor(TabItems.tabWidth/2),
-                         e.clientY - Math.floor(TabItems.tabHeight/2),
-                         TabItems.tabWidth, TabItems.tabHeight);
-              box.inset(-30, -30);
-
-              let opts = {immediately: true, bounds: box};
-              let groupItem = new GroupItem([], opts);
-              groupItem.newTab();
+      iQ(gTabViewFrame.contentDocument).dblclick(function(e) {
+        // Create a group with one tab on double click
+        let box =
+          new Rect(e.clientX - Math.floor(TabItems.tabWidth/2),
+                   e.clientY - Math.floor(TabItems.tabHeight/2),
+                   TabItems.tabWidth, TabItems.tabHeight);
+        box.inset(-30, -30);
 
-              self._lastClick = 0;
-              self._lastClickPositions = null;
-              gTabView.firstUseExperienced = true;
-            } else {
-              self._lastClick = Date.now();
-              self._lastClickPositions = new Point(e.clientX, e.clientY);
-              self._createGroupItemOnDrag(e);
-            }
-          }
-        }
+        let opts = {immediately: true, bounds: box};
+        let groupItem = new GroupItem([], opts);
+        groupItem.newTab();
+
+        gTabView.firstUseExperienced = true;
       });
 
       iQ(window).bind("unload", function() {
         self.uninit();
       });
 
       // ___ setup DOMWillOpenModalDialog message handler
       let mm = gWindow.messageManager;
--- a/browser/components/thumbnails/PageThumbs.jsm
+++ b/browser/components/thumbnails/PageThumbs.jsm
@@ -72,51 +72,62 @@ let PageThumbs = {
   },
 
   /**
    * Creates a canvas containing a thumbnail depicting the given window.
    * @param aWindow The DOM window to capture a thumbnail from.
    * @return The newly created canvas containing the image data.
    */
   capture: function PageThumbs_capture(aWindow) {
+    let telemetryCaptureTime = new Date();
     let [sw, sh, scale] = this._determineCropSize(aWindow);
 
     let canvas = this._createCanvas();
     let ctx = canvas.getContext("2d");
 
     // Scale the canvas accordingly.
     ctx.scale(scale, scale);
 
     try {
       // Draw the window contents to the canvas.
       ctx.drawWindow(aWindow, 0, 0, sw, sh, THUMBNAIL_BG_COLOR,
                      ctx.DRAWWINDOW_DO_NOT_FLUSH);
     } catch (e) {
       // We couldn't draw to the canvas for some reason.
     }
 
+    Services.telemetry.getHistogramById("FX_THUMBNAILS_CAPTURE_TIME_MS")
+      .add(new Date() - telemetryCaptureTime);
+
     return canvas;
   },
 
   /**
    * Stores the image data contained in the given canvas to the underlying
    * storage.
    * @param aKey The key to use for the storage.
    * @param aCanvas The canvas containing the thumbnail's image data.
    * @param aCallback The function to be called when the canvas data has been
    *                  stored (optional).
    */
   store: function PageThumbs_store(aKey, aCanvas, aCallback) {
-    let self = this;
+    let telemetryStoreTime = new Date();
 
     function finish(aSuccessful) {
+      if (aSuccessful) {
+        Services.telemetry.getHistogramById("FX_THUMBNAILS_STORE_TIME_MS")
+          .add(new Date() - telemetryStoreTime);
+      }
+
       if (aCallback)
         aCallback(aSuccessful);
     }
 
+    let self = this;
+
     // Get a writeable cache entry.
     PageThumbsCache.getWriteEntry(aKey, function (aEntry) {
       if (!aEntry) {
         finish(false);
         return;
       }
 
       // Extract image data from the canvas.
--- a/browser/components/thumbnails/PageThumbsProtocol.js
+++ b/browser/components/thumbnails/PageThumbsProtocol.js
@@ -124,22 +124,28 @@ Channel.prototype = {
     this._listener = aListener;
     this._context = aContext;
 
     this._isPending = true;
     this._wasOpened = true;
 
     // Try to read the data from the thumbnail cache.
     this._readCache(function (aData) {
+      let telemetryThumbnailFound = true;
+
       // Update response if there's no data.
       if (!aData) {
         this._responseStatus = 404;
         this._responseText = "Not Found";
+        telemetryThumbnailFound = false;
       }
 
+      Services.telemetry.getHistogramById("FX_THUMBNAILS_HIT_OR_MISS")
+        .add(telemetryThumbnailFound);
+
       this._startRequest();
 
       if (!this.canceled) {
         this._addToLoadGroup();
 
         if (aData)
           this._serveData(aData);
 
--- a/browser/components/thumbnails/test/head.js
+++ b/browser/components/thumbnails/test/head.js
@@ -1,12 +1,15 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
-Cu.import("resource:///modules/PageThumbs.jsm");
+let tmp = {};
+Cu.import("resource:///modules/PageThumbs.jsm", tmp);
+let PageThumbs = tmp.PageThumbs;
+let PageThumbsCache = tmp.PageThumbsCache;
 
 registerCleanupFunction(function () {
   while (gBrowser.tabs.length > 1)
     gBrowser.removeTab(gBrowser.tabs[1]);
 });
 
 let cachedXULDocument;
 
@@ -140,16 +143,17 @@ function getXULDocument(aCallback) {
   iframe.setAttribute("src", "chrome://global/content/mozilla.xhtml");
 
   iframe.addEventListener("DOMContentLoaded", function onLoad() {
     iframe.removeEventListener("DOMContentLoaded", onLoad, false);
     aCallback(cachedXULDocument = iframe.contentDocument);
   }, false);
 
   doc.body.appendChild(iframe);
+  registerCleanupFunction(function () { doc.body.removeChild(iframe); });
 }
 
 /**
  * Checks the top-left pixel of a given canvas' 2d context for a given color.
  * @param aContext The 2D context of a canvas.
  * @param aRed The red component's intensity.
  * @param aGreen The green component's intensity.
  * @param aBlue The blue component's intensity.
--- a/browser/config/mozconfigs/linux32/release
+++ b/browser/config/mozconfigs/linux32/release
@@ -13,8 +13,11 @@ mk_add_options PROFILE_GEN_SCRIPT='$(PYT
 
 # Needed to enable breakpad in application.ini
 export MOZILLA_OFFICIAL=1
 
 export MOZ_TELEMETRY_REPORTING=1
 
 # Treat warnings as errors in directories with FAIL_ON_WARNINGS.
 ac_add_options --enable-warnings-as-errors
+
+# Enable parallel compiling
+mk_add_options MOZ_MAKE_FLAGS="-j4"
--- a/browser/config/mozconfigs/linux64/release
+++ b/browser/config/mozconfigs/linux64/release
@@ -13,8 +13,11 @@ mk_add_options PROFILE_GEN_SCRIPT='$(PYT
 
 # Needed to enable breakpad in application.ini
 export MOZILLA_OFFICIAL=1
 
 export MOZ_TELEMETRY_REPORTING=1
 
 # Treat warnings as errors in directories with FAIL_ON_WARNINGS.
 ac_add_options --enable-warnings-as-errors
+
+# Enable parallel compiling
+mk_add_options MOZ_MAKE_FLAGS="-j4"
--- a/browser/config/mozconfigs/macosx-universal/release
+++ b/browser/config/mozconfigs/macosx-universal/release
@@ -9,8 +9,11 @@ ac_add_options --enable-official-brandin
 
 # Needed to enable breakpad in application.ini
 export MOZILLA_OFFICIAL=1
 
 export MOZ_TELEMETRY_REPORTING=1
 
 # Treat warnings as errors in directories with FAIL_ON_WARNINGS.
 ac_add_options --enable-warnings-as-errors
+
+# Enable parallel compiling
+mk_add_options MOZ_MAKE_FLAGS="-j4"
--- a/browser/config/mozconfigs/win32/debug
+++ b/browser/config/mozconfigs/win32/debug
@@ -1,7 +1,9 @@
 ac_add_options --enable-debug
 ac_add_options --enable-trace-malloc
 
 # Needed to enable breakpad in application.ini
 export MOZILLA_OFFICIAL=1
 
 mk_add_options MOZ_MAKE_FLAGS=-j1
+
+. $topsrcdir/browser/config/mozconfigs/win32/vs2010-mozconfig
--- a/browser/config/mozconfigs/win32/l10n-mozconfig
+++ b/browser/config/mozconfigs/win32/l10n-mozconfig
@@ -1,4 +1,6 @@
 ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL}
 ac_add_options --enable-update-packaging
 ac_add_options --enable-official-branding
 ac_add_options --with-l10n-base=../../l10n-central
+
+. $topsrcdir/browser/config/mozconfigs/win32/vs2010-mozconfig
--- a/browser/config/mozconfigs/win32/nightly
+++ b/browser/config/mozconfigs/win32/nightly
@@ -9,8 +9,10 @@ ac_add_options --enable-jemalloc
 ac_add_options --enable-js-diagnostics
 
 # Needed to enable breakpad in application.ini
 export MOZILLA_OFFICIAL=1
 
 export MOZ_TELEMETRY_REPORTING=1
 
 mk_add_options MOZ_MAKE_FLAGS=-j1
+
+. $topsrcdir/browser/config/mozconfigs/win32/vs2010-mozconfig
--- a/browser/config/mozconfigs/win32/release
+++ b/browser/config/mozconfigs/win32/release
@@ -6,8 +6,10 @@ ac_add_options --enable-update-channel=$
 ac_add_options --enable-update-packaging
 ac_add_options --enable-jemalloc
 ac_add_options --enable-official-branding
 
 # Needed to enable breakpad in application.ini
 export MOZILLA_OFFICIAL=1
 
 export MOZ_TELEMETRY_REPORTING=1
+
+. $topsrcdir/browser/config/mozconfigs/win32/vs2010-mozconfig
--- a/browser/config/version.txt
+++ b/browser/config/version.txt
@@ -1,1 +1,1 @@
-12.0a1
+13.0a1
--- a/browser/devtools/highlighter/Makefile.in
+++ b/browser/devtools/highlighter/Makefile.in
@@ -47,13 +47,11 @@ include $(DEPTH)/config/autoconf.mk
 EXTRA_JS_MODULES = \
 	inspector.jsm \
 	domplate.jsm \
 	InsideOutBox.jsm \
 	TreePanel.jsm \
 	highlighter.jsm \
 	$(NULL)
 
-ifdef ENABLE_TESTS
- 	DIRS += test
-endif
+TEST_DIRS += test
 
 include $(topsrcdir)/config/rules.mk
--- a/browser/devtools/scratchpad/Makefile.in
+++ b/browser/devtools/scratchpad/Makefile.in
@@ -38,16 +38,14 @@
 
 DEPTH		= ../../..
 topsrcdir	= @top_srcdir@
 srcdir		= @srcdir@
 VPATH		= @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
-ifdef ENABLE_TESTS
-	DIRS += test
-endif
+TEST_DIRS += test
 
 include $(topsrcdir)/config/rules.mk
 
 libs::
 	$(NSINSTALL) $(srcdir)/*.jsm $(FINAL_TARGET)/modules/devtools
--- a/browser/devtools/shared/Makefile.in
+++ b/browser/devtools/shared/Makefile.in
@@ -40,16 +40,14 @@
 
 DEPTH		= ../../..
 topsrcdir	= @top_srcdir@
 srcdir		= @srcdir@
 VPATH		= @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
-ifdef ENABLE_TESTS
-	DIRS += test
-endif
+TEST_DIRS += test
 
 include $(topsrcdir)/config/rules.mk
 
 libs::
 	$(NSINSTALL) $(srcdir)/*.jsm $(FINAL_TARGET)/modules/devtools
--- a/browser/devtools/sourceeditor/Makefile.in
+++ b/browser/devtools/sourceeditor/Makefile.in
@@ -39,19 +39,17 @@
 
 DEPTH		= ../../..
 topsrcdir	= @top_srcdir@
 srcdir		= @srcdir@
 VPATH		= @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
-ifdef ENABLE_TESTS
-	DIRS += test
-endif
+TEST_DIRS += test
 
 EXTRA_JS_MODULES = \
 	source-editor.jsm \
 	source-editor-orion.jsm \
 	source-editor-textarea.jsm \
 	source-editor-ui.jsm \
 	$(NULL)
 
--- a/browser/devtools/styleeditor/Makefile.in
+++ b/browser/devtools/styleeditor/Makefile.in
@@ -37,16 +37,14 @@
 
 DEPTH		= ../../..
 topsrcdir	= @top_srcdir@
 srcdir		= @srcdir@
 VPATH		= @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
-ifdef ENABLE_TESTS
-DIRS += test
-endif
+TEST_DIRS += test
 
 include $(topsrcdir)/config/rules.mk
 
 libs::
 	$(NSINSTALL) $(srcdir)/*.jsm $(FINAL_TARGET)/modules/devtools
--- a/browser/devtools/styleeditor/styleeditor.css
+++ b/browser/devtools/styleeditor/styleeditor.css
@@ -68,16 +68,24 @@ li.error > .stylesheet-info > .styleshee
   display: inline;
   cursor: pointer;
 }
 
 .splitview-nav > li > hgroup.stylesheet-info {
   -moz-box-pack: center;
 }
 
+.stylesheet-name {
+  white-space: nowrap;
+}
+
+li.unsaved > hgroup > h1 > .stylesheet-name:before {
+  content: "*";
+}
+
 .stylesheet-enabled {
   display: -moz-box;
 }
 
 .stylesheet-saveButton {
   display: none;
 }
 
--- a/browser/devtools/styleeditor/test/browser_styleeditor_new.js
+++ b/browser/devtools/styleeditor/test/browser_styleeditor_new.js
@@ -29,16 +29,27 @@ function run(aChrome)
   is(aChrome.editors.length, 2,
      "there is 2 stylesheets initially");
 }
 
 let gAddedCount = 0;  // to add new stylesheet after the 2 initial stylesheets
 let gNewEditor;       // to make sure only one new stylesheet got created
 let gUpdateCount = 0; // to make sure only one Update event is triggered
 let gCommitCount = 0; // to make sure only one Commit event is triggered
+let gTransitionEndCount = 0;
+
+function finishOnTransitionEndAndCommit() {
+  if (gCommitCount && gTransitionEndCount) {
+    is(gUpdateCount, 1, "received one Update event");
+    is(gCommitCount, 1, "received one Commit event");
+    is(gTransitionEndCount, 1, "received one transitionend event");
+
+    finish();
+  }
+}
 
 function testEditorAdded(aChrome, aEditor)
 {
   gAddedCount++;
   if (gAddedCount == 2) {
     waitForFocus(function () { // create a new style sheet
       let newButton = gChromeWindow.document.querySelector(".style-editor-newButton");
       EventUtils.synthesizeMouseAtCenter(newButton, {}, gChromeWindow);
@@ -81,16 +92,26 @@ function testEditorAdded(aChrome, aEdito
 
         for each (let c in TESTCASE_CSS_SOURCE) {
           EventUtils.synthesizeKey(c, {}, gChromeWindow);
         }
 
         is(aEditor.sourceEditor.getText(), TESTCASE_CSS_SOURCE + "}",
            "rule bracket has been auto-closed");
 
+        // we know that the testcase above will start a CSS transition
+        content.addEventListener("transitionend", function () {
+          gTransitionEndCount++;
+
+          let computedStyle = content.getComputedStyle(content.document.body, null);
+          is(computedStyle.backgroundColor, "rgb(255, 0, 0)",
+             "content's background color has been updated to red");
+
+          executeSoon(finishOnTransitionEndAndCommit);
+        }, false);
       }, gChromeWindow) ;
     },
 
     onUpdate: function (aEditor) {
       gUpdateCount++;
 
       ok(content.document.documentElement.classList.contains(TRANSITION_CLASS),
          "StyleEditor's transition class has been added to content");
@@ -104,32 +125,23 @@ function testEditorAdded(aChrome, aEdito
       ok(aEditor.hasFlag("unsaved"),
          "new editor has UNSAVED flag after modification");
 
       let summary = aChrome.getSummaryElementForEditor(aEditor);
       let ruleCount = summary.querySelector(".stylesheet-rule-count").textContent;
       is(parseInt(ruleCount), 1,
          "new editor shows 1 rule after modification");
 
-      let computedStyle = content.getComputedStyle(content.document.body, null);
-      is(computedStyle.backgroundColor, "rgb(255, 0, 0)",
-         "content's background color has been updated to red");
-
       ok(!content.document.documentElement.classList.contains(TRANSITION_CLASS),
          "StyleEditor's transition class has been removed from content");
 
-      executeSoon(function () {
-        is(gUpdateCount, 1, "received only one Update event (throttle)");
-        is(gCommitCount, 1, "received only one Commit event (throttle)");
+      aEditor.removeActionListener(listener);
+      gNewEditor = null;
 
-        aEditor.removeActionListener(listener);
-
-        gNewEditor = null;
-        finish();
-      });
+      executeSoon(finishOnTransitionEndAndCommit);
     }
   };
 
   aEditor.addActionListener(listener);
   if (aEditor.sourceEditor) {
     listener.onAttach(aEditor);
   }
 }
--- a/browser/devtools/tilt/Makefile.in
+++ b/browser/devtools/tilt/Makefile.in
@@ -37,17 +37,15 @@
 
 DEPTH		= ../../..
 topsrcdir	= @top_srcdir@
 srcdir		= @srcdir@
 VPATH		= @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
-ifdef ENABLE_TESTS
-	DIRS += test
-endif
+TEST_DIRS += test
 
 include $(topsrcdir)/config/rules.mk
 
 libs::
 	$(NSINSTALL) $(srcdir)/*.jsm $(FINAL_TARGET)/modules/devtools
 	$(NSINSTALL) $(srcdir)/*.js $(FINAL_TARGET)/modules/devtools
--- a/browser/devtools/tilt/Tilt.jsm
+++ b/browser/devtools/tilt/Tilt.jsm
@@ -225,35 +225,33 @@ Tilt.prototype = {
     this.tiltButton.checked = false;
   },
 
   /**
    * Handles the event fired when a tab is selected.
    */
   _onTabSelect: function T__onTabSelect()
   {
-    if (this.visualizers[this.currentWindowId]) {
+    if (this.currentInstance) {
       Services.obs.notifyObservers(null, TILT_NOTIFICATIONS.SHOWN, null);
     } else {
       Services.obs.notifyObservers(null, TILT_NOTIFICATIONS.HIDDEN, null);
     }
   },
 
   /**
    * A node was selected in the Inspector.
    * Called from InspectorUI.
    *
    * @param {Element} aNode
    *                  the newly selected node
    */
   update: function T_update(aNode) {
-    let id = this.currentWindowId;
-
-    if (this.visualizers[id]) {
-      this.visualizers[id].presenter.highlightNode(aNode);
+    if (this.currentInstance) {
+      this.currentInstance.presenter.highlightNode(aNode);
     }
   },
 
   /**
    * Add the browser event listeners to handle state changes.
    * Called from InspectorUI.
    */
   setup: function T_setup()
@@ -272,27 +270,28 @@ Tilt.prototype = {
     Services.obs.addObserver(
       this._whenInitializing.bind(this), TILT_NOTIFICATIONS.INITIALIZING, false);
     Services.obs.addObserver(
       this._whenDestroyed.bind(this), TILT_NOTIFICATIONS.DESTROYED, false);
     Services.obs.addObserver(
       this._whenShown.bind(this), TILT_NOTIFICATIONS.SHOWN, false);
     Services.obs.addObserver(
       this._whenHidden.bind(this), TILT_NOTIFICATIONS.HIDDEN, false);
+
     Services.obs.addObserver(function(aSubject, aTopic, aWinId) {
       this.destroy(aWinId); }.bind(this),
       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.visualizers[this.currentWindowId]) {
+      if (this.currentInstance) {
         this.chromeWindow.InspectorUI.stopInspecting();
         this.inspectButton.disabled = true;
         this.highlighterContainer.style.display = "none";
       }
     }.bind(this);
 
     let onClosed = function() {
       this.inspectButton.disabled = false;
@@ -321,18 +320,26 @@ Tilt.prototype = {
            (TiltGL.isWebGLForceEnabled() || TiltGL.isWebGLSupported()));
   },
 
   /**
    * Gets the ID of the current window object to identify the visualizer.
    */
   get currentWindowId()
   {
-    let gBrowser = this.chromeWindow.gBrowser;
-    return TiltUtils.getWindowId(gBrowser.selectedBrowser.contentWindow);
+    return TiltUtils.getWindowId(
+      this.chromeWindow.gBrowser.selectedBrowser.contentWindow);
+  },
+
+  /**
+   * Gets the visualizer instance for the current tab.
+   */
+  get currentInstance()
+  {
+    return this.visualizers[this.currentWindowId];
   },
 
   /**
    * Gets the Tilt button in the Inspector toolbar.
    */
   get tiltButton()
   {
     return this.chromeWindow.document.getElementById(
--- a/browser/devtools/tilt/TiltVisualizer.jsm
+++ b/browser/devtools/tilt/TiltVisualizer.jsm
@@ -1047,16 +1047,17 @@ TiltVisualizer.Controller.prototype = {
     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);
   },
 
   /**
    * Removes all added events listeners required by this controller.
@@ -1069,16 +1070,17 @@ TiltVisualizer.Controller.prototype = {
     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);
   },
 
   /**
    * Function called each frame, updating the visualization camera transforms.
    *
@@ -1212,34 +1214,42 @@ TiltVisualizer.Controller.prototype = {
   },
 
   /**
    * Called when a key is released.
    */
   onKeyUp: function TVC_onKeyUp(e)
   {
     let code = e.keyCode || e.which;
-    let tilt = this.presenter.chromeWindow.Tilt;
 
-    if (code === e.DOM_VK_ESCAPE) {
-      tilt.destroy(tilt.currentWindowId, true);
-      return;
-    }
     if (code === e.DOM_VK_X) {
       this.presenter.deleteNode();
     }
-
     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)
+  {
+    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) {
     this.arcball.cancelKeyEvents();
   },
 
   /**
    * Called when the content window of the current browser is resized.
--- a/browser/devtools/tilt/test/browser_tilt_05_destruction-esc.js
+++ b/browser/devtools/tilt/test/browser_tilt_05_destruction-esc.js
@@ -26,12 +26,15 @@ function test() {
 }
 
 function cleanup() {
   let id = TiltUtils.getWindowId(gBrowser.selectedBrowser.contentWindow);
 
   is(Tilt.visualizers[id], null,
     "The current instance of the visualizer wasn't destroyed properly.");
 
+  ok(InspectorUI.highlighter && InspectorUI.breadcrumbs,
+    "The Inspector should not close while Tilt is opened.");
+
   Services.obs.removeObserver(cleanup, DESTROYED);
   gBrowser.removeCurrentTab();
   finish();
 }
--- a/browser/devtools/tilt/test/browser_tilt_utils05.js
+++ b/browser/devtools/tilt/test/browser_tilt_utils05.js
@@ -1,13 +1,15 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 "use strict";
 
-Cu.import("resource:///modules/devtools/LayoutHelpers.jsm");
+let tmp = {};
+Cu.import("resource:///modules/devtools/LayoutHelpers.jsm", tmp);
+let LayoutHelpers = tmp.LayoutHelpers;
 
 function init(callback) {
   let iframe = gBrowser.ownerDocument.createElement("iframe");
 
   iframe.addEventListener("load", function onLoad() {
     iframe.removeEventListener("load", onLoad, true);
     callback(iframe);
 
--- a/browser/devtools/webconsole/test/browser_gcli_commands.js
+++ b/browser/devtools/webconsole/test/browser_gcli_commands.js
@@ -1,16 +1,18 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // For more information on GCLI see:
 // - https://github.com/mozilla/gcli/blob/master/docs/index.md
 // - https://wiki.mozilla.org/DevTools/Features/GCLI
 
-Components.utils.import("resource:///modules/gcli.jsm");
+let tmp = {};
+Components.utils.import("resource:///modules/gcli.jsm", tmp);
+let gcli = tmp.gcli;
 
 let hud;
 let gcliterm;
 
 registerCleanupFunction(function() {
   gcliterm = undefined;
   hud = undefined;
   Services.prefs.clearUserPref("devtools.gcli.enable");
--- a/browser/devtools/webconsole/test/browser_gcli_helpers.js
+++ b/browser/devtools/webconsole/test/browser_gcli_helpers.js
@@ -1,16 +1,18 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // For more information on GCLI see:
 // - https://github.com/mozilla/gcli/blob/master/docs/index.md
 // - https://wiki.mozilla.org/DevTools/Features/GCLI
 
-Components.utils.import("resource:///modules/gcli.jsm");
+let tmp = {};
+Components.utils.import("resource:///modules/gcli.jsm", tmp);
+let gcli = tmp.gcli;
 
 let hud;
 let gcliterm;
 
 registerCleanupFunction(function() {
   gcliterm = undefined;
   hud = undefined;
   Services.prefs.clearUserPref("devtools.gcli.enable");