Merge from mozilla-central.
authorDavid Anderson <danderson@mozilla.com>
Tue, 13 Mar 2012 16:10:05 -0700
changeset 105991 7eb3fbbd50d7d1ce99527ee27f8b619c87e96fc7
parent 105989 e159956eb94c48c1f6f0bffea296f2305b45ba38 (current diff)
parent 88953 c71845b3b2a6a1d1708e4a1ed23c0244cb0d4a8c (diff)
child 105992 ae003e1f04ff21c4e9d2229e795321766fadac89
push id1075
push uservporof@mozilla.com
push dateThu, 13 Sep 2012 10:46:49 +0000
treeherderfx-team@f39786e8364d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone14.0a1
Merge from mozilla-central.
.hgtags
accessible/src/base/TextAttrs.cpp
accessible/src/base/TextAttrs.h
accessible/src/base/nsAccUtils.cpp
accessible/src/base/nsAccUtils.h
accessible/src/base/nsAccessible.cpp
accessible/src/base/nsApplicationAccessible.cpp
accessible/src/base/nsApplicationAccessible.h
accessible/src/base/nsCoreUtils.cpp
accessible/src/base/nsCoreUtils.h
accessible/src/html/nsHTMLTableAccessible.cpp
accessible/src/msaa/nsAccessNodeWrap.cpp
accessible/src/xul/nsXULMenuAccessible.cpp
accessible/src/xul/nsXULMenuAccessible.h
accessible/tests/mochitest/Makefile.in
accessible/tests/mochitest/attributes/test_obj_group.xul
accessible/tests/mochitest/table/test_layoutguess.html
accessible/tests/mochitest/test_nsIAccessNode_utils.html
aclocal.m4
browser/app/profile/firefox.js
browser/base/content/browser.js
browser/base/content/browser.xul
browser/base/content/highlighter.css
browser/base/content/newtab/toolbar.js
browser/components/nsBrowserGlue.js
browser/config/version.txt
browser/devtools/highlighter/test/Makefile.in
browser/devtools/highlighter/test/browser_inspector_editor.js
browser/devtools/styleinspector/CssHtmlTree.jsm
browser/devtools/styleinspector/CssLogic.jsm
browser/devtools/styleinspector/StyleInspector.jsm
browser/locales/en-US/chrome/browser/browser.dtd
browser/locales/en-US/chrome/browser/browser.properties
browser/locales/en-US/chrome/browser/devtools/inspector.properties
browser/locales/en-US/chrome/browser/devtools/styleinspector.properties
browser/themes/gnomestripe/newtab/strip.png
browser/themes/gnomestripe/newtab/toolbar.png
browser/themes/pinstripe/newtab/strip.png
browser/themes/pinstripe/newtab/toolbar.png
browser/themes/winstripe/newtab/strip.png
browser/themes/winstripe/newtab/toolbar.png
build/mobile/robocop/Actions.java.in
caps/src/nsPrincipal.cpp
config/autoconf.mk.in
config/milestone.txt
configure.in
content/base/public/nsDOMFile.h
content/base/public/nsINode.h
content/base/src/nsAttrValue.cpp
content/base/src/nsContentList.h
content/base/src/nsDOMAttributeMap.h
content/base/src/nsDOMFile.cpp
content/base/src/nsDOMFileReader.cpp
content/base/src/nsDocument.cpp
content/base/src/nsDocument.h
content/base/src/nsEventSource.cpp
content/base/src/nsEventSource.h
content/base/src/nsGenericElement.cpp
content/base/src/nsGkAtomList.h
content/base/src/nsMappedAttributes.cpp
content/base/src/nsNameSpaceManager.cpp
content/base/src/nsNodeInfoManager.cpp
content/base/src/nsObjectLoadingContent.cpp
content/base/src/nsRange.cpp
content/base/src/nsWebSocket.cpp
content/base/src/nsWebSocket.h
content/base/src/nsXMLHttpRequest.cpp
content/base/src/nsXMLHttpRequest.h
content/base/test/Makefile.in
content/canvas/src/CanvasImageCache.cpp
content/events/src/nsDOMDataTransfer.cpp
content/events/src/nsDOMDataTransfer.h
content/events/src/nsDOMEventTargetHelper.cpp
content/events/src/nsDOMEventTargetHelper.h
content/events/src/nsDOMNotifyPaintEvent.cpp
content/events/src/nsDOMTouchEvent.cpp
content/events/src/nsDOMTouchEvent.h
content/html/content/src/nsHTMLFormElement.h
content/html/content/src/nsHTMLInputElement.cpp
content/media/nsBuiltinDecoderReader.cpp
content/media/nsBuiltinDecoderReader.h
content/media/nsMediaDecoder.cpp
content/media/nsMediaDecoder.h
content/media/wave/nsWaveReader.cpp
content/media/wave/nsWaveReader.h
content/media/webm/nsWebMReader.cpp
content/media/webm/nsWebMReader.h
content/svg/content/src/DOMSVGLengthList.h
content/svg/content/src/DOMSVGNumberList.h
content/svg/content/src/DOMSVGPathSegList.cpp
content/svg/content/src/DOMSVGPathSegList.h
content/svg/content/src/DOMSVGPointList.cpp
content/svg/content/src/DOMSVGPointList.h
content/svg/content/src/DOMSVGTransformList.h
content/xslt/src/xslt/txKeyFunctionCall.cpp
content/xul/templates/src/nsRDFConInstanceTestNode.h
content/xul/templates/src/nsRDFPropertyTestNode.h
dom/Makefile.in
dom/base/nsDOMClassInfo.cpp
dom/base/nsGlobalWindow.cpp
dom/base/nsGlobalWindow.h
dom/base/nsJSEnvironment.cpp
dom/base/nsJSEnvironment.h
dom/base/nsScriptNameSpaceManager.cpp
dom/indexedDB/IDBCursor.cpp
dom/indexedDB/IDBDatabase.cpp
dom/indexedDB/IDBFactory.cpp
dom/indexedDB/IDBIndex.cpp
dom/indexedDB/IDBObjectStore.cpp
dom/indexedDB/IDBTransaction.cpp
dom/plugins/base/nsJSNPRuntime.cpp
dom/plugins/base/nsNPAPIPluginInstance.cpp
dom/plugins/base/nsNPAPIPluginInstance.h
dom/plugins/base/nsPluginHost.cpp
dom/plugins/base/nsPluginInstanceOwner.cpp
dom/src/notification/nsDesktopNotification.cpp
dom/src/notification/nsDesktopNotification.h
dom/src/offline/nsDOMOfflineResourceList.cpp
dom/src/offline/nsDOMOfflineResourceList.h
dom/src/storage/nsDOMStorage.cpp
dom/workers/WorkerPrivate.cpp
dom/workers/XMLHttpRequest.cpp
dom/workers/XMLHttpRequestPrivate.cpp
embedding/android/GeckoAppShell.java
embedding/components/commandhandler/src/nsCommandParams.cpp
gfx/gl/GLContext.cpp
gfx/gl/GLContext.h
gfx/gl/GLContextProviderCGL.mm
gfx/gl/GLContextProviderEGL.cpp
gfx/gl/GLContextProviderGLX.cpp
gfx/gl/GLContextProviderWGL.cpp
gfx/layers/ImageLayers.h
gfx/layers/basic/BasicLayers.cpp
gfx/layers/d3d10/ImageLayerD3D10.cpp
gfx/layers/d3d9/ImageLayerD3D9.cpp
gfx/layers/opengl/ImageLayerOGL.cpp
gfx/layers/opengl/LayerManagerOGL.cpp
gfx/thebes/gfxFont.cpp
gfx/thebes/gfxFont.h
gfx/thebes/gfxQtPlatform.cpp
gfx/thebes/gfxWindowsPlatform.cpp
intl/unicharutil/util/nsUnicharUtils.cpp
intl/unicharutil/util/nsUnicharUtils.h
js/src/config/milestone.txt
js/src/configure.in
js/src/jsapi-tests/testDebugger.cpp
js/src/jsapi.cpp
js/src/jsapi.h
js/src/jscntxt.cpp
js/src/jscntxt.h
js/src/jsdbgapi.cpp
js/src/jsdbgapi.h
js/src/jsfun.cpp
js/src/jsgc.cpp
js/src/jsinterp.cpp
js/src/jsopcode.cpp
js/src/shell/js.cpp
js/src/shell/jsworkers.cpp
js/src/vm/Stack.cpp
js/src/vm/Stack.h
js/xpconnect/shell/xpcshell.cpp
js/xpconnect/src/Makefile.in
js/xpconnect/src/XPCJSRuntime.cpp
layout/base/nsCSSFrameConstructor.cpp
layout/base/nsDisplayList.cpp
layout/base/nsDocumentViewer.cpp
layout/base/nsFrameManager.cpp
layout/base/nsPresShell.cpp
layout/base/tests/Makefile.in
layout/forms/nsFileControlFrame.cpp
layout/forms/nsFileControlFrame.h
layout/generic/nsFrame.cpp
layout/generic/nsGfxScrollFrame.cpp
layout/generic/nsGfxScrollFrame.h
layout/generic/nsIFrame.h
layout/generic/nsObjectFrame.cpp
layout/style/nsCSSRuleProcessor.cpp
layout/style/nsRuleProcessorData.h
layout/style/nsStyleStruct.h
mobile/android/base/AndroidManifest.xml.in
mobile/android/base/GeckoApp.java
mobile/android/base/GeckoAppShell.java
mobile/android/base/Makefile.in
mobile/android/base/resources/layout-v11/awesomebar.xml
mobile/xul/confvars.sh
modules/libpref/src/Preferences.cpp
modules/libpref/src/init/all.js
modules/libpref/src/nsPrefBranch.h
mozglue/android/APKOpen.cpp
mozglue/android/APKOpen.h
mozglue/android/Makefile.in
netwerk/base/public/nsNetUtil.h
netwerk/base/public/nsURIHashKey.h
netwerk/cache/nsCacheEntry.cpp
netwerk/cookie/nsCookieService.h
netwerk/dns/nsHostResolver.cpp
netwerk/protocol/http/nsHttp.cpp
rdf/base/src/nsRDFService.cpp
security/manager/ssl/src/nsNSSCallbacks.cpp
toolkit/components/autocomplete/nsAutoCompleteController.cpp
toolkit/components/autocomplete/nsAutoCompleteController.h
toolkit/components/startup/nsAppStartup.cpp
toolkit/components/telemetry/Telemetry.cpp
toolkit/components/telemetry/TelemetryHistograms.h
toolkit/components/telemetry/TelemetryPing.js
toolkit/content/widgets/videocontrols.xml
toolkit/mozapps/installer/packager.mk
toolkit/system/gnome/nsAlertsIconListener.cpp
toolkit/xre/nsXREDirProvider.cpp
widget/android/AndroidBridge.cpp
widget/android/AndroidBridge.h
widget/android/nsAppShell.cpp
xpcom/components/Module.h
xpcom/ds/nsAtomTable.cpp
xpcom/ds/nsCRT.cpp
xpcom/ds/nsCRT.h
xpcom/ds/nsHashtable.cpp
xpcom/ds/nsStaticNameTable.cpp
xpcom/glue/nsHashKeys.h
xpcom/glue/nsStringAPI.h
xpcom/glue/pldhash.cpp
xpcom/idl-parser/xpidl.py
xpcom/io/nsLocalFileOS2.cpp
xpcom/io/nsLocalFileUnix.cpp
xpcom/tests/TestHashtables.cpp
xpcom/tests/TestUTF.cpp
--- a/.hgtags
+++ b/.hgtags
@@ -73,8 +73,9 @@ 462c726144bc1fb45b61e774f64ac5d61b4e047c
 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
+b6627f28b7ec17e1b46a594df0f780d3a40847e4 FIREFOX_AURORA_13_BASE
--- a/accessible/public/nsIAccessible.idl
+++ b/accessible/public/nsIAccessible.idl
@@ -54,17 +54,17 @@ interface nsIAccessibleRelation;
  * accessibility APIs like MSAA and ATK. Contains the sum of what's needed
  * to support IAccessible as well as ATK's generic accessibility objects.
  * Can also be used by in-process accessibility clients to get information
  * about objects in the accessible tree. The accessible tree is a subset of 
  * nodes in the DOM tree -- such as documents, focusable elements and text.
  * Mozilla creates the implementations of nsIAccessible on demand.
  * See http://www.mozilla.org/projects/ui/accessibility for more information.
  */
-[scriptable, uuid(3126544c-826c-4694-a2ed-67bfe56a1f37)]
+[scriptable, uuid(e7c44e0d-736e-4ead-afee-b51f4b574020)]
 interface nsIAccessible : nsISupports
 {
   /**
    * Parent node in accessible tree.
    */
   readonly attribute nsIAccessible parent;
 
   /**
@@ -106,36 +106,16 @@ interface nsIAccessible : nsISupports
   /**
    * The innerHTML for the HTML element associated with this accessible if applicable.
    * This is a text string of all the markup inside the DOM
    * node, not including the start and end tag for the node.
    */
   readonly attribute DOMString innerHTML;
 
   /**
-   * Retrieve the computed style value for this DOM node, if it is a DOM element.
-   * Note: the meanings of width, height and other size measurements depend
-   * on the version of CSS being used. Therefore, for bounds information, 
-   * it is better to use nsIAccessible::accGetBounds.
-   *
-   * @param pseudoElt [in] The pseudo element to retrieve style for, or NULL
-   *                  for general computed style information for this node.
-   * @param propertyName [in] Retrieve the computed style value for this property name,
-   *                     for example "border-bottom".
-   */
-  DOMString getComputedStyleValue(in DOMString pseudoElt, in DOMString propertyName);
-
-  /**
-   * The method is similar to getComputedStyleValue() excepting that this one
-   * returns nsIDOMCSSPrimitiveValue.
-   */
-  nsIDOMCSSPrimitiveValue getComputedStyleCSSValue(in DOMString pseudoElt,
-                                                   in DOMString propertyName);
-
-  /**
    * The DOM node this nsIAccessible is associated with.
    */
   readonly attribute nsIDOMNode DOMNode;
 
   /**
    * The document accessible that this access node resides in.
    */
   readonly attribute nsIAccessibleDocument document;
--- a/accessible/src/base/AccGroupInfo.h
+++ b/accessible/src/base/AccGroupInfo.h
@@ -61,16 +61,18 @@ public:
   {
     mozilla::a11y::role role = aAccessible->Role();
     if (role != mozilla::a11y::roles::ROW &&
         role != mozilla::a11y::roles::GRID_CELL &&
         role != mozilla::a11y::roles::OUTLINEITEM &&
         role != mozilla::a11y::roles::OPTION &&
         role != mozilla::a11y::roles::LISTITEM &&
         role != mozilla::a11y::roles::MENUITEM &&
+        role != mozilla::a11y::roles::COMBOBOX_OPTION &&
+        role != mozilla::a11y::roles::PARENT_MENUITEM &&
         role != mozilla::a11y::roles::CHECK_MENU_ITEM &&
         role != mozilla::a11y::roles::RADIO_MENU_ITEM &&
         role != mozilla::a11y::roles::RADIOBUTTON &&
         role != mozilla::a11y::roles::PAGETAB)
       return nsnull;
 
     AccGroupInfo* info = new AccGroupInfo(aAccessible, BaseRole(role));
     return info;
@@ -78,16 +80,17 @@ public:
 
 private:
   AccGroupInfo(const AccGroupInfo&);
   AccGroupInfo& operator =(const AccGroupInfo&);
 
   static mozilla::a11y::role BaseRole(mozilla::a11y::role aRole)
   {
     if (aRole == mozilla::a11y::roles::CHECK_MENU_ITEM ||
+        aRole == mozilla::a11y::roles::PARENT_MENUITEM ||
         aRole == mozilla::a11y::roles::RADIO_MENU_ITEM)
       return mozilla::a11y::roles::MENUITEM;
     return aRole;
   }
 
   /**
    * Return true if the given parent role is conceptual parent of the given
    * role.
--- a/accessible/src/base/TextAttrs.cpp
+++ b/accessible/src/base/TextAttrs.cpp
@@ -47,42 +47,16 @@
 #include "gfxUserFontSet.h"
 #include "nsFontMetrics.h"
 #include "nsLayoutUtils.h"
 
 using namespace mozilla;
 using namespace mozilla::a11y;
 
 ////////////////////////////////////////////////////////////////////////////////
-// Constants and structures
-
-/**
- * Item of the gCSSTextAttrsMap map.
- */
-struct nsCSSTextAttrMapItem
-{
-  const char* mCSSName;
-  const char* mCSSValue;
-  nsIAtom** mAttrName;
-  const char* mAttrValue;
-};
-
-/**
- * The map of CSS properties to text attributes.
- */
-const char* const kAnyValue = nsnull;
-const char* const kCopyValue = nsnull;
-
-static nsCSSTextAttrMapItem gCSSTextAttrsMap[] =
-{
-  // CSS name            CSS value        Attribute name                     Attribute value
-  { "vertical-align",    kAnyValue,       &nsGkAtoms::textPosition,          kCopyValue }
-};
-
-////////////////////////////////////////////////////////////////////////////////
 // TextAttrsMgr
 ////////////////////////////////////////////////////////////////////////////////
 
 void
 TextAttrsMgr::GetAttributes(nsIPersistentProperties* aAttributes,
                             PRInt32* aStartHTOffset,
                             PRInt32* aEndHTOffset)
 {
@@ -134,88 +108,87 @@ TextAttrsMgr::GetAttributes(nsIPersisten
   nsIContent *offsetNode = nsnull, *offsetElm = nsnull;
   nsIFrame *frame = nsnull;
   if (mOffsetAcc) {
     offsetNode = mOffsetAcc->GetContent();
     offsetElm = nsCoreUtils::GetDOMElementFor(offsetNode);
     frame = offsetElm->GetPrimaryFrame();
   }
 
-  nsTArray<TextAttr*> textAttrArray(9);
-
   // "language" text attribute
   LangTextAttr langTextAttr(mHyperTextAcc, hyperTextElm, offsetNode);
-  textAttrArray.AppendElement(&langTextAttr);
-
-  // "text-position" text attribute
-  CSSTextAttr posTextAttr(0, hyperTextElm, offsetElm);
-  textAttrArray.AppendElement(&posTextAttr);
 
   // "background-color" text attribute
   BGColorTextAttr bgColorTextAttr(rootFrame, frame);
-  textAttrArray.AppendElement(&bgColorTextAttr);
 
   // "color" text attribute
   ColorTextAttr colorTextAttr(rootFrame, frame);
-  textAttrArray.AppendElement(&colorTextAttr);
 
   // "font-family" text attribute
   FontFamilyTextAttr fontFamilyTextAttr(rootFrame, frame);
-  textAttrArray.AppendElement(&fontFamilyTextAttr);
 
   // "font-size" text attribute
   FontSizeTextAttr fontSizeTextAttr(rootFrame, frame);
-  textAttrArray.AppendElement(&fontSizeTextAttr);
 
   // "font-style" text attribute
   FontStyleTextAttr fontStyleTextAttr(rootFrame, frame);
-  textAttrArray.AppendElement(&fontStyleTextAttr);
 
   // "font-weight" text attribute
   FontWeightTextAttr fontWeightTextAttr(rootFrame, frame);
-  textAttrArray.AppendElement(&fontWeightTextAttr);
 
   // "text-underline(line-through)-style(color)" text attributes
   TextDecorTextAttr textDecorTextAttr(rootFrame, frame);
-  textAttrArray.AppendElement(&textDecorTextAttr);
+
+  // "text-position" text attribute
+  TextPosTextAttr textPosTextAttr(rootFrame, frame);
+
+  TextAttr* attrArray[] =
+  {
+    &langTextAttr,
+    &bgColorTextAttr,
+    &colorTextAttr,
+    &fontFamilyTextAttr,
+    &fontSizeTextAttr,
+    &fontStyleTextAttr,
+    &fontWeightTextAttr,
+    &textDecorTextAttr,
+    &textPosTextAttr
+  };
 
   // Expose text attributes if applicable.
   if (aAttributes) {
-    PRUint32 len = textAttrArray.Length();
-    for (PRUint32 idx = 0; idx < len; idx++)
-      textAttrArray[idx]->Expose(aAttributes, mIncludeDefAttrs);
+    for (PRUint32 idx = 0; idx < ArrayLength(attrArray); idx++)
+      attrArray[idx]->Expose(aAttributes, mIncludeDefAttrs);
   }
 
   // Expose text attributes range where they are applied if applicable.
   if (mOffsetAcc)
-    GetRange(textAttrArray, aStartHTOffset, aEndHTOffset);
+    GetRange(attrArray, ArrayLength(attrArray), aStartHTOffset, aEndHTOffset);
 }
 
 void
-TextAttrsMgr::GetRange(const nsTArray<TextAttr*>& aTextAttrArray,
+TextAttrsMgr::GetRange(TextAttr* aAttrArray[], PRUint32 aAttrArrayLen,
                        PRInt32* aStartHTOffset, PRInt32* aEndHTOffset)
 {
-  PRUint32 attrLen = aTextAttrArray.Length();
-
   // Navigate backward from anchor accessible to find start offset.
   for (PRInt32 childIdx = mOffsetAccIdx - 1; childIdx >= 0; childIdx--) {
     nsAccessible *currAcc = mHyperTextAcc->GetChildAt(childIdx);
 
     // Stop on embedded accessible since embedded accessibles are combined into
     // own range.
     if (nsAccUtils::IsEmbeddedObject(currAcc))
       break;
 
     nsIContent* currElm = nsCoreUtils::GetDOMElementFor(currAcc->GetContent());
     if (!currElm)
       return;
 
     bool offsetFound = false;
-    for (PRUint32 attrIdx = 0; attrIdx < attrLen; attrIdx++) {
-      TextAttr* textAttr = aTextAttrArray[attrIdx];
+    for (PRUint32 attrIdx = 0; attrIdx < aAttrArrayLen; attrIdx++) {
+      TextAttr* textAttr = aAttrArray[attrIdx];
       if (!textAttr->Equal(currElm)) {
         offsetFound = true;
         break;
       }
     }
 
     if (offsetFound)
       break;
@@ -230,18 +203,18 @@ TextAttrsMgr::GetRange(const nsTArray<Te
     if (nsAccUtils::IsEmbeddedObject(currAcc))
       break;
 
     nsIContent* currElm = nsCoreUtils::GetDOMElementFor(currAcc->GetContent());
     if (!currElm)
       return;
 
     bool offsetFound = false;
-    for (PRUint32 attrIdx = 0; attrIdx < attrLen; attrIdx++) {
-      TextAttr* textAttr = aTextAttrArray[attrIdx];
+    for (PRUint32 attrIdx = 0; attrIdx < aAttrArrayLen; attrIdx++) {
+      TextAttr* textAttr = aAttrArray[attrIdx];
 
       // Alter the end offset when text attribute changes its value and stop
       // the search.
       if (!textAttr->Equal(currElm)) {
         offsetFound = true;
         break;
       }
     }
@@ -289,70 +262,16 @@ TextAttrsMgr::LangTextAttr::
   GetLang(nsIContent* aElm, nsAString& aLang)
 {
   nsCoreUtils::GetLanguageFor(aElm, mRootContent, aLang);
   return !aLang.IsEmpty();
 }
 
 
 ////////////////////////////////////////////////////////////////////////////////
-// CSSTextAttr
-////////////////////////////////////////////////////////////////////////////////
-
-TextAttrsMgr::CSSTextAttr::
-  CSSTextAttr(PRUint32 aIndex, nsIContent* aRootElm, nsIContent* aElm) :
-  TTextAttr<nsString>(!aElm), mIndex(aIndex)
-{
-  mIsRootDefined = GetValueFor(aRootElm, &mRootNativeValue);
-
-  if (aElm)
-    mIsDefined = GetValueFor(aElm, &mNativeValue);
-}
-
-bool
-TextAttrsMgr::CSSTextAttr::
-  GetValueFor(nsIContent* aElm, nsString* aValue)
-{
-  nsCOMPtr<nsIDOMCSSStyleDeclaration> currStyleDecl =
-    nsCoreUtils::GetComputedStyleDeclaration(EmptyString(), aElm);
-  if (!currStyleDecl)
-    return false;
-
-  NS_ConvertASCIItoUTF16 cssName(gCSSTextAttrsMap[mIndex].mCSSName);
-
-  nsresult rv = currStyleDecl->GetPropertyValue(cssName, *aValue);
-  if (NS_FAILED(rv))
-    return true;
-
-  const char *cssValue = gCSSTextAttrsMap[mIndex].mCSSValue;
-  if (cssValue != kAnyValue && !aValue->EqualsASCII(cssValue))
-    return false;
-
-  return true;
-}
-
-void
-TextAttrsMgr::CSSTextAttr::
-  ExposeValue(nsIPersistentProperties* aAttributes, const nsString& aValue)
-{
-  const char* attrValue = gCSSTextAttrsMap[mIndex].mAttrValue;
-  if (attrValue != kCopyValue) {
-    nsAutoString formattedValue;
-    AppendASCIItoUTF16(attrValue, formattedValue);
-    nsAccUtils::SetAccAttr(aAttributes, *gCSSTextAttrsMap[mIndex].mAttrName,
-                           formattedValue);
-    return;
-  }
-
-  nsAccUtils::SetAccAttr(aAttributes, *gCSSTextAttrsMap[mIndex].mAttrName,
-                         aValue);
-}
-
-
-////////////////////////////////////////////////////////////////////////////////
 // BGColorTextAttr
 ////////////////////////////////////////////////////////////////////////////////
 
 TextAttrsMgr::BGColorTextAttr::
   BGColorTextAttr(nsIFrame* aRootFrame, nsIFrame* aFrame) :
   TTextAttr<nscolor>(!aFrame), mRootFrame(aRootFrame)
 {
   mIsRootDefined = GetColor(mRootFrame, &mRootNativeValue);
@@ -737,8 +656,109 @@ TextAttrsMgr::TextDecorTextAttr::
                            formattedStyle);
 
     nsAutoString formattedColor;
     StyleInfo::FormatColor(aValue.Color(), formattedColor);
     nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::textLineThroughColor,
                            formattedColor);
   }
 }
+
+////////////////////////////////////////////////////////////////////////////////
+// TextPosTextAttr
+////////////////////////////////////////////////////////////////////////////////
+
+TextAttrsMgr::TextPosTextAttr::
+  TextPosTextAttr(nsIFrame* aRootFrame, nsIFrame* aFrame) :
+  TTextAttr<TextPosValue>(!aFrame)
+{
+  mRootNativeValue = GetTextPosValue(aRootFrame);
+  mIsRootDefined = mRootNativeValue != eTextPosNone;
+
+  if (aFrame) {
+    mNativeValue = GetTextPosValue(aFrame);
+    mIsDefined = mNativeValue != eTextPosNone;
+  }
+}
+
+bool
+TextAttrsMgr::TextPosTextAttr::
+  GetValueFor(nsIContent* aContent, TextPosValue* aValue)
+{
+  nsIFrame* frame = aContent->GetPrimaryFrame();
+  if (frame) {
+    *aValue = GetTextPosValue(frame);
+    return *aValue != eTextPosNone;
+  }
+
+  return false;
+}
+
+void
+TextAttrsMgr::TextPosTextAttr::
+  ExposeValue(nsIPersistentProperties* aAttributes, const TextPosValue& aValue)
+{
+  switch (aValue) {
+    case eTextPosBaseline:
+      nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::textPosition,
+                             NS_LITERAL_STRING("baseline"));
+      break;
+
+    case eTextPosSub:
+      nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::textPosition,
+                             NS_LITERAL_STRING("sub"));
+      break;
+
+    case eTextPosSuper:
+      nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::textPosition,
+                             NS_LITERAL_STRING("super"));
+      break;
+  }
+}
+
+TextAttrsMgr::TextPosValue
+TextAttrsMgr::TextPosTextAttr::
+  GetTextPosValue(nsIFrame* aFrame) const
+{
+  const nsStyleCoord& styleCoord = aFrame->GetStyleTextReset()->mVerticalAlign;
+  switch (styleCoord.GetUnit()) {
+    case eStyleUnit_Enumerated:
+      switch (styleCoord.GetIntValue()) {
+        case NS_STYLE_VERTICAL_ALIGN_BASELINE:
+          return eTextPosBaseline;
+        case NS_STYLE_VERTICAL_ALIGN_SUB:
+          return eTextPosSub;
+        case NS_STYLE_VERTICAL_ALIGN_SUPER:
+          return eTextPosSuper;
+
+        // No good guess for these:
+        //   NS_STYLE_VERTICAL_ALIGN_TOP
+        //   NS_STYLE_VERTICAL_ALIGN_TEXT_TOP
+        //   NS_STYLE_VERTICAL_ALIGN_MIDDLE
+        //   NS_STYLE_VERTICAL_ALIGN_TEXT_BOTTOM
+        //   NS_STYLE_VERTICAL_ALIGN_BOTTOM
+        //   NS_STYLE_VERTICAL_ALIGN_MIDDLE_WITH_BASELINE
+        // Do not expose value of text-position attribute.
+
+        default:
+          break;
+      }
+      return eTextPosNone;
+
+    case eStyleUnit_Percent:
+    {
+      float percentValue = styleCoord.GetPercentValue();
+      return percentValue > 0 ?
+        eTextPosSuper :
+        (percentValue < 0 ? eTextPosSub : eTextPosBaseline);
+    }
+
+    case eStyleUnit_Coord:
+    {
+       nscoord coordValue = styleCoord.GetCoordValue();
+       return coordValue > 0 ?
+         eTextPosSuper :
+         (coordValue < 0 ? eTextPosSub : eTextPosBaseline);
+    }
+  }
+
+  return eTextPosNone;
+}
--- a/accessible/src/base/TextAttrs.h
+++ b/accessible/src/base/TextAttrs.h
@@ -102,21 +102,22 @@ public:
 
 protected:
   /**
    * Calculates range (start and end offsets) of text where the text attributes
    * are stretched. New offsets may be smaller if one of text attributes changes
    * its value before or after the given offsets.
    *
    * @param aTextAttrArray  [in] text attributes array
+   * @param aAttrArrayLen   [in] text attributes array length
    * @param aStartHTOffset  [in, out] the start offset
    * @param aEndHTOffset    [in, out] the end offset
    */
   class TextAttr;
-  void GetRange(const nsTArray<TextAttr*>& aTextAttrArray,
+  void GetRange(TextAttr* aAttrArray[], PRUint32 aAttrArrayLen,
                 PRInt32* aStartHTOffset, PRInt32* aEndHTOffset);
 
 private:
   nsHyperTextAccessible* mHyperTextAcc;
 
   bool mIncludeDefAttrs;
 
   nsAccessible* mOffsetAcc;
@@ -152,17 +153,17 @@ protected:
    * Base class to work with text attributes. See derived classes below.
    */
   template<class T>
   class TTextAttr : public TextAttr
   {
   public:
     TTextAttr(bool aGetRootValue) : mGetRootValue(aGetRootValue) {}
 
-    // ITextAttr
+    // TextAttr
     virtual void Expose(nsIPersistentProperties* aAttributes,
                         bool aIncludeDefAttrValue)
     {
       if (mGetRootValue) {
         if (mIsRootDefined)
           ExposeValue(aAttributes, mRootNativeValue);
         return;
       }
@@ -238,37 +239,16 @@ protected:
 
   private:
     bool GetLang(nsIContent* aElm, nsAString& aLang);
     nsCOMPtr<nsIContent> mRootContent;
   };
 
 
   /**
-   * Class is used for the work with CSS based text attributes.
-   */
-  class CSSTextAttr : public TTextAttr<nsString>
-  {
-  public:
-    CSSTextAttr(PRUint32 aIndex, nsIContent* aRootElm, nsIContent* aElm);
-    virtual ~CSSTextAttr() { }
-
-  protected:
-
-    // TextAttr
-    virtual bool GetValueFor(nsIContent* aElm, nsString* aValue);
-    virtual void ExposeValue(nsIPersistentProperties* aAttributes,
-                             const nsString& aValue);
-
-  private:
-    PRInt32 mIndex;
-  };
-
-
-  /**
    * Class is used for the work with 'background-color' text attribute.
    */
   class BGColorTextAttr : public TTextAttr<nscolor>
   {
   public:
     BGColorTextAttr(nsIFrame* aRootFrame, nsIFrame* aFrame);
     virtual ~BGColorTextAttr() { }
 
@@ -430,14 +410,42 @@ protected:
   protected:
 
     // TextAttr
     virtual bool GetValueFor(nsIContent* aElm, TextDecorValue* aValue);
     virtual void ExposeValue(nsIPersistentProperties* aAttributes,
                              const TextDecorValue& aValue);
   };
 
+  /**
+   * Class is used for the work with "text-position" text attribute.
+   */
+
+  enum TextPosValue {
+    eTextPosNone = 0,
+    eTextPosBaseline,
+    eTextPosSub,
+    eTextPosSuper
+  };
+
+  class TextPosTextAttr : public TTextAttr<TextPosValue>
+  {
+  public:
+    TextPosTextAttr(nsIFrame* aRootFrame, nsIFrame* aFrame);
+    virtual ~TextPosTextAttr() { }
+
+  protected:
+
+    // TextAttr
+    virtual bool GetValueFor(nsIContent* aElm, TextPosValue* aValue);
+    virtual void ExposeValue(nsIPersistentProperties* aAttributes,
+                             const TextPosValue& aValue);
+
+  private:
+    TextPosValue GetTextPosValue(nsIFrame* aFrame) const;
+  };
+
 }; // TextAttrMgr
 
 } // namespace a11y
 } // namespace mozilla
 
 #endif
--- a/accessible/src/base/nsAccUtils.cpp
+++ b/accessible/src/base/nsAccUtils.cpp
@@ -170,80 +170,16 @@ nsAccUtils::GetPositionAndSizeForXULSele
       if (index < static_cast<PRUint32>(indexOf))
         (*aPosInSet)--;
     }
   }
 
   (*aPosInSet)++; // group position is 1-index based.
 }
 
-void
-nsAccUtils::GetPositionAndSizeForXULContainerItem(nsIContent *aContent,
-                                                  PRInt32 *aPosInSet,
-                                                  PRInt32 *aSetSize)
-{
-  nsCOMPtr<nsIDOMXULContainerItemElement> item(do_QueryInterface(aContent));
-  if (!item)
-    return;
-
-  nsCOMPtr<nsIDOMXULContainerElement> container;
-  item->GetParentContainer(getter_AddRefs(container));
-  if (!container)
-    return;
-
-  // Get item count.
-  PRUint32 itemsCount = 0;
-  container->GetItemCount(&itemsCount);
-
-  // Get item index.
-  PRInt32 indexOf = 0;
-  container->GetIndexOfItem(item, &indexOf);
-
-  // Calculate set size and position in the set.
-  *aSetSize = 0, *aPosInSet = 0;
-  for (PRInt32 index = indexOf; index >= 0; index--) {
-    nsCOMPtr<nsIDOMXULElement> item;
-    container->GetItemAtIndex(index, getter_AddRefs(item));
-    nsCOMPtr<nsINode> itemNode(do_QueryInterface(item));
-
-    nsAccessible* itemAcc = itemNode ?
-      GetAccService()->GetAccessible(itemNode, nsnull) : nsnull;
-
-    if (itemAcc) {
-      PRUint32 itemRole = Role(itemAcc);
-      if (itemRole == nsIAccessibleRole::ROLE_SEPARATOR)
-        break; // We reached the beginning of our group.
-
-      if (!(itemAcc->State() & states::INVISIBLE)) {
-        (*aSetSize)++;
-        (*aPosInSet)++;
-      }
-    }
-  }
-
-  for (PRInt32 index = indexOf + 1; index < static_cast<PRInt32>(itemsCount);
-       index++) {
-    nsCOMPtr<nsIDOMXULElement> item;
-    container->GetItemAtIndex(index, getter_AddRefs(item));
-    nsCOMPtr<nsINode> itemNode(do_QueryInterface(item));
-
-    nsAccessible* itemAcc =
-      itemNode ? GetAccService()->GetAccessible(itemNode, nsnull) : nsnull;
-
-    if (itemAcc) {
-      PRUint32 itemRole = Role(itemAcc);
-      if (itemRole == nsIAccessibleRole::ROLE_SEPARATOR)
-        break; // We reached the end of our group.
-
-      if (!(itemAcc->State() & states::INVISIBLE))
-        (*aSetSize)++;
-    }
-  }
-}
-
 PRInt32
 nsAccUtils::GetLevelForXULContainerItem(nsIContent *aContent)
 {
   nsCOMPtr<nsIDOMXULContainerItemElement> item(do_QueryInterface(aContent));
   if (!item)
     return 0;
 
   nsCOMPtr<nsIDOMXULContainerElement> container;
--- a/accessible/src/base/nsAccUtils.h
+++ b/accessible/src/base/nsAccUtils.h
@@ -111,24 +111,16 @@ public:
    * Compute position in group (posinset) and group size (setsize) for
    * nsIDOMXULSelectControlItemElement node.
    */
   static void GetPositionAndSizeForXULSelectControlItem(nsIContent *aContent,
                                                         PRInt32 *aPosInSet,
                                                         PRInt32 *aSetSize);
 
   /**
-   * Compute group position and group size (posinset and setsize) for
-   * nsIDOMXULContainerItemElement node.
-   */
-  static void GetPositionAndSizeForXULContainerItem(nsIContent *aContent,
-                                                    PRInt32 *aPosInSet,
-                                                    PRInt32 *aSetSize);
-
-  /**
    * Compute group level for nsIDOMXULContainerItemElement node.
    */
   static PRInt32 GetLevelForXULContainerItem(nsIContent *aContent);
 
   /**
    * Set container-foo live region attributes for the given node.
    *
    * @param aAttributes    where to store the attributes
--- a/accessible/src/base/nsAccessible.cpp
+++ b/accessible/src/base/nsAccessible.cpp
@@ -227,52 +227,16 @@ nsAccessible::~nsAccessible()
 
 void
 nsAccessible::SetRoleMapEntry(nsRoleMapEntry* aRoleMapEntry)
 {
   mRoleMapEntry = aRoleMapEntry;
 }
 
 NS_IMETHODIMP
-nsAccessible::GetComputedStyleValue(const nsAString& aPseudoElt,
-                                    const nsAString& aPropertyName,
-                                    nsAString& aValue)
-{
-  if (IsDefunct())
-    return NS_ERROR_FAILURE;
-
-  nsCOMPtr<nsIDOMCSSStyleDeclaration> styleDecl =
-    nsCoreUtils::GetComputedStyleDeclaration(aPseudoElt, mContent);
-  NS_ENSURE_TRUE(styleDecl, NS_ERROR_FAILURE);
-
-  return styleDecl->GetPropertyValue(aPropertyName, aValue);
-}
-
-NS_IMETHODIMP
-nsAccessible::GetComputedStyleCSSValue(const nsAString& aPseudoElt,
-                                       const nsAString& aPropertyName,
-                                       nsIDOMCSSPrimitiveValue **aCSSValue) {
-  NS_ENSURE_ARG_POINTER(aCSSValue);
-  *aCSSValue = nsnull;
-
-  if (IsDefunct())
-    return NS_ERROR_FAILURE;
-
-  nsCOMPtr<nsIDOMCSSStyleDeclaration> styleDecl =
-    nsCoreUtils::GetComputedStyleDeclaration(aPseudoElt, mContent);
-  NS_ENSURE_STATE(styleDecl);
-
-  nsCOMPtr<nsIDOMCSSValue> cssValue;
-  styleDecl->GetPropertyCSSValue(aPropertyName, getter_AddRefs(cssValue));
-  NS_ENSURE_TRUE(cssValue, NS_ERROR_FAILURE);
-
-  return CallQueryInterface(cssValue, aCSSValue);
-}
-
-NS_IMETHODIMP
 nsAccessible::GetDocument(nsIAccessibleDocument **aDocument)
 {
   NS_ENSURE_ARG_POINTER(aDocument);
 
   NS_IF_ADDREF(*aDocument = Document());
   return NS_OK;
 }
 
--- a/accessible/src/base/nsApplicationAccessible.cpp
+++ b/accessible/src/base/nsApplicationAccessible.cpp
@@ -463,32 +463,14 @@ nsApplicationAccessible::ScrollTo(PRUint
 NS_IMETHODIMP
 nsApplicationAccessible::ScrollToPoint(PRUint32 aCoordinateType,
                                        PRInt32 aX, PRInt32 aY)
 {
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsApplicationAccessible::GetComputedStyleValue(const nsAString &aPseudoElt,
-                                               const nsAString &aPropertyName,
-                                               nsAString &aValue)
-{
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsApplicationAccessible::GetComputedStyleCSSValue(const nsAString &aPseudoElt,
-                                                  const nsAString &aPropertyName,
-                                                  nsIDOMCSSPrimitiveValue **aCSSPrimitiveValue)
-{
-  NS_ENSURE_ARG_POINTER(aCSSPrimitiveValue);
-  *aCSSPrimitiveValue = nsnull;
-  return NS_OK;
-}
-
-NS_IMETHODIMP
 nsApplicationAccessible::GetLanguage(nsAString &aLanguage)
 {
   aLanguage.Truncate();
   return NS_OK;
 }
 
--- a/accessible/src/base/nsApplicationAccessible.h
+++ b/accessible/src/base/nsApplicationAccessible.h
@@ -71,22 +71,16 @@ public:
 
   // nsIAccessible
   NS_SCRIPTABLE NS_IMETHOD GetDOMNode(nsIDOMNode** aDOMNode);
   NS_SCRIPTABLE NS_IMETHOD GetDocument(nsIAccessibleDocument** aDocument);
   NS_SCRIPTABLE NS_IMETHOD GetRootDocument(nsIAccessibleDocument** aRootDocument);
   NS_SCRIPTABLE NS_IMETHOD GetInnerHTML(nsAString& aInnerHTML);
   NS_SCRIPTABLE NS_IMETHOD ScrollTo(PRUint32 aScrollType);
   NS_SCRIPTABLE NS_IMETHOD ScrollToPoint(PRUint32 aCoordinateType, PRInt32 aX, PRInt32 aY);
-  NS_SCRIPTABLE NS_IMETHOD GetComputedStyleValue(const nsAString& aPseudoElt,
-                                                 const nsAString& aPropertyName,
-                                                 nsAString& aValue NS_OUTPARAM);
-  NS_SCRIPTABLE NS_IMETHOD GetComputedStyleCSSValue(const nsAString& aPseudoElt,
-                                                    const nsAString& aPropertyName,
-                                                    nsIDOMCSSPrimitiveValue** aValue NS_OUTPARAM);
   NS_SCRIPTABLE NS_IMETHOD GetLanguage(nsAString& aLanguage);
   NS_IMETHOD GetParent(nsIAccessible **aParent);
   NS_IMETHOD GetNextSibling(nsIAccessible **aNextSibling);
   NS_IMETHOD GetPreviousSibling(nsIAccessible **aPreviousSibling);
   NS_IMETHOD GetName(nsAString &aName);
   NS_IMETHOD GetValue(nsAString &aValue);
   NS_IMETHOD GetAttributes(nsIPersistentProperties **aAttributes);
   NS_IMETHOD GroupPosition(PRInt32 *aGroupLevel, PRInt32 *aSimilarItemsInGroup,
--- a/accessible/src/base/nsCoreUtils.cpp
+++ b/accessible/src/base/nsCoreUtils.cpp
@@ -565,36 +565,16 @@ nsCoreUtils::GetLanguageFor(nsIContent *
   aLanguage.Truncate();
 
   nsIContent *walkUp = aContent;
   while (walkUp && walkUp != aRootContent &&
          !walkUp->GetAttr(kNameSpaceID_None, nsGkAtoms::lang, aLanguage))
     walkUp = walkUp->GetParent();
 }
 
-already_AddRefed<nsIDOMCSSStyleDeclaration>
-nsCoreUtils::GetComputedStyleDeclaration(const nsAString& aPseudoElt,
-                                         nsIContent *aContent)
-{
-  nsIContent* content = GetDOMElementFor(aContent);
-  if (!content)
-    return nsnull;
-
-  // Returns number of items in style declaration
-  nsCOMPtr<nsIDOMWindow> window =
-    do_QueryInterface(content->OwnerDoc()->GetWindow());
-  if (!window)
-    return nsnull;
-
-  nsCOMPtr<nsIDOMCSSStyleDeclaration> cssDecl;
-  nsCOMPtr<nsIDOMElement> domElement(do_QueryInterface(content));
-  window->GetComputedStyle(domElement, aPseudoElt, getter_AddRefs(cssDecl));
-  return cssDecl.forget();
-}
-
 already_AddRefed<nsIBoxObject>
 nsCoreUtils::GetTreeBodyBoxObject(nsITreeBoxObject *aTreeBoxObj)
 {
   nsCOMPtr<nsIDOMElement> tcElm;
   aTreeBoxObj->GetTreeBody(getter_AddRefs(tcElm));
   nsCOMPtr<nsIDOMXULElement> tcXULElm(do_QueryInterface(tcElm));
   if (!tcXULElm)
     return nsnull;
--- a/accessible/src/base/nsCoreUtils.h
+++ b/accessible/src/base/nsCoreUtils.h
@@ -284,23 +284,16 @@ public:
    * @param aContent     [in] the given node
    * @param aRootContent [in] container of the given node
    * @param aLanguage    [out] language
    */
   static void GetLanguageFor(nsIContent *aContent, nsIContent *aRootContent,
                              nsAString& aLanguage);
 
   /**
-   * Return computed styles declaration for the given node.
-   */
-  static already_AddRefed<nsIDOMCSSStyleDeclaration>
-    GetComputedStyleDeclaration(const nsAString& aPseudoElt,
-                                nsIContent *aContent);
-
-  /**
    * Return box object for XUL treechildren element by tree box object.
    */
   static already_AddRefed<nsIBoxObject>
     GetTreeBodyBoxObject(nsITreeBoxObject *aTreeBoxObj);
 
   /**
    * Return tree box object from any levels DOMNode under the XUL tree.
    */
--- a/accessible/src/html/nsHTMLTableAccessible.cpp
+++ b/accessible/src/html/nsHTMLTableAccessible.cpp
@@ -1436,19 +1436,36 @@ nsHTMLTableAccessible::IsProbablyForLayo
     }
 
     if (childElm->Tag() == nsGkAtoms::tbody) {
       for (nsIContent* rowElm = childElm->GetFirstChild(); rowElm;
            rowElm = rowElm->GetNextSibling()) {
         if (rowElm->IsHTML() && rowElm->Tag() == nsGkAtoms::tr) {
           for (nsIContent* cellElm = rowElm->GetFirstChild(); cellElm;
                cellElm = cellElm->GetNextSibling()) {
-            if (cellElm->IsHTML() && cellElm->Tag() == nsGkAtoms::th) {
-              RETURN_LAYOUT_ANSWER(false,
-                                   "Has th -- legitimate table structures");
+            if (cellElm->IsHTML()) {
+              
+              if (cellElm->NodeInfo()->Equals(nsGkAtoms::th)) {
+                RETURN_LAYOUT_ANSWER(false,
+                                     "Has th -- legitimate table structures");
+              }
+
+              if (cellElm->HasAttr(kNameSpaceID_None, nsGkAtoms::headers) ||
+                  cellElm->HasAttr(kNameSpaceID_None, nsGkAtoms::scope) ||
+                  cellElm->HasAttr(kNameSpaceID_None, nsGkAtoms::abbr)) {
+                RETURN_LAYOUT_ANSWER(false,
+                                     "Has headers, scope, or abbr attribute -- legitimate table structures");
+              }
+
+              nsAccessible* cell = mDoc->GetAccessible(cellElm);
+              if (cell && cell->GetChildCount() == 1 &&
+                  cell->FirstChild()->IsAbbreviation()) {
+                RETURN_LAYOUT_ANSWER(false,
+                                     "has abbr -- legitimate table structures");
+              }
             }
           }
         }
       }
     }
   }
 
   if (HasDescendant(NS_LITERAL_STRING("table"))) {
--- a/accessible/src/msaa/CAccessibleComponent.cpp
+++ b/accessible/src/msaa/CAccessibleComponent.cpp
@@ -38,16 +38,18 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "CAccessibleComponent.h"
 
 #include "AccessibleComponent_i.c"
 
 #include "nsAccessible.h"
+#include "nsCoreUtils.h"
+#include "nsWinUtils.h"
 #include "States.h"
 
 #include "nsString.h"
 
 #include "nsIDOMCSSPrimitiveValue.h"
 #include "nsIDOMNSRGBAColor.h"
 
 using namespace mozilla::a11y;
@@ -151,27 +153,33 @@ CAccessibleComponent::get_background(IA2
 HRESULT
 CAccessibleComponent::GetARGBValueFromCSSProperty(const nsAString& aPropName,
                                                   IA2Color *aColorValue)
 {
 __try {
   *aColorValue = 0;
 
   nsRefPtr<nsAccessible> acc(do_QueryObject(this));
-  if (!acc)
+  if (acc->IsDefunct())
     return E_FAIL;
 
-  nsCOMPtr<nsIDOMCSSPrimitiveValue> cssValue;
-  nsresult rv = acc->GetComputedStyleCSSValue(EmptyString(), aPropName,
-                                              getter_AddRefs(cssValue));
-  if (NS_FAILED(rv) || !cssValue)
-    return GetHRESULT(rv);
+  nsCOMPtr<nsIDOMCSSStyleDeclaration> styleDecl =
+    nsWinUtils::GetComputedStyleDeclaration(acc->GetContent());
+  NS_ENSURE_STATE(styleDecl);
+
+  nsCOMPtr<nsIDOMCSSValue> cssGenericValue;
+  styleDecl->GetPropertyCSSValue(aPropName, getter_AddRefs(cssGenericValue));
+
+  nsCOMPtr<nsIDOMCSSPrimitiveValue> cssValue =
+    do_QueryInterface(cssGenericValue);
+  if (!cssValue)
+    return E_FAIL;
 
   nsCOMPtr<nsIDOMRGBColor> rgbColor;
-  rv = cssValue->GetRGBColorValue(getter_AddRefs(rgbColor));
+  nsresult rv = cssValue->GetRGBColorValue(getter_AddRefs(rgbColor));
   if (NS_FAILED(rv) || !rgbColor)
     return GetHRESULT(rv);
 
   nsCOMPtr<nsIDOMNSRGBAColor> rgbaColor(do_QueryInterface(rgbColor));
   if (!rgbaColor)
     return GetHRESULT(rv);
 
   // get alpha
--- a/accessible/src/msaa/nsAccessNodeWrap.cpp
+++ b/accessible/src/msaa/nsAccessNodeWrap.cpp
@@ -334,17 +334,17 @@ STDMETHODIMP nsAccessNodeWrap::get_compu
 {
 __try{
   *aNumStyleProperties = 0;
 
   if (IsDefunct() || IsDocumentNode())
     return E_FAIL;
 
   nsCOMPtr<nsIDOMCSSStyleDeclaration> cssDecl =
-    nsCoreUtils::GetComputedStyleDeclaration(EmptyString(), mContent);
+    nsWinUtils::GetComputedStyleDeclaration(mContent);
   NS_ENSURE_TRUE(cssDecl, E_FAIL);
 
   PRUint32 length;
   cssDecl->GetLength(&length);
 
   PRUint32 index, realIndex;
   for (index = realIndex = 0; index < length && realIndex < aMaxStyleProperties; index ++) {
     nsAutoString property, value;
@@ -369,17 +369,17 @@ STDMETHODIMP nsAccessNodeWrap::get_compu
     /* [length_is][size_is][in] */ BSTR __RPC_FAR *aStyleProperties,
     /* [length_is][size_is][out] */ BSTR __RPC_FAR *aStyleValues)
 {
 __try {
   if (IsDefunct() || IsDocumentNode())
     return E_FAIL;
  
   nsCOMPtr<nsIDOMCSSStyleDeclaration> cssDecl =
-    nsCoreUtils::GetComputedStyleDeclaration(EmptyString(), mContent);
+    nsWinUtils::GetComputedStyleDeclaration(mContent);
   NS_ENSURE_TRUE(cssDecl, E_FAIL);
 
   PRUint32 index;
   for (index = 0; index < aNumStyleProperties; index ++) {
     nsAutoString value;
     if (aStyleProperties[index])
       cssDecl->GetPropertyValue(nsDependentString(static_cast<PRUnichar*>(aStyleProperties[index])), value);  // Get property value
     aStyleValues[index] = ::SysAllocString(value.get());
--- a/accessible/src/msaa/nsWinUtils.cpp
+++ b/accessible/src/msaa/nsWinUtils.cpp
@@ -50,16 +50,35 @@
 
 using namespace mozilla;
 using namespace mozilla::a11y;
 
 // Window property used by ipc related code in identifying accessible
 // tab windows.
 const PRUnichar* kPropNameTabContent = L"AccessibleTabWindow";
 
+already_AddRefed<nsIDOMCSSStyleDeclaration>
+nsWinUtils::GetComputedStyleDeclaration(nsIContent* aContent)
+{
+  nsIContent* elm = nsCoreUtils::GetDOMElementFor(aContent);
+  if (!elm)
+    return nsnull;
+
+  // Returns number of items in style declaration
+  nsCOMPtr<nsIDOMWindow> window =
+    do_QueryInterface(elm->OwnerDoc()->GetWindow());
+  if (!window)
+    return nsnull;
+
+  nsCOMPtr<nsIDOMCSSStyleDeclaration> cssDecl;
+  nsCOMPtr<nsIDOMElement> domElement(do_QueryInterface(elm));
+  window->GetComputedStyle(domElement, EmptyString(), getter_AddRefs(cssDecl));
+  return cssDecl.forget();
+}
+
 HRESULT
 nsWinUtils::ConvertToIA2Array(nsIArray *aGeckoArray, IUnknown ***aIA2Array,
                               long *aIA2ArrayLen)
 {
   *aIA2Array = NULL;
   *aIA2ArrayLen = 0;
 
   if (!aGeckoArray)
--- a/accessible/src/msaa/nsWinUtils.h
+++ b/accessible/src/msaa/nsWinUtils.h
@@ -48,16 +48,25 @@
 
 const LPCWSTR kClassNameRoot = L"MozillaUIWindowClass";
 const LPCWSTR kClassNameTabContent = L"MozillaContentWindowClass";
 
 class nsWinUtils
 {
 public:
   /**
+   * Return computed styles declaration for the given node.
+   *
+   * @note Please use it carefully since it can shutdown the accessible tree
+   *       you operate on.
+   */
+  static already_AddRefed<nsIDOMCSSStyleDeclaration>
+    GetComputedStyleDeclaration(nsIContent* aContent);
+
+  /**
    * Convert nsIArray array of accessible objects to an array of IUnknown*
    * objects used in IA2 methods.
    */
   static HRESULT ConvertToIA2Array(nsIArray *aCollection,
                                    IUnknown ***aAccessibles, long *aCount);
 
   /**
    * Start window emulation if presence of specific AT is detected.
--- a/accessible/src/xul/nsXULMenuAccessible.cpp
+++ b/accessible/src/xul/nsXULMenuAccessible.cpp
@@ -306,24 +306,16 @@ nsXULMenuitemAccessible::NativeRole()
 }
 
 PRInt32
 nsXULMenuitemAccessible::GetLevelInternal()
 {
   return nsAccUtils::GetLevelForXULContainerItem(mContent);
 }
 
-void
-nsXULMenuitemAccessible::GetPositionAndSizeInternal(PRInt32 *aPosInSet,
-                                                    PRInt32 *aSetSize)
-{
-  nsAccUtils::GetPositionAndSizeForXULContainerItem(mContent, aPosInSet,
-                                                    aSetSize);
-}
-
 bool
 nsXULMenuitemAccessible::CanHaveAnonChildren()
 {
   // That indicates we don't walk anonymous children for menuitems
   return false;
 }
 
 NS_IMETHODIMP nsXULMenuitemAccessible::DoAction(PRUint8 index)
--- a/accessible/src/xul/nsXULMenuAccessible.h
+++ b/accessible/src/xul/nsXULMenuAccessible.h
@@ -58,18 +58,16 @@ public:
   NS_IMETHOD GetActionName(PRUint8 aIndex, nsAString& aName);
 
   // nsAccessible
   virtual void Description(nsString& aDescription);
   virtual nsresult GetNameInternal(nsAString& aName);
   virtual mozilla::a11y::role NativeRole();
   virtual PRUint64 NativeState();
   virtual PRInt32 GetLevelInternal();
-  virtual void GetPositionAndSizeInternal(PRInt32 *aPosInSet,
-                                          PRInt32 *aSetSize);
 
   virtual bool CanHaveAnonChildren();
 
   // ActionAccessible
   virtual PRUint8 ActionCount();
   virtual KeyBinding AccessKey() const;
   virtual KeyBinding KeyboardShortcut() const;
 
--- a/accessible/tests/mochitest/Makefile.in
+++ b/accessible/tests/mochitest/Makefile.in
@@ -96,17 +96,16 @@ include $(topsrcdir)/config/rules.mk
 		test_aria_role_equation.html \
 		test_aria_roles.html \
 		test_aria_roles.xul \
 		test_aria_token_attrs.html \
 		test_bug420863.html \
 		test_descr.html \
 		test_nsIAccessibleDocument.html \
 		test_nsIAccessibleImage.html \
-		test_nsIAccessNode_utils.html \
 		test_nsOuterDocAccessible.html \
 		test_role_nsHyperTextAcc.html \
 		test_textboxes.html \
 		test_textboxes.xul \
 		testTextboxes.js \
 		text.js \
 		treeview.css \
 		treeview.js \
--- a/accessible/tests/mochitest/attributes/test_obj_group.html
+++ b/accessible/tests/mochitest/attributes/test_obj_group.html
@@ -134,16 +134,23 @@
       // HTML headings
       testGroupAttrs("h1", 0, 0, 1);
       testGroupAttrs("h2", 0, 0, 2);
       testGroupAttrs("h3", 0, 0, 3);
       testGroupAttrs("h4", 0, 0, 4);
       testGroupAttrs("h5", 0, 0, 5);
       testGroupAttrs("h6", 0, 0, 6);
 
+      //////////////////////////////////////////////////////////////////////////
+      // ARIA combobox
+      testGroupAttrs("combo1_opt1", 1, 4);
+      testGroupAttrs("combo1_opt2", 2, 4);
+      testGroupAttrs("combo1_opt3", 3, 4);
+      testGroupAttrs("combo1_opt4", 4, 4);
+
       SimpleTest.finish();
     }
 
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(doTest);
   </script>
 </head>
 <body>
@@ -300,10 +307,17 @@
 
   <h1 id="h1">heading1</h1>
   <h2 id="h2">heading2</h2>
   <h3 id="h3">heading3</h3>
   <h4 id="h4">heading4</h4>
   <h5 id="h5">heading5</h5>
   <h6 id="h6">heading6</h6>
 
+  <ul id="combo1" role="combobox">Password
+    <li id="combo1_opt1" role="option">Xyzzy</li>
+    <li id="combo1_opt2" role="option">Plughs</li>
+    <li id="combo1_opt3" role="option">Shazaam</li>
+    <li id="combo1_opt4" role="option">JoeSentMe</li>
+  </ul>
+
 </body>
 </html>
--- a/accessible/tests/mochitest/attributes/test_obj_group.xul
+++ b/accessible/tests/mochitest/attributes/test_obj_group.xul
@@ -86,16 +86,23 @@
       testGroupAttrs("tab2", 2, 2);
 
       //////////////////////////////////////////////////////////////////////////
       // xul:radio
       testGroupAttrs("radio1", 1, 2);
       testGroupAttrs("radio2", 2, 2);
 
       //////////////////////////////////////////////////////////////////////////
+      // xul:menulist
+      testGroupAttrs("menulist1.1", 1);
+      testGroupAttrs("menulist1.2", 2);
+      testGroupAttrs("menulist1.3", 3);
+      testGroupAttrs("menulist1.4", 4);
+
+      //////////////////////////////////////////////////////////////////////////
       // ARIA menu (bug 441888)
       testGroupAttrs("aria-menuitem", 1, 3);
       testGroupAttrs("aria-menuitemcheckbox", 2, 3);
       testGroupAttrs("aria-menuitemradio", 3, 3);
       testGroupAttrs("aria-menuitem2", 1, 1);
 
       //////////////////////////////////////////////////////////////////////////
       // xul:menu (bug 443881)
@@ -170,16 +177,25 @@
     </tabpanels>
   </tabbox>
 
   <radiogroup>
     <radio id="radio1" label="radio1"/>
     <radio id="radio2" label="radio2"/>
   </radiogroup>
 
+  <menulist id="menulist1" label="Vehicle">
+    <menupopup>
+      <menuitem id="menulist1.1" label="Car"/>
+      <menuitem id="menulist1.2" label="Taxi"/>
+      <menuitem id="menulist1.3" label="Bus" selected="true"/>
+      <menuitem id="menulist1.4" label="Train"/>
+    </menupopup>
+  </menulist>
+
   <vbox>
     <description role="menuitem" id="aria-menuitem"
                  value="conventional menuitem"/>
     <description role="menuitemcheckbox" id="aria-menuitemcheckbox"
                  value="conventional checkbox menuitem"/>
     <description role="menuitem" hidden="true"/>
     <description role="menuitemradio" id="aria-menuitemradio"
                  value="conventional radio menuitem"/>
--- a/accessible/tests/mochitest/attributes/test_text.html
+++ b/accessible/tests/mochitest/attributes/test_text.html
@@ -176,16 +176,40 @@
       attrs = {};
       testTextAttrs(ID, 50, attrs, defAttrs, 50, 55);
 
       tempElem = tempElem.nextSibling.nextSibling;
       gComputedStyle = document.defaultView.getComputedStyle(tempElem, "");
       attrs = {"text-position": gComputedStyle.verticalAlign};
       testTextAttrs(ID, 55, attrs, defAttrs, 55, 64);
 
+      attrs = {};
+      testTextAttrs(ID, 64, attrs, defAttrs, 64, 69);
+
+      attrs = { "text-position": "super" };
+      testTextAttrs(ID, 69, attrs, defAttrs, 69, 84);
+
+      attrs = {};
+      testTextAttrs(ID, 84, attrs, defAttrs, 84, 89);
+
+      attrs = { "text-position": "sub" };
+      testTextAttrs(ID, 89, attrs, defAttrs, 89, 102);
+
+      attrs = {};
+      testTextAttrs(ID, 102, attrs, defAttrs, 102, 107);
+
+      attrs = { "text-position": "super" };
+      testTextAttrs(ID, 107, attrs, defAttrs, 107, 123);
+
+      attrs = {};
+      testTextAttrs(ID, 123, attrs, defAttrs, 123, 128);
+
+      attrs = { "text-position": "sub" };
+      testTextAttrs(ID, 128, attrs, defAttrs, 128, 142);
+
       //////////////////////////////////////////////////////////////////////////
       // area7
       ID = "area7";
       defAttrs = buildDefaultTextAttrs(ID, "12pt");
       defAttrs["language"] = "en";
       testDefaultTextAttrs(ID, defAttrs);
 
       attrs = {"language": "ru"};
@@ -501,16 +525,21 @@
 <body style="font-size: 12pt">
 
   <a target="_blank"
      href="https://bugzilla.mozilla.org/show_bug.cgi?id=345759"
      title="Implement text attributes">
     Mozilla Bug 345759
   </a>
   <a target="_blank"
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=473569"
+     title="Restrict text-position to allowed values">
+    Mozilla Bug 473569
+  </a>
+  <a target="_blank"
      href="https://bugzilla.mozilla.org/show_bug.cgi?id=473576"
      title="font-family text attribute should expose actual font used">
     Mozilla Bug 473576
   </a>
   <a target="_blank"
      href="https://bugzilla.mozilla.org/show_bug.cgi?id=523304"
      title="expose text-underline-color and text-line-through-color text attributes">
     Mozilla Bug 523304
@@ -544,17 +573,21 @@
     <span style="color: green">Green</span>
     <img src="../moz.png" alt="image"/>
     <span style="color: red">Red</span>Normal
   </p>
   <p id="area6">
     This <sup>sentence</sup> has the word 
     <span style="vertical-align:super;">sentence</span> in 
     <sub>superscript</sub> and 
-    <span style="vertical-align:sub;">subscript</span>
+    <span style="vertical-align:sub;">subscript</span> and
+    <span style="vertical-align:20%;">superscript 20%</span> and
+    <span style="vertical-align:-20%;">subscript 20%</span> and
+    <span style="vertical-align:20px;">superscript 20px</span> and
+    <span style="vertical-align:-20px;">subscript 20px</span>
   </p>
 
   <p lang="en" id="area7">
     <span lang="ru">Привет</span>
     <span style="background-color: blue">Blue BG color</span>
     <span lang="de">Ich bin/Du bist</span>
     <span lang="en">
       Normal
deleted file mode 100644
--- a/accessible/tests/mochitest/test_nsIAccessNode_utils.html
+++ /dev/null
@@ -1,53 +0,0 @@
-<html>
-
-<head>
-  <title>nsIAccessNode util methods testing</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>
-  <script type="application/javascript"
-          src="common.js"></script>
-
-  <script type="application/javascript">
-    function doTest()
-    {
-      var elmObj = {};
-      var acc = getAccessible("span", null, elmObj);
-      computedStyle = document.defaultView.getComputedStyle(elmObj.value, "");
-
-      // html:span element
-      is(acc.getComputedStyleValue("", "color"), computedStyle.color,
-         "Wrong color for element with ID 'span'");
-
-      // text child of html:span element
-      acc = getAccessible(acc.firstChild);
-      is(acc.getComputedStyleValue("", "color"), computedStyle.color,
-         "Wrong color for text child of element with ID 'span'");
-
-      SimpleTest.finish();
-    }
-
-    SimpleTest.waitForExplicitFinish();
-    addA11yLoadEvent(doTest);
-  </script>
-</head>
-
-<body>
-
-  <a target="_blank"
-     href="https://bugzilla.mozilla.org/show_bug.cgi?id=454211"
-     title="nsIAccessNode util methods testing">
-    Mozilla Bug 454211
-  </a>
-  <p id="display"></p>
-  <div id="content" style="display: none"></div>
-  <pre id="test">
-  </pre>
-
-  <span role="note" style="color: red" id="span">text</span>
-
-</body>
-</html>
--- a/b2g/app/b2g.js
+++ b/b2g/app/b2g.js
@@ -41,17 +41,17 @@ pref("toolkit.defaultChromeURI", "chrome
 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.
-pref("dom.telephony.app.phone.url", "http://localhost:7777/data/local/apps/dialer/dialer.html http://localhost:7777/data/local/apps/homescreen/homescreen.html http://localhost:7777/apps/dialer/dialer.html http://localhost:7777/apps/homescreen/homescreen.html");
+pref("dom.telephony.app.phone.url", "http://localhost:7777/data/local/apps/dialer/dialer.html,http://localhost:7777/data/local/apps/homescreen/homescreen.html,http://localhost:7777/apps/dialer/dialer.html,http://localhost:7777/apps/homescreen/homescreen.html");
 
 // Device pixel to CSS px ratio, in percent. Set to -1 to calculate based on display density.
 pref("browser.viewport.scaleRatio", -1);
 
 /* disable text selection */
 pref("browser.ignoreNativeFrameTextSelection", true);
 
 /* cache prefs */
@@ -439,8 +439,17 @@ pref("b2g.keys.menu.enabled", true);
 pref("b2g.keys.search.enabled", false);
 
 // Screen timeout in minutes
 pref("power.screen.timeout", 60);
 
 pref("full-screen-api.enabled", true);
 
 pref("media.volume.steps", 10);
+
+// Data connection settings. These will eventually live in the
+// navigator.settings API, or even in a database where we can look
+// it up automatically (bug 729440), but for this will have to do.
+pref("ril.data.enabled", false);
+pref("ril.data.roaming.enabled", false);
+pref("ril.data.apn", "");
+pref("ril.data.user", "");
+pref("ril.data.passwd", "");
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -277,17 +277,17 @@ 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", false);
+pref("browser.urlbar.autoFill", true);
 pref("browser.urlbar.autoFill.typed", true);
 // 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);
 
@@ -1037,19 +1037,16 @@ pref("devtools.inspector.sidebarOpen", f
 pref("devtools.inspector.activeSidebar", "ruleview");
 
 // Enable the Debugger
 pref("devtools.debugger.enabled", false);
 
 // The default Debugger UI height
 pref("devtools.debugger.ui.height", 250);
 
-// Disable remote debugging protocol logging
-pref("devtools.debugger.log", false);
-
 // Enable the style inspector
 pref("devtools.styleinspector.enabled", true);
 
 // Enable the Tilt inspector
 pref("devtools.tilt.enabled", true);
 pref("devtools.tilt.intro_transition", true);
 pref("devtools.tilt.outro_transition", true);
 
--- a/browser/base/content/browser.css
+++ b/browser/base/content/browser.css
@@ -435,17 +435,18 @@ window[chromehidden~="toolbar"] toolbar:
 }
 
 /* We use the iconBox as the notification anchor when a popup notification is
    created with a null anchorID, so in that case use a default anchor icon. */
 #notification-popup-box[anchorid="notification-popup-box"] > #default-notification-icon,
 #notification-popup-box[anchorid="geo-notification-icon"] > #geo-notification-icon,
 #notification-popup-box[anchorid="indexedDB-notification-icon"] > #indexedDB-notification-icon,
 #notification-popup-box[anchorid="addons-notification-icon"] > #addons-notification-icon,
-#notification-popup-box[anchorid="password-notification-icon"] > #password-notification-icon {
+#notification-popup-box[anchorid="password-notification-icon"] > #password-notification-icon,
+#notification-popup-box[anchorid="webapps-notification-icon"] > #webapps-notification-icon {
   display: -moz-box;
 }
 
 #invalid-form-popup > description {
   max-width: 280px;
 }
 
 #geolocation-notification {
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -4747,20 +4747,25 @@ var XULBrowserWindow = {
         } catch (e) {}
         URLBarSetURI(uri);
 
         // Update starring UI
         PlacesStarButton.updateState();
       }
 
       // Show or hide browser chrome based on the whitelist
-      if (this.hideChromeForLocation(location))
+      if (this.hideChromeForLocation(location)) {
         document.documentElement.setAttribute("disablechrome", "true");
-      else
-        document.documentElement.removeAttribute("disablechrome");
+      } else {
+        let ss = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore);
+        if (ss.getTabValue(gBrowser.selectedTab, "appOrigin"))
+          document.documentElement.setAttribute("disablechrome", "true");
+        else
+          document.documentElement.removeAttribute("disablechrome");
+      }
 
       // Disable find commands in documents that ask for them to be disabled.
       let disableFind = false;
       if (aLocationURI &&
           (aLocationURI.schemeIs("about") || aLocationURI.schemeIs("chrome"))) {
         let docElt = content.document.documentElement;
         disableFind = docElt && docElt.getAttribute("disablefastfind") == "true";
       }
@@ -9095,17 +9100,16 @@ XPCOMUtils.defineLazyGetter(window, "gSh
   let sysInfo = Components.classes["@mozilla.org/system-info;1"]
                           .getService(Components.interfaces.nsIPropertyBag2);
   return parseFloat(sysInfo.getProperty("version")) < 6;
 #else
   return false;
 #endif
 });
 
-
 var MousePosTracker = {
   _listeners: [],
   _x: 0,
   _y: 0,
   get _windowUtils() {
     delete this._windowUtils;
     return this._windowUtils = window.getInterface(Ci.nsIDOMWindowUtils);
   },
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -522,16 +522,17 @@
                  onfocus="document.getElementById('identity-box').style.MozUserFocus= 'normal'"
                  onblur="setTimeout(function() document.getElementById('identity-box').style.MozUserFocus = '', 0);">
           <box id="notification-popup-box" hidden="true" align="center">
             <image id="default-notification-icon" class="notification-anchor-icon" role="button"/>
             <image id="geo-notification-icon" class="notification-anchor-icon" role="button"/>
             <image id="addons-notification-icon" class="notification-anchor-icon" role="button"/>
             <image id="indexedDB-notification-icon" class="notification-anchor-icon" role="button"/>
             <image id="password-notification-icon" class="notification-anchor-icon" role="button"/>
+            <image id="webapps-notification-icon" class="webapps-anchor-icon" role="button"/>
           </box>
           <!-- Use onclick instead of normal popup= syntax since the popup
                code fires onmousedown, and hence eats our favicon drag events.
                We only add the identity-box button to the tab order when the location bar
                has focus, otherwise pressing F6 focuses it instead of the location bar -->
           <box id="identity-box" role="button"
                onclick="gIdentityHandler.handleIdentityButtonEvent(event);"
                onkeypress="gIdentityHandler.handleIdentityButtonEvent(event);"
@@ -998,18 +999,16 @@
              hidden="true">
 #ifdef XP_MACOSX
       <toolbarbutton id="highlighter-closebutton"
                      oncommand="InspectorUI.closeInspectorUI(false);"
                      tooltiptext="&inspectCloseButton.tooltiptext;"/>
 #endif
       <toolbarbutton id="inspector-inspect-toolbutton"
                      class="devtools-toolbarbutton"
-                     label="&inspectButton.label;"
-                     accesskey="&inspectButton.accesskey;"
                      command="Inspector:Inspect"/>
       <toolbarbutton id="inspector-treepanel-toolbutton"
                      class="devtools-toolbarbutton"
                      label="&htmlPanel.label;"
                      accesskey="&htmlPanel.accesskey;"
                      tooltiptext="&htmlPanel.tooltiptext;"
                      command="Inspector:HTMLPanel"/>
       <arrowscrollbox id="inspector-breadcrumbs"
--- a/browser/base/content/highlighter.css
+++ b/browser/base/content/highlighter.css
@@ -82,8 +82,12 @@ html|*#highlighter-nodeinfobar-tagname {
 
 #highlighter-nodeinfobar-container[disabled] {
   visibility: hidden;
 }
 
 html|*#highlighter-nodeinfobar-tagname {
   text-transform: lowercase;
 }
+
+.devtools-toolbarbutton:not([label]) > .toolbarbutton-text {
+  display: none;
+}
--- a/browser/base/content/newtab/cells.js
+++ b/browser/base/content/newtab/cells.js
@@ -10,26 +10,24 @@
  * aren't handled here.
  */
 function Cell(aGrid, aNode) {
   this._grid = aGrid;
   this._node = aNode;
   this._node._newtabCell = this;
 
   // Register drag-and-drop event handlers.
-  ["DragEnter", "DragOver", "DragExit", "Drop"].forEach(function (aType) {
-    let method = "on" + aType;
-    this[method] = this[method].bind(this);
-    this._node.addEventListener(aType.toLowerCase(), this[method], false);
+  ["dragenter", "dragover", "dragexit", "drop"].forEach(function (aType) {
+    this._node.addEventListener(aType, this, false);
   }, this);
 }
 
 Cell.prototype = {
   /**
-   *
+   * The grid.
    */
   _grid: null,
 
   /**
    * The cell's DOM node.
    */
   get node() this._node,
 
@@ -92,46 +90,32 @@ Cell.prototype = {
    * Checks whether the cell contains a site (is empty).
    * @return Whether the cell is empty.
    */
   isEmpty: function Cell_isEmpty() {
     return !this.site;
   },
 
   /**
-   * Event handler for the 'dragenter' event.
-   * @param aEvent The dragenter event.
-   */
-  onDragEnter: function Cell_onDragEnter(aEvent) {
-    if (gDrag.isValid(aEvent)) {
-      aEvent.preventDefault();
-      gDrop.enter(this, aEvent);
-    }
-  },
-
-  /**
-   * Event handler for the 'dragover' event.
-   * @param aEvent The dragover event.
+   * Handles all cell events.
    */
-  onDragOver: function Cell_onDragOver(aEvent) {
-    if (gDrag.isValid(aEvent))
-      aEvent.preventDefault();
-  },
+  handleEvent: function Cell_handleEvent(aEvent) {
+    if (aEvent.type != "dragexit" && !gDrag.isValid(aEvent))
+      return;
 
-  /**
-   * Event handler for the 'dragexit' event.
-   * @param aEvent The dragexit event.
-   */
-  onDragExit: function Cell_onDragExit(aEvent) {
-    gDrop.exit(this, aEvent);
-  },
-
-  /**
-   * Event handler for the 'drop' event.
-   * @param aEvent The drop event.
-   */
-  onDrop: function Cell_onDrop(aEvent) {
-    if (gDrag.isValid(aEvent)) {
-      aEvent.preventDefault();
-      gDrop.drop(this, aEvent);
+    switch (aEvent.type) {
+      case "dragenter":
+        aEvent.preventDefault();
+        gDrop.enter(this, aEvent);
+        break;
+      case "dragover":
+        aEvent.preventDefault();
+        break;
+      case "dragexit":
+        gDrop.exit(this, aEvent);
+        break;
+      case "drop":
+        aEvent.preventDefault();
+        gDrop.drop(this, aEvent);
+        break;
     }
   }
 };
--- a/browser/base/content/newtab/drag.js
+++ b/browser/base/content/newtab/drag.js
@@ -31,21 +31,21 @@ let gDrag = {
   /**
    * Start a new drag operation.
    * @param aSite The site that's being dragged.
    * @param aEvent The 'dragstart' event.
    */
   start: function Drag_start(aSite, aEvent) {
     this._draggedSite = aSite;
 
-    // Prevent moz-transform for left, top.
-    aSite.node.setAttribute("dragged", "true");
-
-    // Make sure the dragged site is floating above the grid.
-    aSite.node.setAttribute("ontop", "true");
+    // Mark nodes as being dragged.
+    let selector = ".newtab-site, .newtab-control, .newtab-thumbnail";
+    let nodes = aSite.node.parentNode.querySelectorAll(selector);
+    for (let i = 0; i < nodes.length; i++)
+      nodes[i].setAttribute("dragged", "true");
 
     this._setDragData(aSite, aEvent);
 
     // Store the cursor offset.
     let node = aSite.node;
     let rect = node.getBoundingClientRect();
     this._offsetX = aEvent.clientX - rect.left;
     this._offsetY = aEvent.clientY - rect.top;
@@ -83,35 +83,38 @@ let gDrag = {
   },
 
   /**
    * Ends the current drag operation.
    * @param aSite The site that's being dragged.
    * @param aEvent The 'dragend' event.
    */
   end: function Drag_end(aSite, aEvent) {
-    aSite.node.removeAttribute("dragged");
+    let nodes = aSite.node.parentNode.querySelectorAll("[dragged]");
+    for (let i = 0; i < nodes.length; i++)
+      nodes[i].removeAttribute("dragged");
 
     // Slide the dragged site back into its cell (may be the old or the new cell).
-    gTransformation.slideSiteTo(aSite, aSite.cell, {
-      unfreeze: true,
-      callback: function () aSite.node.removeAttribute("ontop")
-    });
+    gTransformation.slideSiteTo(aSite, aSite.cell, {unfreeze: true});
 
     this._draggedSite = null;
   },
 
   /**
    * Checks whether we're responsible for a given drag event.
    * @param aEvent The drag event to check.
    * @return Whether we should handle this drag and drop operation.
    */
   isValid: function Drag_isValid(aEvent) {
     let dt = aEvent.dataTransfer;
-    return dt && dt.types.contains("text/x-moz-url");
+    let mimeType = "text/x-moz-url";
+
+    // Check that the drag data is non-empty.
+    // Can happen when dragging places folders.
+    return dt && dt.types.contains(mimeType) && dt.getData(mimeType);
   },
 
   /**
    * Initializes the drag data for the current drag operation.
    * @param aSite The site that's being dragged.
    * @param aEvent The 'dragstart' event.
    */
   _setDragData: function Drag_setDragData(aSite, aEvent) {
@@ -123,18 +126,18 @@ let gDrag = {
     dt.setData("text/plain", url);
     dt.setData("text/uri-list", url);
     dt.setData("text/x-moz-url", url + "\n" + title);
     dt.setData("text/html", "<a href=\"" + url + "\">" + url + "</a>");
 
     // Create and use an empty drag element. We don't want to use the default
     // drag image with its default opacity.
     let dragElement = document.createElementNS(HTML_NAMESPACE, "div");
-    dragElement.classList.add("drag-element");
-    let body = document.getElementById("body");
-    body.appendChild(dragElement);
+    dragElement.classList.add("newtab-drag");
+    let scrollbox = document.getElementById("newtab-scrollbox");
+    scrollbox.appendChild(dragElement);
     dt.setDragImage(dragElement, 0, 0);
 
     // After the 'dragstart' event has been processed we can remove the
     // temporary drag element from the DOM.
-    setTimeout(function () body.removeChild(dragElement), 0);
+    setTimeout(function () scrollbox.removeChild(dragElement), 0);
   }
 };
--- a/browser/base/content/newtab/dropTargetShim.js
+++ b/browser/base/content/newtab/dropTargetShim.js
@@ -21,52 +21,60 @@ let gDropTargetShim = {
   _lastDropTarget: null,
 
   /**
    * Initializes the drop target shim.
    */
   init: function DropTargetShim_init() {
     let node = gGrid.node;
 
-    this._dragover = this._dragover.bind(this);
+    // Add drag event handlers.
+    node.addEventListener("dragstart", this, true);
+    node.addEventListener("dragend", this, true);
+  },
 
-    // Add drag event handlers.
-    node.addEventListener("dragstart", this._start.bind(this), true);
-    // XXX bug 505521 - Don't listen for drag, it's useless at the moment.
-    //node.addEventListener("drag", this._drag.bind(this), false);
-    node.addEventListener("dragend", this._end.bind(this), true);
+  /**
+   * Handles all shim events.
+   */
+  handleEvent: function DropTargetShim_handleEvent(aEvent) {
+    switch (aEvent.type) {
+      case "dragstart":
+        this._start(aEvent);
+        break;
+      case "dragover":
+        this._dragover(aEvent);
+        break;
+      case "dragend":
+        this._end(aEvent);
+        break;
+    }
   },
 
   /**
    * Handles the 'dragstart' event.
    * @param aEvent The 'dragstart' event.
    */
   _start: function DropTargetShim_start(aEvent) {
-    if (aEvent.target.classList.contains("site")) {
+    if (aEvent.target.classList.contains("newtab-link")) {
       gGrid.lock();
 
       // XXX bug 505521 - Listen for dragover on the document.
-      document.documentElement.addEventListener("dragover", this._dragover, false);
+      document.documentElement.addEventListener("dragover", this, false);
     }
   },
 
   /**
    * Handles the 'drag' event and determines the current drop target.
    * @param aEvent The 'drag' event.
    */
   _drag: function DropTargetShim_drag(aEvent) {
     // Let's see if we find a drop target.
     let target = this._findDropTarget(aEvent);
 
-    if (target == this._lastDropTarget) {
-      // XXX bug 505521 - Don't fire dragover for now (causes recursion).
-      /*if (target)
-        // The last drop target is valid and didn't change.
-        this._dispatchEvent(aEvent, "dragover", target);*/
-    } else {
+    if (target != this._lastDropTarget) {
       if (this._lastDropTarget)
         // We left the last drop target.
         this._dispatchEvent(aEvent, "dragexit", this._lastDropTarget);
 
       if (target)
         // We're now hovering a (new) drop target.
         this._dispatchEvent(aEvent, "dragenter", target);
 
@@ -79,17 +87,17 @@ let gDropTargetShim = {
   },
 
   /**
    * Handles the 'dragover' event as long as bug 505521 isn't fixed to get
    * current mouse cursor coordinates while dragging.
    * @param aEvent The 'dragover' event.
    */
   _dragover: function DropTargetShim_dragover(aEvent) {
-    let sourceNode = aEvent.dataTransfer.mozSourceNode;
+    let sourceNode = aEvent.dataTransfer.mozSourceNode.parentNode;
     gDrag.drag(sourceNode._newtabSite, aEvent);
 
     this._drag(aEvent);
   },
 
   /**
    * Handles the 'dragend' event.
    * @param aEvent The 'dragend' event.
@@ -112,17 +120,17 @@ let gDropTargetShim = {
       // Clean up.
       this._lastDropTarget = null;
       this._cellPositions = null;
     }
 
     gGrid.unlock();
 
     // XXX bug 505521 - Remove the document's dragover listener.
-    document.documentElement.removeEventListener("dragover", this._dragover, false);
+    document.documentElement.removeEventListener("dragover", this, false);
   },
 
   /**
    * Determines the current drop target by matching the dragged site's position
    * against all cells in the grid.
    * @return The currently hovered drop target or null.
    */
   _findDropTarget: function DropTargetShim_findDropTarget() {
--- a/browser/base/content/newtab/grid.js
+++ b/browser/base/content/newtab/grid.js
@@ -19,17 +19,17 @@ let gGrid = {
    */
   _siteFragment: null,
 
   /**
    * All cells contained in the grid.
    */
   get cells() {
     let cells = [];
-    let children = this.node.querySelectorAll("li");
+    let children = this.node.querySelectorAll(".newtab-cell");
     for (let i = 0; i < children.length; i++)
       cells.push(new Cell(this, children[i]));
 
     // Replace the getter with our cached value.
     Object.defineProperty(this, "cells", {value: cells, enumerable: true});
 
     return cells;
   },
@@ -38,18 +38,18 @@ let gGrid = {
    * All sites contained in the grid's cells. Sites may be empty.
    */
   get sites() [cell.site for each (cell in this.cells)],
 
   /**
    * Initializes the grid.
    * @param aSelector The query selector of the grid.
    */
-  init: function Grid_init(aSelector) {
-    this._node = document.querySelector(aSelector);
+  init: function Grid_init() {
+    this._node = document.getElementById("newtab-grid");
     this._createSiteFragment();
     this._draw();
   },
 
   /**
    * Creates a new site in the grid.
    * @param aLink The new site's link.
    * @param aCell The cell that will contain the new site.
@@ -91,31 +91,30 @@ let gGrid = {
   unlock: function Grid_unlock() {
     this.node.removeAttribute("locked");
   },
 
   /**
    * Creates the DOM fragment that is re-used when creating sites.
    */
   _createSiteFragment: function Grid_createSiteFragment() {
-    let site = document.createElementNS(HTML_NAMESPACE, "a");
-    site.classList.add("site");
+    let site = document.createElementNS(HTML_NAMESPACE, "div");
+    site.classList.add("newtab-site");
     site.setAttribute("draggable", "true");
 
     // Create the site's inner HTML code.
     site.innerHTML =
-      '<img class="site-img" width="' + THUMB_WIDTH +'" ' +
-      ' height="' + THUMB_HEIGHT + '" alt=""/>' +
-      '<span class="site-title"/>' +
-      '<span class="site-strip">' +
-      '  <input class="button strip-button strip-button-pin" type="button"' +
-      '   tabindex="-1" title="' + newTabString("pin") + '"/>' +
-      '  <input class="button strip-button strip-button-block" type="button"' +
-      '   tabindex="-1" title="' + newTabString("block") + '"/>' +
-      '</span>';
+      '<a class="newtab-link">' +
+      '  <span class="newtab-thumbnail"/>' +
+      '  <span class="newtab-title"/>' +
+      '</a>' +
+      '<input type="button" title="' + newTabString("pin") + '"' +
+      '       class="newtab-control newtab-control-pin"/>' +
+      '<input type="button" title="' + newTabString("block") + '"' +
+      '       class="newtab-control newtab-control-block"/>';
 
     this._siteFragment = document.createDocumentFragment();
     this._siteFragment.appendChild(site);
   },
 
   /**
    * Draws the grid, creates all sites and puts them into their cells.
    */
--- a/browser/base/content/newtab/newTab.css
+++ b/browser/base/content/newtab/newTab.css
@@ -1,173 +1,186 @@
 :root {
-  -moz-appearance: none;
-}
-
-#scrollbox:not([page-disabled]) {
-  overflow: auto;
+  -moz-user-focus: normal;
 }
 
-#body {
-  position: relative;
-  margin: 0;
-  min-width: 675px;
-  -moz-user-select: none;
-}
-
-.button {
+input[type=button] {
   cursor: pointer;
 }
 
-/* TOOLBAR */
-#toolbar {
-  position: absolute;
+/* SCROLLBOX */
+#newtab-scrollbox {
+  display: -moz-box;
+  position: relative;
+  -moz-box-flex: 1;
 }
 
-#toolbar[page-disabled] {
-  position: fixed;
+#newtab-scrollbox:not([page-disabled]) {
+  overflow: auto;
 }
 
-#toolbar:-moz-locale-dir(rtl) {
-  left: 8px;
+/* TOGGLE */
+#newtab-toggle {
+  position: absolute;
+  top: 12px;
+  right: 12px;
+}
+
+#newtab-toggle:-moz-locale-dir(rtl) {
+  left: 12px;
   right: auto;
 }
 
-.toolbar-button {
-  position: absolute;
-  cursor: pointer;
-  -moz-transition: opacity 200ms ease-out;
+/* MARGINS */
+#newtab-vertical-margin {
+  display: -moz-box;
+  position: relative;
+  -moz-box-flex: 1;
+  -moz-box-orient: vertical;
+}
+
+#newtab-margin-top {
+  min-height: 50px;
+  max-height: 80px;
+  -moz-box-flex: 1;
+}
+
+#newtab-margin-bottom {
+  min-height: 40px;
+  max-height: 100px;
+  -moz-box-flex: 1;
 }
 
-#toolbar-button-show,
-#toolbar-button-reset {
+#newtab-horizontal-margin {
+  display: -moz-box;
+  -moz-box-flex: 5;
+}
+
+.newtab-side-margin {
+  min-width: 40px;
+  max-width: 300px;
+  -moz-box-flex: 1;
+}
+
+/* GRID */
+#newtab-grid {
+  display: -moz-box;
+  -moz-box-flex: 5;
+  -moz-box-orient: vertical;
+  min-width: 600px;
+  min-height: 400px;
+  -moz-transition: 100ms ease-out;
+  -moz-transition-property: opacity;
+}
+
+#newtab-grid[page-disabled] {
   opacity: 0;
+}
+
+#newtab-grid[locked],
+#newtab-grid[page-disabled] {
   pointer-events: none;
 }
 
-#toolbar-button-reset[modified],
-#toolbar-button-show[page-disabled] {
-  opacity: 1;
-  pointer-events: auto;
-}
-
-#toolbar-button-hide[page-disabled],
-#toolbar-button-reset[page-disabled] {
-  opacity: 0;
-  pointer-events: none;
-}
-
-/* GRID */
-#grid {
-  width: 637px;
-  height: 411px;
-  overflow: hidden;
-  list-style-type: none;
-  -moz-transition: opacity 200ms ease-out;
-}
-
-#grid[page-disabled] {
-  opacity: 0;
-}
-
-#grid[page-disabled],
-#grid[locked] {
-  pointer-events: none;
+/* ROWS */
+.newtab-row {
+  display: -moz-box;
+  -moz-box-orient: horizontal;
+  -moz-box-direction: normal;
+  -moz-box-flex: 1;
 }
 
 /* CELLS */
-.cell {
-  float: left;
-  width: 201px;
-  height: 127px;
-  margin-bottom: 15px;
-  -moz-margin-end: 16px;
-}
-
-.cell:-moz-locale-dir(rtl) {
-  float: right;
-}
-
-.cell:nth-child(3n+3) {
-  -moz-margin-end: 0;
+.newtab-cell {
+  display: -moz-box;
+  -moz-box-flex: 1;
 }
 
 /* SITES */
-.site {
-  display: block;
+.newtab-site {
   position: relative;
-  width: 201px;
-  height: 127px;
+  -moz-box-flex: 1;
+  -moz-transition: 100ms ease-out;
+  -moz-transition-property: top, left, opacity;
 }
 
-.site[frozen] {
+.newtab-site[frozen] {
   position: absolute;
   pointer-events: none;
 }
 
-.site[ontop] {
+.newtab-site[dragged] {
+  -moz-transition-property: none;
   z-index: 10;
 }
 
-/* SITE IMAGE */
-.site-img {
-  display: block;
-  opacity: 0.75;
-  -moz-transition: opacity 200ms ease-out;
+/* LINK + THUMBNAILS */
+.newtab-link,
+.newtab-thumbnail {
+  position: absolute;
+  left: 0;
+  top: 0;
+  right: 0;
+  bottom: 0;
 }
 
-.site:hover > .site-img,
-.site[ontop] > .site-img,
-.site:-moz-focusring > .site-img {
+.newtab-thumbnail {
+  opacity: .8;
+  -moz-transition: opacity 100ms ease-out;
+}
+
+.newtab-thumbnail[dragged],
+.newtab-link:-moz-focusring > .newtab-thumbnail,
+.newtab-site:hover > .newtab-link > .newtab-thumbnail {
   opacity: 1;
 }
 
-.site-img[loading] {
-  display: none;
-}
-
-/* SITE TITLE */
-.site-title {
+/* TITLES */
+.newtab-title {
   position: absolute;
   left: 0;
+  right: 0;
   bottom: 0;
+  white-space: nowrap;
   overflow: hidden;
+  text-overflow: ellipsis;
 }
 
-/* SITE STRIP */
-.site-strip {
+/* CONTROLS */
+.newtab-control {
   position: absolute;
-  left: 0;
-  top: 0;
-  width: 195px;
-  height: 17px;
-  overflow: hidden;
+  top: 4px;
   opacity: 0;
-  -moz-transition: opacity 200ms ease-out;
+  -moz-transition: opacity 100ms ease-out;
 }
 
-.site:hover:not([frozen]) > .site-strip {
+.newtab-control:-moz-focusring,
+.newtab-site:hover > .newtab-control {
   opacity: 1;
 }
 
-.strip-button-pin,
-.strip-button-block:-moz-locale-dir(rtl) {
-  float: left;
+.newtab-control[dragged] {
+  opacity: 0 !important;
 }
 
-.strip-button-block,
-.strip-button-pin:-moz-locale-dir(rtl) {
-  float: right;
+.newtab-control-pin:-moz-locale-dir(ltr),
+.newtab-control-block:-moz-locale-dir(rtl) {
+  left: 4px;
+}
+
+.newtab-control-block:-moz-locale-dir(ltr),
+.newtab-control-pin:-moz-locale-dir(rtl) {
+  right: 4px;
 }
 
 /* DRAG & DROP */
 
 /*
  * This is just a temporary drag element used for dataTransfer.setDragImage()
  * so that we can use custom drag images and elements. It needs an opacity of
  * 0.01 so that the core code detects that it's in fact a visible element.
  */
-.drag-element {
+.newtab-drag {
   width: 1px;
   height: 1px;
   background-color: #fff;
   opacity: 0.01;
 }
--- a/browser/base/content/newtab/newTab.js
+++ b/browser/base/content/newtab/newTab.js
@@ -25,26 +25,23 @@ let {
 XPCOMUtils.defineLazyGetter(this, "gStringBundle", function() {
   return Services.strings.
     createBundle("chrome://browser/locale/newTab.properties");
 });
 
 function newTabString(name) gStringBundle.GetStringFromName('newtab.' + name);
 
 const HTML_NAMESPACE = "http://www.w3.org/1999/xhtml";
-const THUMB_WIDTH = 201;
-const THUMB_HEIGHT = 127;
 
 #include batch.js
 #include transformations.js
 #include page.js
-#include toolbar.js
 #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");
+gPage.init();
--- a/browser/base/content/newtab/newTab.xul
+++ b/browser/base/content/newtab/newTab.xul
@@ -1,40 +1,56 @@
 <?xml version="1.0" encoding="UTF-8"?>
 
 # 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/.
 
-<?xml-stylesheet href="chrome://global/skin/global.css"?>
+<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
 <?xml-stylesheet href="chrome://browser/content/newtab/newTab.css" type="text/css"?>
 <?xml-stylesheet href="chrome://browser/skin/newtab/newTab.css" type="text/css"?>
 
 <!DOCTYPE window [
   <!ENTITY % newTabDTD SYSTEM "chrome://browser/locale/newTab.dtd">
   %newTabDTD;
 ]>
 
-<xul:window xmlns="http://www.w3.org/1999/xhtml"
+<xul:window id="newtab-window" xmlns="http://www.w3.org/1999/xhtml"
             xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
-            disablefastfind="true" title="&newtab.pageTitle;">
-  <xul:vbox id="scrollbox" flex="1" title=" ">
-    <body id="body">
-      <div id="toolbar">
-        <input class="button toolbar-button" id="toolbar-button-show"
-               type="button" title="&newtab.show;"/>
-        <input class="button toolbar-button" id="toolbar-button-hide"
-               type="button" title="&newtab.hide;"/>
-        <input class="button toolbar-button" id="toolbar-button-reset"
-               type="button" title="&newtab.reset;"/>
+            xul:disablefastfind="true" xul:title="&newtab.pageTitle;">
+
+  <div id="newtab-scrollbox">
+
+    <div id="newtab-vertical-margin">
+      <div id="newtab-margin-top"/>
+
+      <div id="newtab-horizontal-margin">
+        <div class="newtab-side-margin"/>
+
+        <div id="newtab-grid">
+          <div class="newtab-row">
+            <div class="newtab-cell"/>
+            <div class="newtab-cell"/>
+            <div class="newtab-cell"/>
+          </div>
+          <div class="newtab-row">
+            <div class="newtab-cell"/>
+            <div class="newtab-cell"/>
+            <div class="newtab-cell"/>
+          </div>
+          <div class="newtab-row">
+            <div class="newtab-cell"/>
+            <div class="newtab-cell"/>
+            <div class="newtab-cell"/>
+          </div>
+        </div>
+
+        <div class="newtab-side-margin"/>
       </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>
+      <div id="newtab-margin-bottom"/>
+    </div>
+    <input id="newtab-toggle" type="button"/>
+  </div>
 
-      <xul:script type="text/javascript;version=1.8"
-                  src="chrome://browser/content/newtab/newTab.js"/>
-    </body>
-  </xul:vbox>
+  <xul:script type="text/javascript;version=1.8"
+              src="chrome://browser/content/newtab/newTab.js"/>
 </xul:window>
--- a/browser/base/content/newtab/page.js
+++ b/browser/base/content/newtab/page.js
@@ -6,35 +6,34 @@
 
 /**
  * This singleton represents the whole 'New Tab Page' and takes care of
  * initializing all its components.
  */
 let gPage = {
   /**
    * Initializes the page.
-   * @param aToolbarSelector The query selector for the page toolbar.
-   * @param aGridSelector The query selector for the grid.
    */
-  init: function Page_init(aToolbarSelector, aGridSelector) {
-    gToolbar.init(aToolbarSelector);
-    this._gridSelector = aGridSelector;
-
+  init: function Page_init() {
     // Add ourselves to the list of pages to receive notifications.
     gAllPages.register(this);
 
     // Listen for 'unload' to unregister this page.
-    function unload() { gAllPages.unregister(this); }
-    addEventListener("unload", unload.bind(this), false);
+    addEventListener("unload", this, false);
+
+    // Listen for toggle button clicks.
+    let button = document.getElementById("newtab-toggle");
+    button.addEventListener("click", this, false);
 
     // Check if the new tab feature is enabled.
-    if (gAllPages.enabled)
+    let enabled = gAllPages.enabled;
+    if (enabled)
       this._init();
-    else
-      this._updateAttributes(false);
+
+    this._updateAttributes(enabled);
   },
 
   /**
    * Listens for notifications specific to this page.
    */
   observe: function Page_observe() {
     let enabled = gAllPages.enabled;
     this._updateAttributes(enabled);
@@ -43,132 +42,82 @@ let gPage = {
     if (enabled)
       this._init();
   },
 
   /**
    * Updates the whole page and the grid when the storage has changed.
    */
   update: function Page_update() {
-    this.updateModifiedFlag();
     gGrid.refresh();
   },
 
   /**
-   * Checks if the page is modified and sets the CSS class accordingly
-   */
-  updateModifiedFlag: function Page_updateModifiedFlag() {
-    let node = document.getElementById("toolbar-button-reset");
-    let modified = this._isModified();
-
-    if (modified)
-      node.setAttribute("modified", "true");
-    else
-      node.removeAttribute("modified");
-
-    this._updateTabIndices(gAllPages.enabled, modified);
-  },
-
-  /**
    * Internally initializes the page. This runs only when/if the feature
    * is/gets enabled.
    */
   _init: function Page_init() {
     if (this._initialized)
       return;
 
     this._initialized = true;
 
     gLinks.populateCache(function () {
-      // Check if the grid is modified.
-      this.updateModifiedFlag();
-
       // Initialize and render the grid.
-      gGrid.init(this._gridSelector);
+      gGrid.init();
 
       // Initialize the drop target shim.
       gDropTargetShim.init();
 
 #ifdef XP_MACOSX
       // Workaround to prevent a delay on MacOSX due to a slow drop animation.
-      document.addEventListener("dragover", this.onDragOver, false);
-      document.addEventListener("drop", this.onDrop, false);
+      document.addEventListener("dragover", this, false);
+      document.addEventListener("drop", this, false);
 #endif
     }.bind(this));
   },
 
   /**
    * Updates the 'page-disabled' attributes of the respective DOM nodes.
-   * @param aValue Whether to set or remove attributes.
+   * @param aValue Whether the New Tab Page is enabled or not.
    */
   _updateAttributes: function Page_updateAttributes(aValue) {
-    let nodes = document.querySelectorAll("#grid, #scrollbox, #toolbar, .toolbar-button");
+    let selector = "#newtab-scrollbox, #newtab-toggle, #newtab-grid";
+    let nodes = document.querySelectorAll(selector);
 
     // Set the nodes' states.
     for (let i = 0; i < nodes.length; i++) {
       let node = nodes[i];
       if (aValue)
         node.removeAttribute("page-disabled");
       else
         node.setAttribute("page-disabled", "true");
     }
 
-    this._updateTabIndices(aValue, this._isModified());
-  },
-
-  /**
-   * Checks whether the page is modified.
-   * @return Whether the page is modified or not.
-   */
-  _isModified: function Page_isModified() {
-    // The page is considered modified only if sites have been removed.
-    return !gBlockedLinks.isEmpty();
+    // Update the toggle button's title.
+    let toggle = document.getElementById("newtab-toggle");
+    toggle.setAttribute("title", newTabString(aValue ? "hide" : "show"));
   },
 
   /**
-   * Updates the tab indices of focusable elements.
-   * @param aEnabled Whether the page is currently enabled.
-   * @param aModified Whether the page is currently modified.
+   * Handles all page events.
    */
-  _updateTabIndices: function Page_updateTabIndices(aEnabled, aModified) {
-    function setFocusable(aNode, aFocusable) {
-      if (aFocusable)
-        aNode.removeAttribute("tabindex");
-      else
-        aNode.setAttribute("tabindex", "-1");
-    }
-
-    // Sites and the 'hide' button are always focusable when the grid is shown.
-    let nodes = document.querySelectorAll(".site, #toolbar-button-hide");
-    for (let i = 0; i < nodes.length; i++)
-      setFocusable(nodes[i], aEnabled);
-
-    // The 'show' button is focusable when the grid is hidden.
-    let btnShow = document.getElementById("toolbar-button-show");
-    setFocusable(btnShow, !aEnabled);
-
-    // The 'reset' button is focusable when the grid is shown and modified.
-    let btnReset = document.getElementById("toolbar-button-reset");
-    setFocusable(btnReset, aEnabled && aModified);
-  },
-
-  /**
-   * Handles the 'dragover' event. Workaround to prevent a delay on MacOSX
-   * due to a slow drop animation.
-   * @param aEvent The 'dragover' event.
-   */
-  onDragOver: function Page_onDragOver(aEvent) {
-    if (gDrag.isValid(aEvent) && gDrag.draggedSite)
-      aEvent.preventDefault();
-  },
-
-  /**
-   * Handles the 'drop' event. Workaround to prevent a delay on MacOSX due to
-   * a slow drop animation.
-   * @param aEvent The 'drop' event.
-   */
-  onDrop: function Page_onDrop(aEvent) {
-    if (gDrag.isValid(aEvent) && gDrag.draggedSite) {
-      aEvent.preventDefault();
-      aEvent.stopPropagation();
+  handleEvent: function Page_handleEvent(aEvent) {
+    switch (aEvent.type) {
+      case "unload":
+        gAllPages.unregister(this);
+        break;
+      case "click":
+        gAllPages.enabled = !gAllPages.enabled;
+        break;
+      case "dragover":
+        if (gDrag.isValid(aEvent) && gDrag.draggedSite)
+          aEvent.preventDefault();
+        break;
+      case "drop":
+        if (gDrag.isValid(aEvent) && gDrag.draggedSite) {
+          aEvent.preventDefault();
+          aEvent.stopPropagation();
+        }
+        break;
     }
   }
 };
--- a/browser/base/content/newtab/sites.js
+++ b/browser/base/content/newtab/sites.js
@@ -77,22 +77,26 @@ Site.prototype = {
    */
   isPinned: function Site_isPinned() {
     return gPinnedLinks.isPinned(this._link);
   },
 
   /**
    * Blocks the site (removes it from the grid) and calls the given callback
    * when done.
-   * @param aCallback The callback to be called when finished.
+   * @param aCallback The function to be called when finished.
    */
   block: function Site_block(aCallback) {
-    gBlockedLinks.block(this._link);
-    gUpdater.updateGrid(aCallback);
-    gPage.updateModifiedFlag();
+    if (gBlockedLinks.isBlocked(this._link)) {
+      if (aCallback)
+        aCallback();
+    } else {
+      gBlockedLinks.block(this._link);
+      gUpdater.updateGrid(aCallback);
+    }
   },
 
   /**
    * Gets the DOM node specified by the given query selector.
    * @param aSelector The query selector.
    * @return The DOM node we found.
    */
   _querySelector: function Site_querySelector(aSelector) {
@@ -100,110 +104,76 @@ Site.prototype = {
   },
 
   /**
    * Updates attributes for all nodes which status depends on this site being
    * pinned or unpinned.
    * @param aPinned Whether this site is now pinned or unpinned.
    */
   _updateAttributes: function (aPinned) {
-    let buttonPin = this._querySelector(".strip-button-pin");
+    let control = this._querySelector(".newtab-control-pin");
 
     if (aPinned) {
-      this.node.setAttribute("pinned", true);
-      buttonPin.setAttribute("title", newTabString("unpin"));
+      control.setAttribute("pinned", true);
+      control.setAttribute("title", newTabString("unpin"));
     } else {
-      this.node.removeAttribute("pinned");
-      buttonPin.setAttribute("title", newTabString("pin"));
+      control.removeAttribute("pinned");
+      control.setAttribute("title", newTabString("pin"));
     }
   },
 
   /**
    * Renders the site's data (fills the HTML fragment).
    */
   _render: function Site_render() {
     let title = this.title || this.url;
-    this.node.setAttribute("title", title);
-    this.node.setAttribute("href", this.url);
-    this._querySelector(".site-title").textContent = title;
+    let link = this._querySelector(".newtab-link");
+    link.setAttribute("title", title);
+    link.setAttribute("href", this.url);
+    this._querySelector(".newtab-title").textContent = title;
 
     if (this.isPinned())
       this._updateAttributes(true);
 
-    this._renderThumbnail();
-  },
-
-  /**
-   * Renders the site's thumbnail.
-   */
-  _renderThumbnail: function Site_renderThumbnail() {
-    let img = this._querySelector(".site-img")
-    img.setAttribute("alt", this.title || this.url);
-    img.setAttribute("loading", "true");
-
-    // Wait until the image has loaded.
-    img.addEventListener("load", function onLoad() {
-      img.removeEventListener("load", onLoad, false);
-      img.removeAttribute("loading");
-    }, false);
-
-    // Set the thumbnail url.
-    img.setAttribute("src", PageThumbs.getThumbnailURL(this.url));
+    let thumbnailURL = PageThumbs.getThumbnailURL(this.url);
+    let thumbnail = this._querySelector(".newtab-thumbnail");
+    thumbnail.style.backgroundImage = "url(" + thumbnailURL + ")";
   },
 
   /**
    * Adds event handlers for the site and its buttons.
    */
   _addEventHandlers: function Site_addEventHandlers() {
     // Register drag-and-drop event handlers.
-    ["DragStart", /*"Drag",*/ "DragEnd"].forEach(function (aType) {
-      let method = "_on" + aType;
-      this[method] = this[method].bind(this);
-      this._node.addEventListener(aType.toLowerCase(), this[method], false);
-    }, this);
-
-    let self = this;
-
-    function pin(aEvent) {
-      if (aEvent)
-        aEvent.preventDefault();
+    this._node.addEventListener("dragstart", this, false);
+    this._node.addEventListener("dragend", this, false);
 
-      if (self.isPinned())
-        self.unpin();
-      else
-        self.pin();
-    }
-
-    function block(aEvent) {
-      if (aEvent)
-        aEvent.preventDefault();
-
-      self.block();
-    }
-
-    this._querySelector(".strip-button-pin").addEventListener("click", pin, false);
-    this._querySelector(".strip-button-block").addEventListener("click", block, false);
+    let controls = this.node.querySelectorAll(".newtab-control");
+    for (let i = 0; i < controls.length; i++)
+      controls[i].addEventListener("click", this, false);
   },
 
   /**
-   * Event handler for the 'dragstart' event.
-   * @param aEvent The drag event.
+   * Handles all site events.
    */
-  _onDragStart: function Site_onDragStart(aEvent) {
-    gDrag.start(this, aEvent);
-  },
-
-  /**
-   * Event handler for the 'drag' event.
-   * @param aEvent The drag event.
-  */
-  _onDrag: function Site_onDrag(aEvent) {
-    gDrag.drag(this, aEvent);
-  },
-
-  /**
-   * Event handler for the 'dragend' event.
-   * @param aEvent The drag event.
-   */
-  _onDragEnd: function Site_onDragEnd(aEvent) {
-    gDrag.end(this, aEvent);
+  handleEvent: function Site_handleEvent(aEvent) {
+    switch (aEvent.type) {
+      case "click":
+        aEvent.preventDefault();
+        if (aEvent.target.classList.contains("newtab-control-block"))
+          this.block();
+        else if (this.isPinned())
+          this.unpin();
+        else
+          this.pin();
+        break;
+      case "dragstart":
+        gDrag.start(this, aEvent);
+        break;
+      case "drag":
+        gDrag.drag(this, aEvent);
+        break;
+      case "dragend":
+        gDrag.end(this, aEvent);
+        break;
+    }
   }
 };
deleted file mode 100644
--- a/browser/base/content/newtab/toolbar.js
+++ /dev/null
@@ -1,87 +0,0 @@
-#ifdef 0
-/* 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/. */
-#endif
-
-/**
- * This singleton represents the page's toolbar that allows to enable/disable
- * the 'New Tab Page' feature and to reset the whole page.
- */
-let gToolbar = {
-  /**
-   * Initializes the toolbar.
-   * @param aSelector The query selector of the toolbar.
-   */
-  init: function Toolbar_init(aSelector) {
-    this._node = document.querySelector(aSelector);
-    let buttons = this._node.querySelectorAll("input");
-
-    // Listen for 'click' events on the toolbar buttons.
-    ["show", "hide", "reset"].forEach(function (aType, aIndex) {
-      let self = this;
-      let button = buttons[aIndex];
-      let handler = function () self[aType]();
-
-      button.addEventListener("click", handler, false);
-
-#ifdef XP_MACOSX
-      // Per default buttons lose focus after being clicked on Mac OS X.
-      // So when the URL bar has focus and a toolbar button is clicked the
-      // URL bar regains focus and the history pops up. We need to prevent
-      // that by explicitly removing its focus.
-      button.addEventListener("mousedown", function () {
-        window.focus();
-      }, false);
-#endif
-    }, this);
-  },
-
-  /**
-   * Enables the 'New Tab Page' feature.
-   */
-  show: function Toolbar_show() {
-    this._passButtonFocus("show", "hide");
-    gAllPages.enabled = true;
-  },
-
-  /**
-   * Disables the 'New Tab Page' feature.
-   */
-  hide: function Toolbar_hide() {
-    this._passButtonFocus("hide", "show");
-    gAllPages.enabled = false;
-  },
-
-  /**
-   * Resets the whole page and forces it to re-render its content.
-   * @param aCallback The callback to call when the page has been reset.
-   */
-  reset: function Toolbar_reset(aCallback) {
-    this._passButtonFocus("reset", "hide");
-    let node = gGrid.node;
-
-    // animate the page reset
-    gTransformation.fadeNodeOut(node, function () {
-      NewTabUtils.reset();
-
-      gLinks.populateCache(function () {
-        gAllPages.update();
-
-        // Without the setTimeout() we have a strange flicker.
-        setTimeout(function () gTransformation.fadeNodeIn(node, aCallback));
-      }, true);
-    });
-  },
-
-  /**
-   * Passes the focus from the current button to the next.
-   * @param aCurrent The button that currently has focus.
-   * @param aNext The button that is focused next.
-   */
-  _passButtonFocus: function Toolbar_passButtonFocus(aCurrent, aNext) {
-    if (document.querySelector("#toolbar-button-" + aCurrent + ":-moz-focusring"))
-      document.getElementById("toolbar-button-" + aNext).focus();
-  }
-};
-
--- a/browser/base/content/newtab/transformations.js
+++ b/browser/base/content/newtab/transformations.js
@@ -6,16 +6,34 @@
 
 /**
  * This singleton allows to transform the grid by repositioning a site's node
  * in the DOM and by showing or hiding the node. It additionally provides
  * convenience methods to work with a site's DOM node.
  */
 let gTransformation = {
   /**
+   * Returns the width of the left and top border of a cell. We need to take it
+   * into account when measuring and comparing site and cell positions.
+   */
+  get _cellBorderWidths() {
+    let cstyle = window.getComputedStyle(gGrid.cells[0].node, null);
+    let widths = {
+      left: parseInt(cstyle.getPropertyValue("border-left-width")),
+      top: parseInt(cstyle.getPropertyValue("border-top-width"))
+    };
+
+    // Cache this value, overwrite the getter.
+    Object.defineProperty(this, "_cellBorderWidths",
+                          {value: widths, enumerable: true});
+
+    return widths;
+  },
+
+  /**
    * Gets a DOM node's position.
    * @param aNode The DOM node.
    * @return A Rect instance with the position.
    */
   getNodePosition: function Transformation_getNodePosition(aNode) {
     let {left, top, width, height} = aNode.getBoundingClientRect();
     return new Rect(left + scrollX, top + scrollY, width, height);
   },
@@ -75,27 +93,38 @@ let gTransformation = {
     style.left = left + "px";
   },
 
   /**
    * Freezes a site in its current position by positioning it absolute.
    * @param aSite The site to freeze.
    */
   freezeSitePosition: function Transformation_freezeSitePosition(aSite) {
+    if (this._isFrozen(aSite))
+      return;
+
+    let style = aSite.node.style;
+    let comp = getComputedStyle(aSite.node, null);
+    style.width = comp.getPropertyValue("width")
+    style.height = comp.getPropertyValue("height");
+
     aSite.node.setAttribute("frozen", "true");
     this.setSitePosition(aSite, this.getNodePosition(aSite.node));
   },
 
   /**
    * Unfreezes a site by removing its absolute positioning.
    * @param aSite The site to unfreeze.
    */
   unfreezeSitePosition: function Transformation_unfreezeSitePosition(aSite) {
+    if (!this._isFrozen(aSite))
+      return;
+
     let style = aSite.node.style;
-    style.left = style.top = "";
+    style.left = style.top = style.width = style.height = "";
     aSite.node.removeAttribute("frozen");
   },
 
   /**
    * Slides the given site to the target node's position.
    * @param aSite The site to move.
    * @param aTarget The slide target.
    * @param aOptions Set of options (see below).
@@ -112,18 +141,23 @@ let gTransformation = {
     function finish() {
       if (aOptions && aOptions.unfreeze)
         self.unfreezeSitePosition(aSite);
 
       if (callback)
         callback();
     }
 
+    // We need to take the width of a cell's border into account.
+    targetPosition.left += this._cellBorderWidths.left;
+    targetPosition.top += this._cellBorderWidths.top;
+
     // Nothing to do here if the positions already match.
-    if (currentPosition.equals(targetPosition)) {
+    if (currentPosition.left == targetPosition.left &&
+        currentPosition.top == targetPosition.top) {
       finish();
     } else {
       this.setSitePosition(aSite, targetPosition);
       this._whenTransitionEnded(aSite.node, finish);
     }
   },
 
   /**
@@ -217,10 +251,19 @@ let gTransformation = {
    * Moves a site to the cell with the given index.
    * @param aSite The site to move.
    * @param aIndex The target cell's index.
    * @param aOptions Options that are directly passed to slideSiteTo().
    */
   _moveSite: function Transformation_moveSite(aSite, aIndex, aOptions) {
     this.freezeSitePosition(aSite);
     this.slideSiteTo(aSite, gGrid.cells[aIndex], aOptions);
+  },
+
+  /**
+   * Checks whether a site is currently frozen.
+   * @param aSite The site to check.
+   * @return Whether the given site is frozen.
+   */
+  _isFrozen: function Transformation_isFrozen(aSite) {
+    return aSite.node.hasAttribute("frozen");
   }
 };
--- a/browser/base/content/test/browser_pageInfo.js
+++ b/browser/base/content/test/browser_pageInfo.js
@@ -1,46 +1,38 @@
 function test() {
   waitForExplicitFinish();
 
   var pageInfo;
+
   gBrowser.selectedTab = gBrowser.addTab();
-  gBrowser.selectedBrowser.addEventListener("load", function () {
-    gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true);
+  gBrowser.selectedBrowser.addEventListener("load", function loadListener() {
+    gBrowser.selectedBrowser.removeEventListener("load", loadListener, true);
+
+    Services.obs.addObserver(observer, "page-info-dialog-loaded", false);
     pageInfo = BrowserPageInfo();
-    Services.obs.addObserver(observer, "page-info-dialog-loaded", false);
   }, true);
   content.location =
     "https://example.com/browser/browser/base/content/test/feed_tab.html";
 
   function observer(win, topic, data) {
-    if (topic != "page-info-dialog-loaded")
-      return;
-
-    Services.obs.removeObserver(observer, topic);
+    Services.obs.removeObserver(observer, "page-info-dialog-loaded");
     handlePageInfo();
   }
 
-  function $(aId) { return pageInfo.document.getElementById(aId) };
-
   function handlePageInfo() {
-    var feedTab = $("feedTab");
-    var feedListbox = $("feedListbox");
-
-    ok(feedListbox, "Feed list is null (feeds tab is broken)");
+    ok(pageInfo.document.getElementById("feedTab"), "Feed tab");
+    let feedListbox = pageInfo.document.getElementById("feedListbox");
+    ok(feedListbox, "Feed list");
 
     var feedRowsNum = feedListbox.getRowCount();
-
-    ok(feedRowsNum == 3, "Number of feeds listed: " +
-                         feedRowsNum + ", should be 3");
-
+    is(feedRowsNum, 3, "Number of feeds listed");
 
     for (var i = 0; i < feedRowsNum; i++) {
       let feedItem = feedListbox.getItemAtIndex(i);
-      ok(feedItem.getAttribute("name") == (i+1), 
-         "Name given: " + feedItem.getAttribute("name") + ", should be " + (i+1));
+      is(feedItem.getAttribute("name"), i + 1, "Feed name");
     }
 
     pageInfo.close();
     gBrowser.removeCurrentTab();
     finish();
   }
 }
--- a/browser/base/content/test/newtab/Makefile.in
+++ b/browser/base/content/test/newtab/Makefile.in
@@ -18,13 +18,15 @@ include $(topsrcdir)/config/rules.mk
 	browser_newtab_drop_preview.js \
 	browser_newtab_private_browsing.js \
 	browser_newtab_reset.js \
 	browser_newtab_tabsync.js \
 	browser_newtab_unpin.js \
 	browser_newtab_bug722273.js \
 	browser_newtab_bug723102.js \
 	browser_newtab_bug723121.js \
+	browser_newtab_bug725996.js \
+	browser_newtab_bug734043.js \
 	head.js \
 	$(NULL)
 
 libs::	$(_BROWSER_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
--- a/browser/base/content/test/newtab/browser_newtab_bug723102.js
+++ b/browser/base/content/test/newtab/browser_newtab_bug723102.js
@@ -7,11 +7,12 @@ function runTests() {
   setPinnedLinks("");
 
   yield addNewTabPageTab();
   let firstTab = gBrowser.selectedTab;
 
   yield addNewTabPageTab();
   gBrowser.removeTab(firstTab);
 
-  cw.gToolbar.hide();
+  ok(NewTabUtils.allPages.enabled, true, "page is enabled");
+  NewTabUtils.allPages.enabled = false;
   ok(cw.gGrid.node.hasAttribute("page-disabled"), "page is disabled");
 }
--- a/browser/base/content/test/newtab/browser_newtab_bug723121.js
+++ b/browser/base/content/test/newtab/browser_newtab_bug723121.js
@@ -5,25 +5,29 @@ function runTests() {
   setLinks("0,1,2,3,4,5,6,7,8");
   setPinnedLinks("");
 
   yield addNewTabPageTab();
   checkGridLocked(false, "grid is unlocked");
 
   let cell = cells[0].node;
   let site = cells[0].site.node;
+  let link = site.querySelector(".newtab-link");
 
-  sendDragEvent(site, "dragstart");
+  sendDragEvent(link, "dragstart");
   checkGridLocked(true, "grid is now locked");
 
-  sendDragEvent(site, "dragend");
+  sendDragEvent(link, "dragend");
   checkGridLocked(false, "grid isn't locked anymore");
 
   sendDragEvent(cell, "dragstart");
   checkGridLocked(false, "grid isn't locked - dragstart was ignored");
+
+  sendDragEvent(site, "dragstart");
+  checkGridLocked(false, "grid isn't locked - dragstart was ignored");
 }
 
 function checkGridLocked(aLocked, aMessage) {
   is(cw.gGrid.node.hasAttribute("locked"), aLocked, aMessage);
 }
 
 function sendDragEvent(aNode, aType) {
   let ifaceReq = cw.QueryInterface(Ci.nsIInterfaceRequestor);
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/newtab/browser_newtab_bug725996.js
@@ -0,0 +1,52 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+function runTests() {
+  setLinks("0,1,2,3,4,5,6,7,8");
+  setPinnedLinks("");
+
+  yield addNewTabPageTab();
+  checkGrid("0,1,2,3,4,5,6,7,8");
+
+  let cell = cells[0].node;
+
+  sendDropEvent(cell, "about:blank#99\nblank");
+  is(NewTabUtils.pinnedLinks.links[0].url, "about:blank#99",
+     "first cell is pinned and contains the dropped site");
+
+  yield whenPagesUpdated();
+  checkGrid("99p,0,1,2,3,4,5,6,7");
+
+  sendDropEvent(cell, "");
+  is(NewTabUtils.pinnedLinks.links[0].url, "about:blank#99",
+     "first cell is still pinned with the site we dropped before");
+}
+
+function sendDropEvent(aNode, aData) {
+  let ifaceReq = cw.QueryInterface(Ci.nsIInterfaceRequestor);
+  let windowUtils = ifaceReq.getInterface(Ci.nsIDOMWindowUtils);
+
+  let dataTransfer = {
+    mozUserCancelled: false,
+    setData: function () null,
+    setDragImage: function () null,
+    getData: function () aData,
+
+    types: {
+      contains: function (aType) aType == "text/x-moz-url"
+    },
+
+    mozGetDataAt: function (aType, aIndex) {
+      if (aIndex || aType != "text/x-moz-url")
+        return null;
+
+      return aData;
+    },
+  };
+
+  let event = cw.document.createEvent("DragEvents");
+  event.initDragEvent("drop", true, true, cw, 0, 0, 0, 0, 0,
+                      false, false, false, false, 0, null, dataTransfer);
+
+  windowUtils.dispatchDOMEventViaPresShell(aNode, event, true);
+}
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/newtab/browser_newtab_bug734043.js
@@ -0,0 +1,30 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+function runTests() {
+  // TODO Bug 735166 - Intermittent timeout in browser_newtab_bug734043.js
+  return;
+
+  setLinks("0,1,2,3,4,5,6,7,8");
+  setPinnedLinks("");
+
+  yield addNewTabPageTab();
+
+  let receivedError = false;
+  let block = cw.document.querySelector(".newtab-control-block");
+
+  function onError() {
+    receivedError = true;
+  }
+
+  cw.addEventListener("error", onError);
+
+  for (let i = 0; i < 3; i++) {
+    EventUtils.synthesizeMouseAtCenter(block, {}, cw);
+    yield executeSoon(TestRunner.next);
+  }
+
+  yield whenPagesUpdated();
+  ok(!receivedError, "we got here without any errors");
+  cw.removeEventListener("error", onError);
+}
--- a/browser/base/content/test/newtab/browser_newtab_disable.js
+++ b/browser/base/content/test/newtab/browser_newtab_disable.js
@@ -10,25 +10,25 @@ function runTests() {
   setLinks("0,1,2,3,4,5,6,7,8");
   setPinnedLinks("");
 
   yield addNewTabPageTab();
   let gridNode = cw.gGrid.node;
 
   ok(!gridNode.hasAttribute("page-disabled"), "page is not disabled");
 
-  cw.gToolbar.hide();
+  NewTabUtils.allPages.enabled = false;
   ok(gridNode.hasAttribute("page-disabled"), "page is disabled");
 
   let oldGridNode = cw.gGrid.node;
 
   // create a second new tage page and make sure it's disabled. enable it
   // again and check if the former page gets enabled as well.
   yield addNewTabPageTab();
   ok(gridNode.hasAttribute("page-disabled"), "page is disabled");
 
   // check that no sites have been rendered
   is(0, cw.document.querySelectorAll(".site").length, "no sites have been rendered");
 
-  cw.gToolbar.show();
+  NewTabUtils.allPages.enabled = true;
   ok(!gridNode.hasAttribute("page-disabled"), "page is not disabled");
   ok(!oldGridNode.hasAttribute("page-disabled"), "old page is not disabled");
 }
--- a/browser/base/content/test/newtab/browser_newtab_reset.js
+++ b/browser/base/content/test/newtab/browser_newtab_reset.js
@@ -1,15 +1,18 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /*
  * These tests make sure that resetting the 'New Tage Page' works as expected.
  */
 function runTests() {
+  // Disabled until bug 716543 is fixed.
+  return;
+
   // create a new tab page and check its modified state after blocking a site
   setLinks("0,1,2,3,4,5,6,7,8");
   setPinnedLinks("");
 
   yield addNewTabPageTab();
   let resetButton = cw.document.getElementById("toolbar-button-reset");
 
   checkGrid("0,1,2,3,4,5,6,7,8");
--- a/browser/base/content/test/newtab/browser_newtab_tabsync.js
+++ b/browser/base/content/test/newtab/browser_newtab_tabsync.js
@@ -3,16 +3,19 @@
 
 /*
  * These tests make sure that all changes that are made to a specific
  * 'New Tab Page' are synchronized with all other open 'New Tab Pages'
  * automatically. All about:newtab pages should always be in the same
  * state.
  */
 function runTests() {
+  // Disabled until bug 716543 is fixed.
+  return;
+
   setLinks("0,1,2,3,4,5,6,7,8,9");
   setPinnedLinks(",1");
 
   yield addNewTabPageTab();
   checkGrid("0,1p,2,3,4,5,6,7,8");
 
   let resetButton = cw.document.getElementById("toolbar-button-reset");
   ok(!resetButton.hasAttribute("modified"), "page is not modified");
--- a/browser/base/content/test/newtab/head.js
+++ b/browser/base/content/test/newtab/head.js
@@ -183,17 +183,17 @@ function checkGrid(aSitesPattern, aSites
     // Check the site's url.
     if (site.url != "about:blank#" + num) {
       valid = false;
       is(site.url, "about:blank#" + num, "cell#" + index + " has the wrong url");
     }
 
     let shouldBePinned = /p$/.test(id);
     let cellContainsPinned = site.isPinned();
-    let cssClassPinned = site.node && site.node.hasAttribute("pinned");
+    let cssClassPinned = site.node && site.node.querySelector(".newtab-control-pin").hasAttribute("pinned");
 
     // Check if the site should be and is pinned.
     if (shouldBePinned) {
       if (!cellContainsPinned) {
         valid = false;
         ok(false, "expected cell#" + index + " to be pinned");
       } else if (!cssClassPinned) {
         valid = false;
@@ -260,8 +260,25 @@ function simulateDrop(aDropTarget, aDrag
   if (aDragSource)
     cw.gDrag.start(aDragSource.site, event);
 
   cw.gDrop.drop(aDropTarget, event, function () executeSoon(TestRunner.next));
 
   if (aDragSource)
     cw.gDrag.end(aDragSource.site);
 }
+
+/**
+ * Resumes testing when all pages have been updated.
+ */
+function whenPagesUpdated() {
+  let page = {
+    update: function () {
+      NewTabUtils.allPages.unregister(this);
+      executeSoon(TestRunner.next);
+    }
+  };
+
+  NewTabUtils.allPages.register(page);
+  registerCleanupFunction(function () {
+    NewTabUtils.allPages.unregister(page);
+  });
+}
--- a/browser/base/content/utilityOverlay.js
+++ b/browser/base/content/utilityOverlay.js
@@ -39,17 +39,33 @@
 #
 # ***** END LICENSE BLOCK *****
 
 // Services = object with smart getters for common XPCOM services
 Components.utils.import("resource://gre/modules/Services.jsm");
 Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
 
 XPCOMUtils.defineLazyGetter(this, "BROWSER_NEW_TAB_URL", function () {
-  return Services.prefs.getCharPref("browser.newtab.url") || "about:blank";
+  const PREF = "browser.newtab.url";
+
+  function getNewTabPageURL() {
+    return Services.prefs.getCharPref(PREF) || "about:blank";
+  }
+
+  function update() {
+    BROWSER_NEW_TAB_URL = getNewTabPageURL();
+  }
+
+  Services.prefs.addObserver(PREF, update, false);
+  addEventListener("unload", function onUnload() {
+    removeEventListener("unload", onUnload);
+    Services.prefs.removeObserver(PREF, update);
+  });
+
+  return getNewTabPageURL();
 });
 
 var TAB_DROP_TYPE = "application/x-moz-tabbrowser-tab";
 
 var gBidiUI = false;
 
 /**
  * Determines whether the given url is considered a special URL for new tabs.
--- a/browser/build.mk
+++ b/browser/build.mk
@@ -93,16 +93,22 @@ distclean::
 	@$(MAKE) -C browser/installer distclean
 
 source-package::
 	@$(MAKE) -C browser/installer source-package
 
 upload::
 	@$(MAKE) -C browser/installer upload
 
+source-upload::
+	@$(MAKE) -C browser/installer source-upload
+
+hg-bundle::
+	@$(MAKE) -C browser/installer hg-bundle
+
 l10n-check::
 	@$(MAKE) -C browser/locales l10n-check
 
 ifdef ENABLE_TESTS
 # Implemented in testing/testsuite-targets.mk
 
 mochitest-browser-chrome:
 	$(RUN_MOCHITEST) --browser-chrome
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -62,16 +62,19 @@ XPCOMUtils.defineLazyGetter(this, "NetUt
 XPCOMUtils.defineLazyGetter(this, "PlacesUtils", function() {
   Cu.import("resource://gre/modules/PlacesUtils.jsm");
   return PlacesUtils;
 });
 
 XPCOMUtils.defineLazyModuleGetter(this, "KeywordURLResetPrompter",
                                   "resource:///modules/KeywordURLResetPrompter.jsm");
 
+XPCOMUtils.defineLazyModuleGetter(this, "webappsUI", 
+                                  "resource://gre/modules/webappsUI.jsm");
+
 const PREF_PLUGINS_NOTIFYUSER = "plugins.update.notifyUser";
 const PREF_PLUGINS_UPDATEURL  = "plugins.update.url";
 
 // We try to backup bookmarks at idle times, to avoid doing that at shutdown.
 // Number of idle seconds before trying to backup bookmarks.  15 minutes.
 const BOOKMARKS_BACKUP_IDLE_TIME = 15 * 60;
 // Minimum interval in milliseconds between backups.
 const BOOKMARKS_BACKUP_INTERVAL = 86400 * 1000;
@@ -341,16 +344,17 @@ BrowserGlue.prototype = {
       this._idleService.removeIdleObserver(this, BOOKMARKS_BACKUP_IDLE_TIME);
     if (this._isPlacesInitObserver)
       os.removeObserver(this, "places-init-complete");
     if (this._isPlacesLockedObserver)
       os.removeObserver(this, "places-database-locked");
     if (this._isPlacesShutdownObserver)
       os.removeObserver(this, "places-shutdown");
     os.removeObserver(this, "defaultURIFixup-using-keyword-pref");
+    webappsUI.uninit();
   },
 
   _onAppDefaults: function BG__onAppDefaults() {
     // apply distribution customizations (prefs)
     // other customizations are applied in _onProfileStartup()
     this._distributionCustomizer.applyPrefDefaults();
   },
 
@@ -365,16 +369,19 @@ BrowserGlue.prototype = {
 
     // apply distribution customizations
     // prefs are applied in _onAppDefaults()
     this._distributionCustomizer.applyCustomizations();
 
     // handle any UI migration
     this._migrateUI();
 
+    // Initialize webapps UI
+    webappsUI.init();
+
     Services.obs.notifyObservers(null, "browser-ui-startup-complete", "");
   },
 
   // the first browser window has finished initializing
   _onFirstWindowLoaded: function BG__onFirstWindowLoaded() {
 #ifdef XP_WIN
     // For windows seven, initialize the jump list module.
     const WINTASKBAR_CONTRACTID = "@mozilla.org/windows-taskbar;1";
@@ -400,17 +407,16 @@ BrowserGlue.prototype = {
       this._showRightsNotification();
 #ifdef MOZ_TELEMETRY_REPORTING
     } else {
       // Only show telemetry notification when about:rights notification is not shown.
       this._showTelemetryNotification();
 #endif
     }
 
-
     // Show update notification, if needed.
     if (Services.prefs.prefHasUserValue("app.update.postupdate"))
       this._showUpdateNotification();
 
     // Load the "more info" page for a locked places.sqlite
     // This property is set earlier by places-database-locked topic.
     if (this._isPlacesDatabaseLocked) {
       this._showPlacesLockedNotificationBox();
--- a/browser/components/places/content/browserPlacesViews.js
+++ b/browser/components/places/content/browserPlacesViews.js
@@ -371,41 +371,36 @@ PlacesViewBase.prototype = {
    * @param aPopup
    *        The livemark container popup
    * @param aStatus
    *        The livemark status
    */
   _setLivemarkStatusMenuItem:
   function PVB_setLivemarkStatusMenuItem(aPopup, aStatus) {
     let statusMenuitem = aPopup._statusMenuitem;
-    let stringId = "";
-    if (aStatus == Ci.mozILivemark.STATUS_LOADING)
-      stringId = "bookmarksLivemarkLoading";
-    else if (aStatus == Ci.mozILivemark.STATUS_FAILED)
-      stringId = "bookmarksLivemarkFailed";
-
-    if (stringId && !statusMenuitem) {
+    if (!statusMenuitem) {
       // Create the status menuitem and cache it in the popup object.
       statusMenuitem = document.createElement("menuitem");
-      statusMenuitem.setAttribute("livemarkStatus", stringId);
       statusMenuitem.className = "livemarkstatus-menuitem";
-      statusMenuitem.setAttribute("label", PlacesUIUtils.getString(stringId));
       statusMenuitem.setAttribute("disabled", true);
-      aPopup.insertBefore(statusMenuitem, aPopup._startMarker.nextSibling);
       aPopup._statusMenuitem = statusMenuitem;
     }
-    else if (stringId &&
-             statusMenuitem.getAttribute("livemarkStatus") != stringId) {
+
+    if (aStatus == Ci.mozILivemark.STATUS_LOADING ||
+        aStatus == Ci.mozILivemark.STATUS_FAILED) {
       // Status has changed, update the cached status menuitem.
+      let stringId = aStatus == Ci.mozILivemark.STATUS_LOADING ?
+                       "bookmarksLivemarkLoading" : "bookmarksLivemarkFailed";
       statusMenuitem.setAttribute("label", PlacesUIUtils.getString(stringId));
+      if (aPopup._startMarker.nextSibling != statusMenuitem)
+        aPopup.insertBefore(statusMenuitem, aPopup._startMarker.nextSibling);
     }
-    else if (!stringId && statusMenuitem) {
+    else {
       // The livemark has finished loading.
       aPopup.removeChild(aPopup._statusMenuitem);
-      aPopup._statusMenuitem = null;
     }
   },
 
   toggleCutNode: function PVB_toggleCutNode(aNode, aValue) {
     let elt = aNode._DOMElement;
     if (elt) {
       // We may get the popup for menus, but we need the menu itself.
       if (elt.localName == "menupopup")
@@ -887,16 +882,24 @@ function PlacesToolbar(aPlace) {
 
   this._viewElt._placesView = this;
 
   this._addEventListeners(this._viewElt, this._cbEvents, false);
   this._addEventListeners(this._rootElt, ["popupshowing", "popuphidden"], true);
   this._addEventListeners(this._rootElt, ["overflow", "underflow"], true);
   this._addEventListeners(window, ["resize", "unload"], false);
 
+  // If personal-bookmarks has been dragged to the tabs toolbar,
+  // we have to track addition and removals of tabs, to properly
+  // recalculate the available space for bookmarks.
+  // TODO (bug 734730): Use a performant mutation listener when available.
+  if (this._viewElt.parentNode.parentNode == document.getElementById("TabsToolbar")) {
+    this._addEventListeners(gBrowser.tabContainer, ["TabOpen", "TabClose"], false);
+  }
+
   PlacesViewBase.call(this, aPlace);
 
   Services.telemetry.getHistogramById("FX_BOOKMARKS_TOOLBAR_INIT_MS")
                     .add(Date.now() - startTime);
 }
 
 PlacesToolbar.prototype = {
   __proto__: PlacesViewBase.prototype,
@@ -913,16 +916,17 @@ PlacesToolbar.prototype = {
   },
 
   uninit: function PT_uninit() {
     this._removeEventListeners(this._viewElt, this._cbEvents, false);
     this._removeEventListeners(this._rootElt, ["popupshowing", "popuphidden"],
                                true);
     this._removeEventListeners(this._rootElt, ["overflow", "underflow"], true);
     this._removeEventListeners(window, ["resize", "unload"], false);
+    this._removeEventListeners(gBrowser.tabContainer, ["TabOpen", "TabClose"], false);
 
     PlacesViewBase.prototype.uninit.apply(this, arguments);
   },
 
   _openedMenuButton: null,
   _allowPopupShowing: true,
 
   _rebuild: function PT__rebuild() {
@@ -1012,19 +1016,20 @@ PlacesToolbar.prototype = {
     if (aBefore)
       this._rootElt.insertBefore(button, aBefore);
     else
       this._rootElt.appendChild(button);
   },
 
   _updateChevronPopupNodesVisibility:
   function PT__updateChevronPopupNodesVisibility() {
-    for (let i = 0; i < this._chevronPopup.childNodes.length; i++) {
-      this._chevronPopup.childNodes[i].hidden =
-        this._rootElt.childNodes[i].style.visibility != "hidden";
+    for (let i = 0, node = this._chevronPopup._startMarker.nextSibling;
+         node != this._chevronPopup._endMarker;
+         i++, node = node.nextSibling) {
+      node.hidden = this._rootElt.childNodes[i].style.visibility != "hidden";
     }
   },
 
   _onChevronPopupShowing:
   function PT__onChevronPopupShowing(aEvent) {
     // Handle popupshowing only for the chevron popup, not for nested ones.
     if (aEvent.target != this._chevronPopup)
       return;
@@ -1066,17 +1071,21 @@ PlacesToolbar.prototype = {
       case "underflow":
         if (aEvent.target != aEvent.currentTarget)
           return;
 
         // Ignore purely vertical underflows.
         if (aEvent.detail == 0)
           return;
 
+        this.updateChevron();
         this._chevron.collapsed = true;
+        break;
+      case "TabOpen":
+      case "TabClose":
         this.updateChevron();
         break;
       case "dragstart":
         this._onDragStart(aEvent);
         break;
       case "dragover":
         this._onDragOver(aEvent);
         break;
--- a/browser/components/thumbnails/PageThumbs.jsm
+++ b/browser/components/thumbnails/PageThumbs.jsm
@@ -13,25 +13,25 @@ const Ci = Components.interfaces;
 const HTML_NAMESPACE = "http://www.w3.org/1999/xhtml";
 
 /**
  * The default width for page thumbnails.
  *
  * Hint: This is the default value because the 'New Tab Page' is the only
  *       client for now.
  */
-const THUMBNAIL_WIDTH = 201;
+const THUMBNAIL_WIDTH = 400;
 
 /**
  * The default height for page thumbnails.
  *
  * Hint: This is the default value because the 'New Tab Page' is the only
  *       client for now.
  */
-const THUMBNAIL_HEIGHT = 127;
+const THUMBNAIL_HEIGHT = 225;
 
 /**
  * The default background color for page thumbnails.
  */
 const THUMBNAIL_BG_COLOR = "#fff";
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
--- a/browser/config/mozconfigs/linux32/debug
+++ b/browser/config/mozconfigs/linux32/debug
@@ -13,8 +13,11 @@ export MOZILLA_OFFICIAL=1
 # Enable parallel compiling
 mk_add_options MOZ_MAKE_FLAGS="-j4"
 
 #Use ccache
 ac_add_options --with-ccache=/usr/bin/ccache
 
 # Treat warnings as errors in directories with FAIL_ON_WARNINGS.
 ac_add_options --enable-warnings-as-errors
+
+# Package js shell.
+export MOZ_PACKAGE_JSSHELL=1
--- a/browser/config/mozconfigs/linux32/nightly
+++ b/browser/config/mozconfigs/linux32/nightly
@@ -22,8 +22,11 @@ mk_add_options PROFILE_GEN_SCRIPT='$(PYT
 # Enable parallel compiling
 mk_add_options MOZ_MAKE_FLAGS="-j4"
 
 #Use ccache
 ac_add_options --with-ccache=/usr/bin/ccache
 
 # Treat warnings as errors in directories with FAIL_ON_WARNINGS.
 ac_add_options --enable-warnings-as-errors
+
+# Package js shell.
+export MOZ_PACKAGE_JSSHELL=1
--- a/browser/config/mozconfigs/linux32/release
+++ b/browser/config/mozconfigs/linux32/release
@@ -16,8 +16,11 @@ 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"
+
+# Package js shell.
+export MOZ_PACKAGE_JSSHELL=1
--- a/browser/config/mozconfigs/linux64/debug
+++ b/browser/config/mozconfigs/linux64/debug
@@ -10,8 +10,11 @@ ac_add_options --enable-stdcxx-compat
 # Needed to enable breakpad in application.ini
 export MOZILLA_OFFICIAL=1
 
 # Enable parallel compiling
 mk_add_options MOZ_MAKE_FLAGS="-j4"
 
 # Treat warnings as errors in directories with FAIL_ON_WARNINGS.
 ac_add_options --enable-warnings-as-errors
+
+# Package js shell.
+export MOZ_PACKAGE_JSSHELL=1
--- a/browser/config/mozconfigs/linux64/nightly
+++ b/browser/config/mozconfigs/linux64/nightly
@@ -22,8 +22,11 @@ mk_add_options PROFILE_GEN_SCRIPT='$(PYT
 # Enable parallel compiling
 mk_add_options MOZ_MAKE_FLAGS="-j4"
 
 #Use ccache
 ac_add_options --with-ccache=/usr/bin/ccache
 
 # Treat warnings as errors in directories with FAIL_ON_WARNINGS.
 ac_add_options --enable-warnings-as-errors
+
+# Package js shell.
+export MOZ_PACKAGE_JSSHELL=1
--- a/browser/config/mozconfigs/linux64/release
+++ b/browser/config/mozconfigs/linux64/release
@@ -16,8 +16,11 @@ 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"
+
+# Package js shell.
+export MOZ_PACKAGE_JSSHELL=1
--- a/browser/config/mozconfigs/macosx-universal/nightly
+++ b/browser/config/mozconfigs/macosx-universal/nightly
@@ -17,8 +17,11 @@ export MOZILLA_OFFICIAL=1
 
 export MOZ_TELEMETRY_REPORTING=1
 mk_add_options MOZ_MAKE_FLAGS="-j4"
 
 ac_add_options --with-macbundlename-prefix=Firefox
 
 # Treat warnings as errors in directories with FAIL_ON_WARNINGS.
 ac_add_options --enable-warnings-as-errors
+
+# Package js shell.
+export MOZ_PACKAGE_JSSHELL=1
--- a/browser/config/mozconfigs/macosx-universal/release
+++ b/browser/config/mozconfigs/macosx-universal/release
@@ -12,8 +12,11 @@ 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"
+
+# Package js shell.
+export MOZ_PACKAGE_JSSHELL=1
--- a/browser/config/mozconfigs/macosx32/debug
+++ b/browser/config/mozconfigs/macosx32/debug
@@ -5,8 +5,11 @@ ac_add_options --enable-signmar
 
 # Enable parallel compiling
 mk_add_options MOZ_MAKE_FLAGS="-j4"
 
 # Needed to enable breakpad in application.ini
 export MOZILLA_OFFICIAL=1
 
 ac_add_options --with-macbundlename-prefix=Firefox
+
+# Package js shell.
+export MOZ_PACKAGE_JSSHELL=1
--- a/browser/config/mozconfigs/macosx64/debug
+++ b/browser/config/mozconfigs/macosx64/debug
@@ -10,8 +10,11 @@ mk_add_options MOZ_MAKE_FLAGS="-j4"
 
 # Needed to enable breakpad in application.ini
 export MOZILLA_OFFICIAL=1
 
 ac_add_options --with-macbundlename-prefix=Firefox
 
 # Treat warnings as errors in directories with FAIL_ON_WARNINGS.
 ac_add_options --enable-warnings-as-errors
+
+# Package js shell.
+export MOZ_PACKAGE_JSSHELL=1
--- a/browser/config/mozconfigs/win32/debug
+++ b/browser/config/mozconfigs/win32/debug
@@ -3,8 +3,11 @@ ac_add_options --enable-trace-malloc
 ac_add_options --enable-signmar
 
 # 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
+
+# Package js shell.
+export MOZ_PACKAGE_JSSHELL=1
--- a/browser/config/mozconfigs/win32/nightly
+++ b/browser/config/mozconfigs/win32/nightly
@@ -12,8 +12,11 @@ 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
+
+# Package js shell.
+export MOZ_PACKAGE_JSSHELL=1
--- a/browser/config/mozconfigs/win32/release
+++ b/browser/config/mozconfigs/win32/release
@@ -8,8 +8,11 @@ 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
+
+# Package js shell.
+export MOZ_PACKAGE_JSSHELL=1
--- a/browser/config/mozconfigs/win64/debug
+++ b/browser/config/mozconfigs/win64/debug
@@ -4,8 +4,13 @@ ac_add_options --host=x86_64-pc-mingw32
 ac_add_options --enable-debug
 ac_add_options --enable-trace-malloc
 ac_add_options --enable-signmar
 
 # Needed to enable breakpad in application.ini
 export MOZILLA_OFFICIAL=1
 
 mk_add_options MOZ_MAKE_FLAGS=-j1
+
+# Package js shell.
+export MOZ_PACKAGE_JSSHELL=1
+
+. $topsrcdir/browser/config/mozconfigs/win64/vs2010-mozconfig
--- a/browser/config/mozconfigs/win64/nightly
+++ b/browser/config/mozconfigs/win64/nightly
@@ -13,8 +13,13 @@ ac_add_options --enable-signmar
 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
+
+# Package js shell.
+export MOZ_PACKAGE_JSSHELL=1
+
+. $topsrcdir/browser/config/mozconfigs/win64/vs2010-mozconfig
new file mode 100644
--- /dev/null
+++ b/browser/config/mozconfigs/win64/vs2010-mozconfig
@@ -0,0 +1,16 @@
+export INCLUDE=/c/tools/msvs10/vc/include:/c/tools/msvs10/vc/atlmfc/include:/c/tools/sdks/v7.0/include:/c/tools/sdks/v7.0/include/atl:/c/tools/sdks/dx10/include
+export LIBPATH=/c/tools/msvs10/vc/lib/amd64:/c/tools/msvs10/vc/atlmfc/lib/amd64
+export LIB=/c/tools/msvs10/vc/lib/amd64:/c/tools/msvs10/vc/atlmfc/lib/amd64:/c/tools/sdks/v7.0/lib/x64:/c/tools/sdks/dx10/lib/x64
+export PATH="/c/tools/msvs10/Common7/IDE:/c/tools/msvs10/VC/BIN/amd64:/c/tools/msvs10/VC/BIN/x86_amd64:/c/tools/msvs10/VC/BIN:/c/tools/msvs10/Common7/Tools:/c/tools/msvs10/VC/VCPackages:${PATH}"
+export WIN32_REDIST_DIR=/c/tools/msvs10/VC/redist/x64/Microsoft.VC100.CRT
+
+# Use 32bit linker for PGO crash bug.
+# https://connect.microsoft.com/VisualStudio/feedback/details/686117/
+export LD=/c/tools/msvs10/VC/BIN/x86_amd64/link.exe
+
+
+mk_add_options "export LIB=$LIB"
+mk_add_options "export LIBPATH=$LIBPATH"
+mk_add_options "export PATH=$PATH"
+mk_add_options "export INCLUDE=$INCLUDE"
+mk_add_options "export WIN32_REDIST_DIR=$WIN32_REDIST_DIR"
--- a/browser/config/version.txt
+++ b/browser/config/version.txt
@@ -1,1 +1,1 @@
-13.0a1
+14.0a1
--- a/browser/devtools/debugger/test/Makefile.in
+++ b/browser/devtools/debugger/test/Makefile.in
@@ -66,33 +66,35 @@ include $(topsrcdir)/config/rules.mk
 	browser_dbg_stack-01.js \
 	browser_dbg_stack-02.js \
 	browser_dbg_stack-03.js \
 	browser_dbg_stack-04.js \
 	browser_dbg_location-changes.js \
 	browser_dbg_script-switching.js \
 	browser_dbg_pause-resume.js \
 	browser_dbg_update-editor-mode.js \
-	browser_dbg_select-line.js \
+	$(warning browser_dbg_select-line.js temporarily disabled due to oranges, see bug 726609) \
 	browser_dbg_clean-exit.js \
 	browser_dbg_bug723069_editor-breakpoints.js \
 	browser_dbg_bug731394_editor-contextmenu.js \
+	browser_dbg_displayName.js \
 	head.js \
 	$(NULL)
 
 _BROWSER_TEST_PAGES = \
 	browser_dbg_tab1.html \
 	browser_dbg_tab2.html \
 	browser_dbg_debuggerstatement.html \
 	browser_dbg_stack.html \
 	browser_dbg_script-switching.html \
 	test-script-switching-01.js \
 	test-script-switching-02.js \
 	browser_dbg_frame-parameters.html \
 	browser_dbg_update-editor-mode.html \
 	test-editor-mode \
+	browser_dbg_displayName.html \
 	$(NULL)
 
 libs:: $(_BROWSER_TEST_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
 
 libs:: $(_BROWSER_TEST_PAGES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
new file mode 100644
--- /dev/null
+++ b/browser/devtools/debugger/test/browser_dbg_displayName.html
@@ -0,0 +1,26 @@
+<!DOCTYPE HTML>
+<html>
+<head><title>Browser Debugger Test Tab</title>
+<!-- Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/ -->
+<script type="text/javascript">
+
+var a = function() {
+  return function() {
+    debugger;
+  }
+}
+
+var anon = a();
+anon.displayName = "anonFunc";
+
+function evalCall() {
+  eval("anon();");
+}
+
+</script>
+</head>
+
+<body></body>
+
+</html>
new file mode 100644
--- /dev/null
+++ b/browser/devtools/debugger/test/browser_dbg_displayName.js
@@ -0,0 +1,53 @@
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+var gPane = null;
+var gTab = null;
+var gDebuggee = null;
+var gDebugger = null;
+
+const TAB_URL = EXAMPLE_URL + "browser_dbg_displayName.html";
+
+function test() {
+  debug_tab_pane(TAB_URL, function(aTab, aDebuggee, aPane) {
+    gTab = aTab;
+    gDebuggee = aDebuggee;
+    gPane = aPane;
+    gDebugger = gPane.debuggerWindow;
+
+    testAnonCall();
+  });
+}
+
+function testAnonCall() {
+  gPane.activeThread.addOneTimeListener("framesadded", function() {
+    Services.tm.currentThread.dispatch({ run: function() {
+
+      let frames = gDebugger.DebuggerView.Stackframes._frames;
+
+      is(gDebugger.StackFrames.activeThread.state, "paused",
+        "Should only be getting stack frames while paused.");
+
+      is(frames.querySelectorAll(".dbg-stackframe").length, 3,
+        "Should have three frames.");
+
+      is(frames.querySelector("#stackframe-0 .dbg-stackframe-name").textContent,
+        "anonFunc", "Frame name should be anonFunc");
+
+      resumeAndFinish();
+    }}, 0);
+  });
+
+  gDebuggee.evalCall();
+}
+
+function resumeAndFinish() {
+  gDebugger.StackFrames.activeThread.resume(function() {
+    removeTab(gTab);
+    gPane = null;
+    gDebuggee = null;
+    finish();
+  });
+}
--- a/browser/devtools/highlighter/TreePanel.jsm
+++ b/browser/devtools/highlighter/TreePanel.jsm
@@ -304,92 +304,107 @@ TreePanel.prototype = {
           this.IUI.highlighter.highlight(node);
         }
       }
     }
   },
 
   /**
    * Handle double-click events in the html tree panel.
-   * (double-clicking an attribute value allows it to be edited)
+   * Double-clicking an attribute name or value allows it to be edited.
    * @param aEvent
    *        The mouse event.
    */
   onTreeDblClick: function TP_onTreeDblClick(aEvent)
   {
     // if already editing an attribute value, double-clicking elsewhere
     // in the tree is the same as a click, which dismisses the editor
     if (this.editingContext)
       this.closeEditor();
 
     let target = aEvent.target;
 
+    if (!this.hasClass(target, "editable")) {
+      return;
+    }
+
+    let repObj = this.getRepObject(target);
+
     if (this.hasClass(target, "nodeValue")) {
-      let repObj = this.getRepObject(target);
       let attrName = target.getAttribute("data-attributeName");
       let attrVal = target.innerHTML;
 
-      this.editAttributeValue(target, repObj, attrName, attrVal);
+      this.editAttribute(target, repObj, attrName, attrVal);
+    }
+
+    if (this.hasClass(target, "nodeName")) {
+      let attrName = target.innerHTML;
+      let attrValNode = target.nextSibling.nextSibling; // skip 2 (=)
+
+      if (attrValNode)
+        this.editAttribute(target, repObj, attrName, attrValNode.innerHTML);
     }
   },
 
   /**
-   * Starts the editor for an attribute value.
+   * Starts the editor for an attribute name or value.
    * @param aAttrObj
-   *        The DOM object representing the attribute value in the HTML Tree
+   *        The DOM object representing the attribute name or value in the HTML
+   *        Tree.
    * @param aRepObj
    *        The original DOM (target) object being inspected/edited
    * @param aAttrName
    *        The name of the attribute being edited
    * @param aAttrVal
    *        The current value of the attribute being edited
    */
-  editAttributeValue:
-  function TP_editAttributeValue(aAttrObj, aRepObj, aAttrName, aAttrVal)
+  editAttribute:
+  function TP_editAttribute(aAttrObj, aRepObj, aAttrName, aAttrVal)
   {
     let editor = this.treeBrowserDocument.getElementById("attribute-editor");
     let editorInput =
       this.treeBrowserDocument.getElementById("attribute-editor-input");
     let attrDims = aAttrObj.getBoundingClientRect();
     // figure out actual viewable viewport dimensions (sans scrollbars)
     let viewportWidth = this.treeBrowserDocument.documentElement.clientWidth;
     let viewportHeight = this.treeBrowserDocument.documentElement.clientHeight;
 
     // saves the editing context for use when the editor is saved/closed
     this.editingContext = {
       attrObj: aAttrObj,
       repObj: aRepObj,
-      attrName: aAttrName
+      attrName: aAttrName,
+      attrValue: aAttrVal
     };
 
     // highlight attribute-value node in tree while editing
     this.addClass(aAttrObj, "editingAttributeValue");
 
     // show the editor
     this.addClass(editor, "editing");
 
     // offset the editor below the attribute-value node being edited
-    let editorVeritcalOffset = 2;
+    let editorVerticalOffset = 2;
 
     // keep the editor comfortably within the bounds of the viewport
     let editorViewportBoundary = 5;
 
     // outer editor is sized based on the <input> box inside it
     editorInput.style.width = Math.min(attrDims.width, viewportWidth -
                                 editorViewportBoundary) + "px";
     editorInput.style.height = Math.min(attrDims.height, viewportHeight -
                                 editorViewportBoundary) + "px";
     let editorDims = editor.getBoundingClientRect();
 
     // calculate position for the editor according to the attribute node
     let editorLeft = attrDims.left + this.treeIFrame.contentWindow.scrollX -
                     // center the editor against the attribute value
                     ((editorDims.width - attrDims.width) / 2);
     let editorTop = attrDims.top + this.treeIFrame.contentWindow.scrollY +
-                    attrDims.height + editorVeritcalOffset;
+                    attrDims.height + editorVerticalOffset;
 
     // but, make sure the editor stays within the visible viewport
     editorLeft = Math.max(0, Math.min(
                                       (this.treeIFrame.contentWindow.scrollX +
                                           viewportWidth - editorDims.width),
                                       editorLeft)
                           );
     editorTop = Math.max(0, Math.min(
@@ -398,18 +413,23 @@ TreePanel.prototype = {
                                       editorTop)
                           );
 
     // position the editor
     editor.style.left = editorLeft + "px";
     editor.style.top = editorTop + "px";
 
     // set and select the text
-    editorInput.value = aAttrVal;
-    editorInput.select();
+    if (this.hasClass(aAttrObj, "nodeValue")) {
+      editorInput.value = aAttrVal;
+      editorInput.select();
+    } else {
+      editorInput.value = aAttrName;
+      editorInput.select();
+    }
 
     // listen for editor specific events
     this.bindEditorEvent(editor, "click", function(aEvent) {
       aEvent.stopPropagation();
     });
     this.bindEditorEvent(editor, "dblclick", function(aEvent) {
       aEvent.stopPropagation();
     });
@@ -505,25 +525,42 @@ TreePanel.prototype = {
 
   /**
    * Commit the edits made in the editor, then close it.
    */
   saveEditor: function TP_saveEditor()
   {
     let editorInput =
       this.treeBrowserDocument.getElementById("attribute-editor-input");
+    let dirty = false;
 
-    // set the new attribute value on the original target DOM element
-    this.editingContext.repObj.setAttribute(this.editingContext.attrName,
-                                              editorInput.value);
+    if (this.hasClass(this.editingContext.attrObj, "nodeValue")) {
+      // set the new attribute value on the original target DOM element
+      this.editingContext.repObj.setAttribute(this.editingContext.attrName,
+                                                editorInput.value);
+
+      // update the HTML tree attribute value
+      this.editingContext.attrObj.innerHTML = editorInput.value;
+      dirty = true;
+    }
 
-    // update the HTML tree attribute value
-    this.editingContext.attrObj.innerHTML = editorInput.value;
+    if (this.hasClass(this.editingContext.attrObj, "nodeName")) {
+      // remove the original attribute from the original target DOM element
+      this.editingContext.repObj.removeAttribute(this.editingContext.attrName);
 
-    this.IUI.isDirty = true;
+      // set the new attribute value on the original target DOM element
+      this.editingContext.repObj.setAttribute(editorInput.value,
+                                              this.editingContext.attrValue);
+
+      // update the HTML tree attribute value
+      this.editingContext.attrObj.innerHTML = editorInput.value;
+      dirty = true;
+    }
+
+    this.IUI.isDirty = dirty;
     this.IUI.nodeChanged(this.registrationObject);
 
     // event notification
     Services.obs.notifyObservers(null, this.IUI.INSPECTOR_NOTIFICATIONS.EDITOR_SAVED,
                                   null);
 
     this.closeEditor();
   },
--- a/browser/devtools/highlighter/inspector.jsm
+++ b/browser/devtools/highlighter/inspector.jsm
@@ -22,16 +22,17 @@
  *
  * Contributor(s):
  *   Rob Campbell <rcampbell@mozilla.com> (original author)
  *   Mihai Șucan <mihai.sucan@gmail.com>
  *   Julian Viereck <jviereck@mozilla.com>
  *   Paul Rouget <paul@mozilla.com>
  *   Kyle Simpson <ksimpson@mozilla.com>
  *   Johan Charlez <johan.charlez@gmail.com>
+ *   Mike Ratcliffe <mratcliffe@mozilla.com>
  *
  * Alternatively, the contents of this file may be used under the terms of
  * either the GNU General Public License Version 2 or later (the "GPL"), or
  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  * in which case the provisions of the GPL or the LGPL are applicable instead
  * of those above. If you wish to allow use of your version of this file only
  * under the terms of either the GPL or the LGPL, and not to allow others to
  * use your version of this file under the terms of the MPL, indicate your
@@ -99,16 +100,25 @@ function InspectorUI(aWindow)
 {
   this.chromeWin = aWindow;
   this.chromeDoc = aWindow.document;
   this.tabbrowser = aWindow.gBrowser;
   this.tools = {};
   this.toolEvents = {};
   this.store = new InspectorStore();
   this.INSPECTOR_NOTIFICATIONS = INSPECTOR_NOTIFICATIONS;
+
+  // Set the tooltip of the inspect button.
+  let keysbundle = Services.strings.createBundle(
+    "chrome://global/locale/keys.properties");
+  let returnString = keysbundle.GetStringFromName("VK_RETURN");
+  let tooltip = this.strings.formatStringFromName("inspectButton.tooltiptext",
+    [returnString], 1);
+  let button = this.chromeDoc.getElementById("inspector-inspect-toolbutton");
+  button.setAttribute("tooltiptext", tooltip);
 }
 
 InspectorUI.prototype = {
   browser: null,
   tools: null,
   toolEvents: null,
   inspecting: false,
   ruleViewEnabled: true,
@@ -840,30 +850,26 @@ InspectorUI.prototype = {
   },
 
   /**
    * Copy the innerHTML of the selected Node to the clipboard. Called via the
    * Inspector:CopyInner command.
    */
   copyInnerHTML: function IUI_copyInnerHTML()
   {
-    let clipboard = Cc["@mozilla.org/widget/clipboardhelper;1"].
-                    getService(Ci.nsIClipboardHelper);
-    clipboard.copyString(this.selection.innerHTML);
+    clipboardHelper.copyString(this.selection.innerHTML);
   },
 
   /**
    * Copy the outerHTML of the selected Node to the clipboard. Called via the
    * Inspector:CopyOuter command.
    */
   copyOuterHTML: function IUI_copyOuterHTML()
   {
-    let clipboard = Cc["@mozilla.org/widget/clipboardhelper;1"].
-                    getService(Ci.nsIClipboardHelper);
-    clipboard.copyString(this.selection.outerHTML);
+    clipboardHelper.copyString(this.selection.outerHTML);
   },
 
   /**
    * Delete the selected node. Called via the Inspector:DeleteNode command.
    */
   deleteNode: function IUI_deleteNode()
   {
     let selection = this.selection;
@@ -921,22 +927,44 @@ InspectorUI.prototype = {
       let ruleViewStore = this.store.getValue(winID, "ruleView");
       if (!ruleViewStore) {
         ruleViewStore = {};
         this.store.setValue(winID, "ruleView", ruleViewStore);
       }
 
       this.ruleView = new CssRuleView(doc, ruleViewStore);
 
+      // Add event handlers bound to this.
       this.boundRuleViewChanged = this.ruleViewChanged.bind(this);
       this.ruleView.element.addEventListener("CssRuleViewChanged",
                                              this.boundRuleViewChanged);
       this.cssRuleViewBoundCSSLinkClicked = this.ruleViewCSSLinkClicked.bind(this);
       this.ruleView.element.addEventListener("CssRuleViewCSSLinkClicked",
                                              this.cssRuleViewBoundCSSLinkClicked);
+      this.cssRuleViewBoundMouseDown = this.ruleViewMouseDown.bind(this);
+      this.ruleView.element.addEventListener("mousedown",
+                                             this.cssRuleViewBoundMouseDown);
+      this.cssRuleViewBoundMouseUp = this.ruleViewMouseUp.bind(this);
+      this.ruleView.element.addEventListener("mouseup",
+                                             this.cssRuleViewBoundMouseUp);
+      this.cssRuleViewBoundMouseMove = this.ruleViewMouseMove.bind(this);
+      this.cssRuleViewBoundMenuUpdate = this.ruleViewMenuUpdate.bind(this);
+
+      this.cssRuleViewBoundCopy = this.ruleViewCopy.bind(this);
+      iframe.addEventListener("copy", this.cssRuleViewBoundCopy);
+
+      this.cssRuleViewBoundCopyRule = this.ruleViewCopyRule.bind(this);
+      this.cssRuleViewBoundCopyDeclaration =
+        this.ruleViewCopyDeclaration.bind(this);
+      this.cssRuleViewBoundCopyProperty = this.ruleViewCopyProperty.bind(this);
+      this.cssRuleViewBoundCopyPropertyValue =
+        this.ruleViewCopyPropertyValue.bind(this);
+
+      // Add the rule view's context menu.
+      this.ruleViewAddContextMenu();
 
       doc.documentElement.appendChild(this.ruleView.element);
       this.ruleView.highlight(this.selection);
       Services.obs.notifyObservers(null,
         INSPECTOR_NOTIFICATIONS.RULEVIEWREADY, null);
     }.bind(this);
 
     iframe.addEventListener("load", boundLoadListener, true);
@@ -998,28 +1026,365 @@ InspectorUI.prototype = {
       this.chromeWin.StyleEditor.openChrome(styleSheet, rule.ruleLine);
     } else {
       let href = rule.elementStyle.element.ownerDocument.location.href;
       this.chromeWin.openUILinkIn("view-source:" + href, "window");
     }
   },
 
   /**
+   * This is the mousedown handler for the rule view. We use it to track whether
+   * text is currently getting selected.
+   * .
+   * @param aEvent The event object
+   */
+  ruleViewMouseDown: function IUI_ruleViewMouseDown(aEvent)
+  {
+    this.ruleView.element.addEventListener("mousemove",
+      this.cssRuleViewBoundMouseMove);
+  },
+
+  /**
+   * This is the mouseup handler for the rule view. We use it to track whether
+   * text is currently getting selected.
+   * .
+   * @param aEvent The event object
+   */
+  ruleViewMouseUp: function IUI_ruleViewMouseUp(aEvent)
+  {
+    this.ruleView.element.removeEventListener("mousemove",
+      this.cssRuleViewBoundMouseMove);
+    this.ruleView._selectionMode = false;
+  },
+
+  /**
+   * This is the mousemove handler for the rule view. We use it to track whether
+   * text is currently getting selected.
+   * .
+   * @param aEvent The event object
+   */
+  ruleViewMouseMove: function IUI_ruleViewMouseMove(aEvent)
+  {
+    this.ruleView._selectionMode = true;
+  },
+
+  /**
+   * Add a context menu to the rule view.
+   */
+  ruleViewAddContextMenu: function IUI_ruleViewAddContextMenu()
+  {
+    let iframe = this.getToolIframe(this.ruleViewObject);
+    let popupSet = this.chromeDoc.getElementById("mainPopupSet");
+    let menu = this.chromeDoc.createElement("menupopup");
+    menu.addEventListener("popupshowing", this.cssRuleViewBoundMenuUpdate);
+    menu.id = "rule-view-context-menu";
+
+    // Copy selection
+    let label = styleInspectorStrings
+      .GetStringFromName("rule.contextmenu.copyselection");
+    let accessKey = styleInspectorStrings
+      .GetStringFromName("rule.contextmenu.copyselection.accesskey");
+    let item = this.chromeDoc.createElement("menuitem");
+    item.id = "rule-view-copy";
+    item.setAttribute("label", label);
+    item.setAttribute("accesskey", accessKey);
+    item.addEventListener("command", this.cssRuleViewBoundCopy);
+    menu.appendChild(item);
+
+    // Copy rule
+    label = styleInspectorStrings.
+      GetStringFromName("rule.contextmenu.copyrule");
+    accessKey = styleInspectorStrings.
+      GetStringFromName("rule.contextmenu.copyrule.accesskey");
+    item = this.chromeDoc.createElement("menuitem");
+    item.id = "rule-view-copy-rule";
+    item.setAttribute("label", label);
+    item.setAttribute("accesskey", accessKey);
+    item.addEventListener("command", this.cssRuleViewBoundCopyRule);
+    menu.appendChild(item);
+
+    // Copy declaration
+    label = styleInspectorStrings.
+      GetStringFromName("rule.contextmenu.copydeclaration");
+    accessKey = styleInspectorStrings.
+      GetStringFromName("rule.contextmenu.copydeclaration.accesskey");
+    item = this.chromeDoc.createElement("menuitem");
+    item.id = "rule-view-copy-declaration";
+    item.setAttribute("label", label);
+    item.setAttribute("accesskey", accessKey);
+    item.addEventListener("command", this.cssRuleViewBoundCopyDeclaration);
+    menu.appendChild(item);
+
+    // Copy property name
+    label = styleInspectorStrings.
+      GetStringFromName("rule.contextmenu.copyproperty");
+    accessKey = styleInspectorStrings.
+      GetStringFromName("rule.contextmenu.copyproperty.accesskey");
+    item = this.chromeDoc.createElement("menuitem");
+    item.id = "rule-view-copy-property";
+    item.setAttribute("label", label);
+    item.setAttribute("accesskey", accessKey);
+    item.addEventListener("command", this.cssRuleViewBoundCopyProperty);
+    menu.appendChild(item);
+
+    // Copy property value
+    label = styleInspectorStrings.
+      GetStringFromName("rule.contextmenu.copypropertyvalue");
+    accessKey = styleInspectorStrings.
+      GetStringFromName("rule.contextmenu.copypropertyvalue.accesskey");
+    item = this.chromeDoc.createElement("menuitem");
+    item.id = "rule-view-copy-property-value";
+    item.setAttribute("label", label);
+    item.setAttribute("accesskey", accessKey);
+    item.addEventListener("command", this.cssRuleViewBoundCopyPropertyValue);
+    menu.appendChild(item);
+
+    popupSet.appendChild(menu);
+
+    iframe.setAttribute("context", menu.id);
+  },
+
+  /**
+   * Update the rule view's context menu by disabling irrelevant menuitems and
+   * enabling relevant ones.
+   *
+   * @param aEvent The event object
+   */
+  ruleViewMenuUpdate: function IUI_ruleViewMenuUpdate(aEvent)
+  {
+    let iframe = this.getToolIframe(this.ruleViewObject);
+    let win = iframe.contentWindow;
+
+    // Copy selection.
+    let disable = win.getSelection().isCollapsed;
+    let menuitem = this.chromeDoc.getElementById("rule-view-copy");
+    menuitem.disabled = disable;
+
+    // Copy property, copy property name & copy property value.
+    let node = this.chromeDoc.popupNode;
+    if (!node.classList.contains("ruleview-property") &&
+        !node.classList.contains("ruleview-computed")) {
+      while (node = node.parentElement) {
+        if (node.classList.contains("ruleview-property") ||
+          node.classList.contains("ruleview-computed")) {
+          break;
+        }
+      }
+    }
+    let disablePropertyItems = !node || (node &&
+      !node.classList.contains("ruleview-property") &&
+      !node.classList.contains("ruleview-computed"));
+
+    menuitem = this.chromeDoc.querySelector("#rule-view-copy-declaration");
+    menuitem.disabled = disablePropertyItems;
+    menuitem = this.chromeDoc.querySelector("#rule-view-copy-property");
+    menuitem.disabled = disablePropertyItems;
+    menuitem = this.chromeDoc.querySelector("#rule-view-copy-property-value");
+    menuitem.disabled = disablePropertyItems;
+  },
+
+  /**
+   * Copy selected text from the rule view.
+   *
+   * @param aEvent The event object
+   */
+  ruleViewCopy: function IUI_ruleViewCopy(aEvent)
+  {
+    let iframe = this.getToolIframe(this.ruleViewObject);
+    let win = iframe.contentWindow;
+    let text = win.getSelection().toString();
+
+    // Remove any double newlines.
+    text = text.replace(/(\r?\n)\r?\n/g, "$1");
+
+    // Remove "inline"
+    let inline = styleInspectorStrings.GetStringFromName("rule.sourceInline");
+    let rx = new RegExp("^" + inline + "\\r?\\n?", "g");
+    text = text.replace(rx, "");
+
+    // Remove file:line
+    text = text.replace(/[\w\.]+:\d+(\r?\n)/g, "$1");
+
+    // Remove inherited from: line
+    let inheritedFrom = styleInspectorStrings
+      .GetStringFromName("rule.inheritedSource");
+    inheritedFrom = inheritedFrom.replace(/\s%S\s\(%S\)/g, "");
+    rx = new RegExp("(\r?\n)" + inheritedFrom + ".*", "g");
+    text = text.replace(rx, "$1");
+
+    clipboardHelper.copyString(text);
+
+    if (aEvent) {
+      aEvent.preventDefault();
+    }
+  },
+
+  /**
+   * Copy a rule from the rule view.
+   *
+   * @param aEvent The event object
+   */
+  ruleViewCopyRule: function IUI_ruleViewCopyRule(aEvent)
+  {
+    let node = this.chromeDoc.popupNode;
+    if (node.className != "ruleview-code") {
+      if (node.className == "ruleview-rule-source") {
+        node = node.nextElementSibling;
+      } else {
+        while (node = node.parentElement) {
+          if (node.className == "ruleview-code") {
+            break;
+          }
+        }
+      }
+    }
+
+    if (node.className == "ruleview-code") {
+      // We need to strip expanded properties from the node because we use
+      // node.textContent below, which also gets text from hidden nodes. The
+      // simplest way to do this is to clone the node and remove them from the
+      // clone.
+      node = node.cloneNode();
+      let computed = node.querySelector(".ruleview-computedlist");
+      if (computed) {
+        computed.parentNode.removeChild(computed);
+      }
+    }
+
+    let text = node.textContent;
+
+    // Format the rule
+    if (osString == "WINNT") {
+      text = text.replace(/{/g, "{\r\n    ");
+      text = text.replace(/;/g, ";\r\n    ");
+      text = text.replace(/\s*}/g, "\r\n}");
+    } else {
+      text = text.replace(/{/g, "{\n    ");
+      text = text.replace(/;/g, ";\n    ");
+      text = text.replace(/\s*}/g, "\n}");
+    }
+
+    clipboardHelper.copyString(text);
+  },
+
+  /**
+   * Copy a declaration from the rule view.
+   *
+   * @param aEvent The event object
+   */
+  ruleViewCopyDeclaration: function IUI_ruleViewCopyDeclaration(aEvent)
+  {
+    let node = this.chromeDoc.popupNode;
+    if (!node.classList.contains("ruleview-property") &&
+        !node.classList.contains("ruleview-computed")) {
+      while (node = node.parentElement) {
+        if (node.classList.contains("ruleview-property") ||
+            node.classList.contains("ruleview-computed")) {
+          break;
+        }
+      }
+    }
+
+    // We need to strip expanded properties from the node because we use
+    // node.textContent below, which also gets text from hidden nodes. The
+    // simplest way to do this is to clone the node and remove them from the
+    // clone.
+    node = node.cloneNode();
+    let computed = node.querySelector(".ruleview-computedlist");
+    if (computed) {
+      computed.parentNode.removeChild(computed);
+    }
+    clipboardHelper.copyString(node.textContent);
+  },
+
+  /**
+   * Copy a property name from the rule view.
+   *
+   * @param aEvent The event object
+   */
+  ruleViewCopyProperty: function IUI_ruleViewCopyProperty(aEvent)
+  {
+    let node = this.chromeDoc.popupNode;
+
+    if (!node.classList.contains("ruleview-propertyname")) {
+      node = node.querySelector(".ruleview-propertyname");
+    }
+
+    if (node) {
+      clipboardHelper.copyString(node.textContent);
+    }
+  },
+
+  /**
+   * Copy a property value from the rule view.
+   *
+   * @param aEvent The event object
+   */
+  ruleViewCopyPropertyValue: function IUI_ruleViewCopyPropertyValue(aEvent)
+  {
+    let node = this.chromeDoc.popupNode;
+
+    if (!node.classList.contains("ruleview-propertyvalue")) {
+      node = node.querySelector(".ruleview-propertyvalue");
+    }
+
+    if (node) {
+      clipboardHelper.copyString(node.textContent);
+    }
+  },
+
+  /**
    * Destroy the rule view.
    */
   destroyRuleView: function IUI_destroyRuleView()
   {
     let iframe = this.getToolIframe(this.ruleViewObject);
+    iframe.removeEventListener("copy", this.cssRuleViewBoundCopy);
     iframe.parentNode.removeChild(iframe);
 
     if (this.ruleView) {
+      let menu = this.chromeDoc.querySelector("#rule-view-context-menu");
+      if (menu) {
+        // Copy
+        let menuitem = this.chromeDoc.querySelector("#rule-view-copy");
+        menuitem.removeEventListener("command", this.cssRuleViewBoundCopy);
+
+        // Copy rule
+        menuitem = this.chromeDoc.querySelector("#rule-view-copy-rule");
+        menuitem.removeEventListener("command", this.cssRuleViewBoundCopyRule);
+
+        // Copy property
+        menuitem = this.chromeDoc.querySelector("#rule-view-copy-declaration");
+        menuitem.removeEventListener("command",
+                                     this.cssRuleViewBoundCopyDeclaration);
+
+        // Copy property name
+        menuitem = this.chromeDoc.querySelector("#rule-view-copy-property");
+        menuitem.removeEventListener("command",
+                                     this.cssRuleViewBoundCopyProperty);
+
+        // Copy property value
+        menuitem = this.chromeDoc.querySelector("#rule-view-copy-property-value");
+        menuitem.removeEventListener("command",
+                                     this.cssRuleViewBoundCopyPropertyValue);
+
+        menu.removeEventListener("popupshowing", this.cssRuleViewBoundMenuUpdate);
+        menu.parentNode.removeChild(menu);
+      }
+
       this.ruleView.element.removeEventListener("CssRuleViewChanged",
                                                 this.boundRuleViewChanged);
       this.ruleView.element.removeEventListener("CssRuleViewCSSLinkClicked",
                                                 this.cssRuleViewBoundCSSLinkClicked);
+      this.ruleView.element.removeEventListener("mousedown",
+                                                this.cssRuleViewBoundMouseDown);
+      this.ruleView.element.removeEventListener("mouseup",
+                                                this.cssRuleViewBoundMouseUp);
+      this.ruleView.element.removeEventListener("mousemove",
+                                                this.cssRuleViewBoundMouseMove);
       delete boundRuleViewChanged;
       this.ruleView.clear();
       delete this.ruleView;
     }
   },
 
   /////////////////////////////////////////////////////////////////////////
   //// Utility Methods
@@ -1231,16 +1596,17 @@ InspectorUI.prototype = {
     btn.setAttribute("group", "sidebar-tools");
     this.sidebarToolbar.appendChild(btn);
 
     // create tool iframe
     let iframe = this.chromeDoc.createElement("iframe");
     iframe.id = "devtools-sidebar-iframe-" + aRegObj.id;
     iframe.setAttribute("flex", "1");
     iframe.setAttribute("tooltip", "aHTMLTooltip");
+    iframe.addEventListener("mousedown", iframe.focus);
     this.sidebarDeck.appendChild(iframe);
 
     // wire up button to show the iframe
     this.bindToolEvent(btn, "click", function showIframe() {
       this.toolShow(aRegObj);
     }.bind(this));
   },
 
@@ -1342,16 +1708,20 @@ InspectorUI.prototype = {
    */
   unregisterSidebarTool: function IUI_unregisterSidebarTool(aRegObj)
   {
     // unbind tool button click event
     let buttonId = this.getToolbarButtonId(aRegObj.id);
     let btn = this.chromeDoc.getElementById(buttonId);
     this.unbindToolEvent(btn, "click");
 
+    // Remove focus listener
+    let iframe = this.getToolIframe(aRegObj);
+    iframe.removeEventListener("mousedown", iframe.focus);
+
     // remove sidebar buttons and tools
     this.sidebarToolbar.removeChild(btn);
 
     // call unregister callback and remove from collection, this also removes
     // the iframe.
     if (aRegObj.unregister)
       aRegObj.unregister.call(aRegObj.context);
 
@@ -2227,8 +2597,22 @@ XPCOMUtils.defineLazyGetter(this, "Style
   var obj = {};
   Cu.import("resource:///modules/devtools/StyleInspector.jsm", obj);
   return obj.StyleInspector;
 });
 
 XPCOMUtils.defineLazyGetter(this, "DOMUtils", function () {
   return Cc["@mozilla.org/inspector/dom-utils;1"].getService(Ci.inIDOMUtils);
 });
+
+XPCOMUtils.defineLazyGetter(this, "clipboardHelper", function() {
+  return Cc["@mozilla.org/widget/clipboardhelper;1"].
+    getService(Ci.nsIClipboardHelper);
+});
+
+XPCOMUtils.defineLazyGetter(this, "styleInspectorStrings", function() {
+  return Services.strings.createBundle(
+    "chrome://browser/locale/devtools/styleinspector.properties");
+});
+
+XPCOMUtils.defineLazyGetter(this, "osString", function() {
+  return Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime).OS;
+});
--- a/browser/devtools/highlighter/test/Makefile.in
+++ b/browser/devtools/highlighter/test/Makefile.in
@@ -53,16 +53,17 @@ include $(topsrcdir)/config/rules.mk
 		browser_inspector_tab_switch.js \
 		browser_inspector_treePanel_output.js \
 		browser_inspector_treePanel_input.html \
 		browser_inspector_treePanel_result.html \
 		browser_inspector_registertools.js \
 		browser_inspector_bug_665880.js \
 		browser_inspector_bug_674871.js \
 		browser_inspector_editor.js \
+		browser_inspector_editor_name.js \
 		browser_inspector_bug_566084_location_changed.js \
 		browser_inspector_infobar.js \
 		browser_inspector_bug_690361.js \
 		browser_inspector_bug_672902_keyboard_shortcuts.js \
 		browser_inspector_keybindings.js \
 		browser_inspector_breadcrumbs.html \
 		browser_inspector_breadcrumbs.js \
 		browser_inspector_bug_699308_iframe_navigation.js \
--- a/browser/devtools/highlighter/test/browser_inspector_editor.js
+++ b/browser/devtools/highlighter/test/browser_inspector_editor.js
@@ -1,20 +1,15 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=2 et sw=2 tw=80: */
 /* ***** BEGIN LICENSE BLOCK *****
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
- *
- * Contributor(s):
- *   Rob Campbell <rcampbell@mozilla.com>
- *   Mihai Sucan <mihai.sucan@gmail.com>
- *   Kyle Simpson <ksimpson@mozilla.com>
- *
- * ***** END LICENSE BLOCK ***** */
+ * ***** END LICENSE BLOCK *****
+ */
 
 let doc;
 let div;
 let editorTestSteps;
 
 function doNextStep() {
   editorTestSteps.next();
 }
new file mode 100644
--- /dev/null
+++ b/browser/devtools/highlighter/test/browser_inspector_editor_name.js
@@ -0,0 +1,253 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* ***** BEGIN LICENSE BLOCK *****
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ * ***** END LICENSE BLOCK *****
+ */
+
+let doc;
+let div;
+let editorTestSteps;
+
+function doNextStep() {
+  editorTestSteps.next();
+}
+
+function setupEditorTests()
+{
+  div = doc.createElement("div");
+  div.setAttribute("id", "foobar");
+  div.setAttribute("class", "barbaz");
+  doc.body.appendChild(div);
+
+  Services.obs.addObserver(setupHTMLPanel, InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false);
+  InspectorUI.toggleInspectorUI();
+}
+
+function setupHTMLPanel()
+{
+  Services.obs.removeObserver(setupHTMLPanel, InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED);
+  Services.obs.addObserver(runEditorTests, InspectorUI.INSPECTOR_NOTIFICATIONS.TREEPANELREADY, false);
+  InspectorUI.toggleHTMLPanel();
+}
+
+function runEditorTests()
+{
+  Services.obs.removeObserver(runEditorTests, InspectorUI.INSPECTOR_NOTIFICATIONS.TREEPANELREADY);
+  InspectorUI.stopInspecting();
+  InspectorUI.inspectNode(doc.body, true);
+
+  // setup generator for async test steps
+  editorTestSteps = doEditorTestSteps();
+
+  // add step listeners
+  Services.obs.addObserver(doNextStep, InspectorUI.INSPECTOR_NOTIFICATIONS.EDITOR_OPENED, false);
+  Services.obs.addObserver(doNextStep, InspectorUI.INSPECTOR_NOTIFICATIONS.EDITOR_CLOSED, false);
+  Services.obs.addObserver(doNextStep, InspectorUI.INSPECTOR_NOTIFICATIONS.EDITOR_SAVED, false);
+
+  // start the tests
+  doNextStep();
+}
+
+function highlighterTrap()
+{
+  // bug 696107
+  InspectorUI.highlighter.removeListener("nodeselected", highlighterTrap);
+  ok(false, "Highlighter moved. Shouldn't be here!");
+  finishUp();
+}
+
+function doEditorTestSteps()
+{
+  let treePanel = InspectorUI.treePanel;
+  let editor = treePanel.treeBrowserDocument.getElementById("attribute-editor");
+  let editorInput = treePanel.treeBrowserDocument.getElementById("attribute-editor-input");
+
+  // Step 1: grab and test the attribute-name nodes in the HTML panel, then open editor
+  let nodes = treePanel.treeBrowserDocument.querySelectorAll(".nodeName.editable");
+  let attrNameNode_id = nodes[0]
+  let attrNameNode_class = nodes[1];
+
+  is(attrNameNode_id.innerHTML, "id", "Step 1: we have the correct `id` attribute-name node in the HTML panel");
+  is(attrNameNode_class.innerHTML, "class", "we have the correct `class` attribute-name node in the HTML panel");
+
+  // double-click the `id` attribute-name node to open the editor
+  executeSoon(function() {
+    // firing 2 clicks right in a row to simulate a double-click
+    EventUtils.synthesizeMouse(attrNameNode_id, 2, 2, {clickCount: 2}, attrNameNode_id.ownerDocument.defaultView);
+  });
+
+  yield; // End of Step 1
+
+
+  // Step 2: validate editing session, enter new attribute value into editor, and save input
+  ok(InspectorUI.treePanel.editingContext, "Step 2: editor session started");
+  let selection = InspectorUI.selection;
+
+  ok(selection, "Selection is: " + selection);
+
+  let editorVisible = editor.classList.contains("editing");
+  ok(editorVisible, "editor popup visible");
+
+  // check if the editor popup is "near" the correct position
+  let editorDims = editor.getBoundingClientRect();
+  let attrNameNodeDims = attrNameNode_id.getBoundingClientRect();
+  let editorPositionOK = (editorDims.left >= (attrNameNodeDims.left - editorDims.width - 5)) &&
+                          (editorDims.right <= (attrNameNodeDims.right + editorDims.width + 5)) &&
+                          (editorDims.top >= (attrNameNodeDims.top - editorDims.height - 5)) &&
+                          (editorDims.bottom <= (attrNameNodeDims.bottom + editorDims.height + 5));
+
+  ok(editorPositionOK, "editor position acceptable");
+
+  // check to make sure the attribute-value node being edited is properly highlighted
+  let attrNameNodeHighlighted = attrNameNode_id.classList.contains("editingAttributeValue");
+  ok(attrNameNodeHighlighted, "`id` attribute-name node is editor-highlighted");
+
+  is(treePanel.editingContext.repObj, div, "editor session has correct reference to div");
+  is(treePanel.editingContext.attrObj, attrNameNode_id, "editor session has correct reference to `id` attribute-name node in HTML panel");
+  is(treePanel.editingContext.attrName, "id", "editor session knows correct attribute-name");
+
+  editorInput.value = "burp";
+  editorInput.focus();
+
+  InspectorUI.highlighter.addListener("nodeselected", highlighterTrap);
+
+  // hit <enter> to save the textbox value
+  executeSoon(function() {
+    // Extra key to test that keyboard handlers have been removed. bug 696107.
+    EventUtils.synthesizeKey("VK_LEFT", {}, attrNameNode_id.ownerDocument.defaultView);
+    EventUtils.synthesizeKey("VK_RETURN", {}, attrNameNode_id.ownerDocument.defaultView);
+  });
+
+  // two `yield` statements, to trap both the "SAVED" and "CLOSED" events that will be triggered
+  yield;
+  yield; // End of Step 2
+
+  // remove this from previous step
+  InspectorUI.highlighter.removeListener("nodeselected", highlighterTrap);
+
+  // Step 3: validate that the previous editing session saved correctly, then open editor on `class` attribute value
+  ok(!treePanel.editingContext, "Step 3: editor session ended");
+  editorVisible = editor.classList.contains("editing");
+  ok(!editorVisible, "editor popup hidden");
+  attrNameNodeHighlighted = attrNameNode_id.classList.contains("editingAttributeValue");
+  ok(!attrNameNodeHighlighted, "`id` attribute-value node is no longer editor-highlighted");
+  is(div.getAttribute("burp"), "foobar", "`id` attribute-name successfully updated");
+  is(attrNameNode_id.innerHTML, "burp", "attribute-name node in HTML panel successfully updated");
+
+  // double-click the `class` attribute-value node to open the editor
+  executeSoon(function() {
+    // firing 2 clicks right in a row to simulate a double-click
+    EventUtils.synthesizeMouse(attrNameNode_class, 2, 2, {clickCount: 2}, attrNameNode_class.ownerDocument.defaultView);
+  });
+
+  yield; // End of Step 3
+
+
+  // Step 4: enter value into editor, then hit <escape> to discard it
+  ok(treePanel.editingContext, "Step 4: editor session started");
+  editorVisible = editor.classList.contains("editing");
+  ok(editorVisible, "editor popup visible");
+
+  is(treePanel.editingContext.attrObj, attrNameNode_class, "editor session has correct reference to `class` attribute-name node in HTML panel");
+  is(treePanel.editingContext.attrName, "class", "editor session knows correct attribute-name");
+
+  editorInput.value = "Hello World";
+  editorInput.focus();
+
+  // hit <escape> to discard the inputted value
+  executeSoon(function() {
+    EventUtils.synthesizeKey("VK_ESCAPE", {}, attrNameNode_class.ownerDocument.defaultView);
+  });
+
+  yield; // End of Step 4
+
+
+  // Step 5: validate that the previous editing session discarded correctly, then open editor on `id` attribute value again
+  ok(!treePanel.editingContext, "Step 5: editor session ended");
+  editorVisible = editor.classList.contains("editing");
+  ok(!editorVisible, "editor popup hidden");
+  is(div.getAttribute("class"), "barbaz", "`class` attribute-name *not* updated");
+  is(attrNameNode_class.innerHTML, "class", "attribute-name node in HTML panel *not* updated");
+
+  // double-click the `id` attribute-name node to open the editor
+  executeSoon(function() {
+    // firing 2 clicks right in a row to simulate a double-click
+    EventUtils.synthesizeMouse(attrNameNode_id, 2, 2, {clickCount: 2}, attrNameNode_id.ownerDocument.defaultView);
+  });
+
+  yield; // End of Step 5
+
+
+  // Step 6: validate that editor opened again, then test double-click inside of editor (should do nothing)
+  ok(treePanel.editingContext, "Step 6: editor session started");
+  editorVisible = editor.classList.contains("editing");
+  ok(editorVisible, "editor popup visible");
+
+  // double-click on the editor input box
+  executeSoon(function() {
+    // firing 2 clicks right in a row to simulate a double-click
+    EventUtils.synthesizeMouse(editorInput, 2, 2, {clickCount: 2}, editorInput.ownerDocument.defaultView);
+
+    // since the previous double-click is supposed to do nothing,
+    // wait a brief moment, then move on to the next step
+    executeSoon(function() {
+      doNextStep();
+    });
+  });
+
+  yield; // End of Step 6
+
+
+  // Step 7: validate that editing session is still correct, then enter a value and try a click
+  //         outside of editor (should cancel the editing session)
+  ok(treePanel.editingContext, "Step 7: editor session still going");
+  editorVisible = editor.classList.contains("editing");
+  ok(editorVisible, "editor popup still visible");
+
+  editorInput.value = "all your base are belong to us";
+
+  // single-click the `class` attribute-value node
+  executeSoon(function() {
+    EventUtils.synthesizeMouse(attrNameNode_class, 2, 2, {}, attrNameNode_class.ownerDocument.defaultView);
+  });
+
+  yield; // End of Step 7
+
+
+  // Step 8: validate that the editor was closed and that the editing was not saved
+  ok(!treePanel.editingContext, "Step 8: editor session ended");
+  editorVisible = editor.classList.contains("editing");
+  ok(!editorVisible, "editor popup hidden");
+  is(div.getAttribute("burp"), "foobar", "`id` attribute-name *not* updated");
+  is(attrNameNode_id.innerHTML, "burp", "attribute-value node in HTML panel *not* updated");
+
+  // End of Step 8
+  executeSoon(finishUp);
+}
+
+function finishUp() {
+  // end of all steps, so clean up
+  Services.obs.removeObserver(doNextStep, InspectorUI.INSPECTOR_NOTIFICATIONS.EDITOR_OPENED, false);
+  Services.obs.removeObserver(doNextStep, InspectorUI.INSPECTOR_NOTIFICATIONS.EDITOR_CLOSED, false);
+  Services.obs.removeObserver(doNextStep, InspectorUI.INSPECTOR_NOTIFICATIONS.EDITOR_SAVED, false);
+  doc = div = null;
+  InspectorUI.closeInspectorUI();
+  gBrowser.removeCurrentTab();
+  finish();
+}
+
+function test()
+{
+  waitForExplicitFinish();
+  gBrowser.selectedTab = gBrowser.addTab();
+  gBrowser.selectedBrowser.addEventListener("load", function() {
+    gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true);
+    doc = content.document;
+    waitForFocus(setupEditorTests, content);
+  }, true);
+
+  content.location = "data:text/html,basic tests for html panel attribute-value editor";
+}
+
--- a/browser/devtools/scratchpad/scratchpad.xul
+++ b/browser/devtools/scratchpad/scratchpad.xul
@@ -19,16 +19,17 @@
    - The Mozilla Foundation.
    - Portions created by the Initial Developer are Copyright (C) 2011
    - the Initial Developer. All Rights Reserved.
    -
    - Contributor(s):
    -   Rob Campbell <robcee@mozilla.com> (original author)
    -   Mihai Sucan <mihai.sucan@gmail.com>
    -   Erik Vold <erikvvold@gmail.com>
+   -   Mark Capella <markcapella@twcny.rr.com>
    -
    - Alternatively, the contents of this file may be used under the terms of
    - either the GNU General Public License Version 2 or later (the "GPL"), or
    - the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
    - in which case the provisions of the GPL or the LGPL are applicable instead
    - of those above. If you wish to allow use of your version of this file only
    - under the terms of either the GPL or the LGPL, and not to allow others to
    - use your version of this file under the terms of the MPL, indicate your
--- a/browser/devtools/sourceeditor/orion/Makefile.dryice.js
+++ b/browser/devtools/sourceeditor/orion/Makefile.dryice.js
@@ -48,16 +48,17 @@ copy({
     ORION_EDITOR + "/orion/textview/global.js",
     ORION_EDITOR + "/orion/textview/eventTarget.js",
     ORION_EDITOR + "/orion/editor/regex.js",
     ORION_EDITOR + "/orion/textview/keyBinding.js",
     ORION_EDITOR + "/orion/textview/annotations.js",
     ORION_EDITOR + "/orion/textview/rulers.js",
     ORION_EDITOR + "/orion/textview/undoStack.js",
     ORION_EDITOR + "/orion/textview/textModel.js",
+    ORION_EDITOR + "/orion/textview/projectionTextModel.js",
     ORION_EDITOR + "/orion/textview/tooltip.js",
     ORION_EDITOR + "/orion/textview/textView.js",
     ORION_EDITOR + "/orion/textview/textDND.js",
     ORION_EDITOR + "/orion/editor/htmlGrammar.js",
     ORION_EDITOR + "/orion/editor/textMateStyler.js",
     ORION_EDITOR + "/examples/textview/textStyler.js",
   ],
   dest: js_src,
--- a/browser/devtools/sourceeditor/orion/README
+++ b/browser/devtools/sourceeditor/orion/README
@@ -17,16 +17,20 @@ Orion version: git clone from 2012-01-26
 
   + patches for Eclipse Bug 370606 - Problems with UndoStack and deletions at
                                      the beginning of the document
     http://git.eclipse.org/c/orion/org.eclipse.orion.client.git/commit/?id=cec71bddaf32251c34d3728df5da13c130d14f33
     http://git.eclipse.org/c/orion/org.eclipse.orion.client.git/commit/?id=3ce24b94f1d8103b16b9cf16f2f50a6302d43b18
     http://git.eclipse.org/c/orion/org.eclipse.orion.client.git/commit/?id=27177e9a3dc70c20b4877e3eab3adfff1d56e342
     see https://bugs.eclipse.org/bugs/show_bug.cgi?id=370606
 
+  + patch for Mozilla Bug 730532 - remove CSS2Properties aliases for MozOpacity
+                                   and MozOutline*
+    see https://bugzilla.mozilla.org/show_bug.cgi?id=730532#c3
+
 # License
 
 The following files are licensed according to the contents in the LICENSE
 file:
   orion.js
   orion.css
 
 # Theming
--- a/browser/devtools/sourceeditor/orion/orion.js
+++ b/browser/devtools/sourceeditor/orion/orion.js
@@ -2565,16 +2565,600 @@ define("orion/textview/textModel", ['ori
 });/*******************************************************************************
  * @license
  * Copyright (c) 2010, 2011 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials are made 
  * available under the terms of the Eclipse Public License v1.0 
  * (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution 
  * License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html). 
  * 
+ * Contributors: 
+ *		Felipe Heidrich (IBM Corporation) - initial API and implementation
+ *		Silenio Quarti (IBM Corporation) - initial API and implementation
+ ******************************************************************************/
+
+/*global define */
+
+define("orion/textview/projectionTextModel", ['orion/textview/textModel', 'orion/textview/eventTarget'], function(mTextModel, mEventTarget) {
+
+	/**
+	 * @class This object represents a projection range. A projection specifies a
+	 * range of text and the replacement text. The range of text is relative to the
+	 * base text model associated to a projection model.
+	 * <p>
+	 * <b>See:</b><br/>
+	 * {@link orion.textview.ProjectionTextModel}<br/>
+	 * {@link orion.textview.ProjectionTextModel#addProjection}<br/>
+	 * </p>		 
+	 * @name orion.textview.Projection
+	 * 
+	 * @property {Number} start The start offset of the projection range. 
+	 * @property {Number} end The end offset of the projection range. This offset is exclusive.
+	 * @property {String|orion.textview.TextModel} [text=""] The projection text to be inserted
+	 */
+	/**
+	 * Constructs a new <code>ProjectionTextModel</code> based on the specified <code>TextModel</code>.
+	 *
+	 * @param {orion.textview.TextModel} baseModel The base text model.
+	 *
+	 * @name orion.textview.ProjectionTextModel
+	 * @class The <code>ProjectionTextModel</code> represents a projection of its base text
+	 * model. Projection ranges can be added to the projection text model to hide and/or insert
+	 * ranges to the base text model.
+	 * <p>
+	 * The contents of the projection text model is modified when changes occur in the base model,
+	 * projection model or by calls to {@link #addProjection} and {@link #removeProjection}.
+	 * </p>
+	 * <p>
+	 * <b>See:</b><br/>
+	 * {@link orion.textview.TextView}<br/>
+	 * {@link orion.textview.TextModel}
+	 * {@link orion.textview.TextView#setModel}
+	 * </p>
+	 * @borrows orion.textview.EventTarget#addEventListener as #addEventListener
+	 * @borrows orion.textview.EventTarget#removeEventListener as #removeEventListener
+	 * @borrows orion.textview.EventTarget#dispatchEvent as #dispatchEvent
+	 */
+	function ProjectionTextModel(baseModel) {
+		this._model = baseModel;	/* Base Model */
+		this._projections = [];
+	}
+
+	ProjectionTextModel.prototype = /** @lends orion.textview.ProjectionTextModel.prototype */ {
+		/**
+		 * Adds a projection range to the model.
+		 * <p>
+		 * The model must notify the listeners before and after the the text is
+		 * changed by calling {@link #onChanging} and {@link #onChanged} respectively. 
+		 * </p>
+		 * @param {orion.textview.Projection} projection The projection range to be added.
+		 * 
+		 * @see #removeProjection
+		 */
+		addProjection: function(projection) {
+			if (!projection) {return;}
+			//start and end can't overlap any exist projection
+			var model = this._model, projections = this._projections;
+			projection._lineIndex = model.getLineAtOffset(projection.start);
+			projection._lineCount = model.getLineAtOffset(projection.end) - projection._lineIndex;
+			var text = projection.text;
+			if (!text) { text = ""; }
+			if (typeof text === "string") {
+				projection._model = new mTextModel.TextModel(text, model.getLineDelimiter());
+			} else {
+				projection._model = text;
+			}
+			var eventStart = this.mapOffset(projection.start, true);
+			var removedCharCount = projection.end - projection.start;
+			var removedLineCount = projection._lineCount;
+			var addedCharCount = projection._model.getCharCount();
+			var addedLineCount = projection._model.getLineCount() - 1;
+			var modelChangingEvent = {
+				type: "Changing",
+				text: projection._model.getText(),
+				start: eventStart,
+				removedCharCount: removedCharCount,
+				addedCharCount: addedCharCount,
+				removedLineCount: removedLineCount,
+				addedLineCount: addedLineCount
+			};
+			this.onChanging(modelChangingEvent);
+			var index = this._binarySearch(projections, projection.start);
+			projections.splice(index, 0, projection);
+			var modelChangedEvent = {
+				type: "Changed",
+				start: eventStart,
+				removedCharCount: removedCharCount,
+				addedCharCount: addedCharCount,
+				removedLineCount: removedLineCount,
+				addedLineCount: addedLineCount
+			};
+			this.onChanged(modelChangedEvent);
+		},
+		/**
+		 * Returns all projection ranges of this model.
+		 * 
+		 * @return {orion.textview.Projection[]} The projection ranges.
+		 * 
+		 * @see #addProjection
+		 */
+		getProjections: function() {
+			return this._projections.slice(0);
+		},
+		/**
+		 * Gets the base text model.
+		 *
+		 * @return {orion.textview.TextModel} The base text model.
+		 */
+		getBaseModel: function() {
+			return this._model;
+		},
+		/**
+		 * Maps offsets between the projection model and its base model.
+		 *
+		 * @param {Number} offset The offset to be mapped.
+		 * @param {Boolean} [baseOffset=false] <code>true</code> if <code>offset</code> is in base model and
+		 *	should be mapped to the projection model.
+		 * @return {Number} The mapped offset
+		 */
+		mapOffset: function(offset, baseOffset) {
+			var projections = this._projections, delta = 0, i, projection;
+			if (baseOffset) {
+				for (i = 0; i < projections.length; i++) {
+					projection = projections[i];
+					if (projection.start > offset) { break; }
+					if (projection.end > offset) { return -1; }
+					delta += projection._model.getCharCount() - (projection.end - projection.start);
+				}
+				return offset + delta;
+			}
+			for (i = 0; i < projections.length; i++) {
+				projection = projections[i];
+				if (projection.start > offset - delta) { break; }
+				var charCount = projection._model.getCharCount();
+				if (projection.start + charCount > offset - delta) {
+					return -1;
+				}
+				delta += charCount - (projection.end - projection.start);
+			}
+			return offset - delta;
+		},
+		/**
+		 * Removes a projection range from the model.
+		 * <p>
+		 * The model must notify the listeners before and after the the text is
+		 * changed by calling {@link #onChanging} and {@link #onChanged} respectively. 
+		 * </p>
+		 * 
+		 * @param {orion.textview.Projection} projection The projection range to be removed.
+		 * 
+		 * @see #addProjection
+		 */
+		removeProjection: function(projection) {
+			//TODO remove listeners from model
+			var i, delta = 0;
+			for (i = 0; i < this._projections.length; i++) {
+				var p = this._projections[i];
+				if (p === projection) {
+					projection = p;
+					break;
+				}
+				delta += p._model.getCharCount() - (p.end - p.start);
+			}
+			if (i < this._projections.length) {
+				var model = this._model;
+				var eventStart = projection.start + delta;
+				var addedCharCount = projection.end - projection.start;
+				var addedLineCount = projection._lineCount;
+				var removedCharCount = projection._model.getCharCount();
+				var removedLineCount = projection._model.getLineCount() - 1;
+				var modelChangingEvent = {
+					type: "Changing",
+					text: model.getText(projection.start, projection.end),
+					start: eventStart,
+					removedCharCount: removedCharCount,
+					addedCharCount: addedCharCount,
+					removedLineCount: removedLineCount,
+					addedLineCount: addedLineCount
+				};
+				this.onChanging(modelChangingEvent);
+				this._projections.splice(i, 1);
+				var modelChangedEvent = {
+					type: "Changed",
+					start: eventStart,
+					removedCharCount: removedCharCount,
+					addedCharCount: addedCharCount,
+					removedLineCount: removedLineCount,
+					addedLineCount: addedLineCount
+				};
+				this.onChanged(modelChangedEvent);
+			}
+		},
+		/** @ignore */
+		_binarySearch: function (array, offset) {
+			var high = array.length, low = -1, index;
+			while (high - low > 1) {
+				index = Math.floor((high + low) / 2);
+				if (offset <= array[index].start) {
+					high = index;
+				} else {
+					low = index;
+				}
+			}
+			return high;
+		},
+		/**
+		 * @see orion.textview.TextModel#getCharCount
+		 */
+		getCharCount: function() {
+			var count = this._model.getCharCount(), projections = this._projections;
+			for (var i = 0; i < projections.length; i++) {
+				var projection = projections[i];
+				count += projection._model.getCharCount() - (projection.end - projection.start);
+			}
+			return count;
+		},
+		/**
+		 * @see orion.textview.TextModel#getLine
+		 */
+		getLine: function(lineIndex, includeDelimiter) {
+			if (lineIndex < 0) { return null; }
+			var model = this._model, projections = this._projections;
+			var delta = 0, result = [], offset = 0, i, lineCount, projection;
+			for (i = 0; i < projections.length; i++) {
+				projection = projections[i];
+				if (projection._lineIndex >= lineIndex - delta) { break; }
+				lineCount = projection._model.getLineCount() - 1;
+				if (projection._lineIndex + lineCount >= lineIndex - delta) {
+					var projectionLineIndex = lineIndex - (projection._lineIndex + delta);
+					if (projectionLineIndex < lineCount) {
+						return projection._model.getLine(projectionLineIndex, includeDelimiter);
+					} else {
+						result.push(projection._model.getLine(lineCount));
+					}
+				}
+				offset = projection.end;
+				delta += lineCount - projection._lineCount;
+			}
+			offset = Math.max(offset, model.getLineStart(lineIndex - delta));
+			for (; i < projections.length; i++) {
+				projection = projections[i];
+				if (projection._lineIndex > lineIndex - delta) { break; }
+				result.push(model.getText(offset, projection.start));
+				lineCount = projection._model.getLineCount() - 1;
+				if (projection._lineIndex + lineCount > lineIndex - delta) {
+					result.push(projection._model.getLine(0, includeDelimiter));
+					return result.join("");
+				}
+				result.push(projection._model.getText());
+				offset = projection.end;
+				delta += lineCount - projection._lineCount;
+			}
+			var end = model.getLineEnd(lineIndex - delta, includeDelimiter);
+			if (offset < end) {
+				result.push(model.getText(offset, end));
+			}
+			return result.join("");
+		},
+		/**
+		 * @see orion.textview.TextModel#getLineAtOffset
+		 */
+		getLineAtOffset: function(offset) {
+			var model = this._model, projections = this._projections;
+			var delta = 0, lineDelta = 0;
+			for (var i = 0; i < projections.length; i++) {
+				var projection = projections[i];
+				if (projection.start > offset - delta) { break; }
+				var charCount = projection._model.getCharCount();
+				if (projection.start + charCount > offset - delta) {
+					var projectionOffset = offset - (projection.start + delta);
+					lineDelta += projection._model.getLineAtOffset(projectionOffset);
+					delta += projectionOffset;
+					break;
+				}
+				lineDelta += projection._model.getLineCount() - 1 - projection._lineCount;
+				delta += charCount - (projection.end - projection.start);
+			}
+			return model.getLineAtOffset(offset - delta) + lineDelta;
+		},
+		/**
+		 * @see orion.textview.TextModel#getLineCount
+		 */
+		getLineCount: function() {
+			var model = this._model, projections = this._projections;
+			var count = model.getLineCount();
+			for (var i = 0; i < projections.length; i++) {
+				var projection = projections[i];
+				count += projection._model.getLineCount() - 1 - projection._lineCount;
+			}
+			return count;
+		},
+		/**
+		 * @see orion.textview.TextModel#getLineDelimiter
+		 */
+		getLineDelimiter: function() {
+			return this._model.getLineDelimiter();
+		},
+		/**
+		 * @see orion.textview.TextModel#getLineEnd
+		 */
+		getLineEnd: function(lineIndex, includeDelimiter) {
+			if (lineIndex < 0) { return -1; }
+			var model = this._model, projections = this._projections;
+			var delta = 0, offsetDelta = 0;
+			for (var i = 0; i < projections.length; i++) {
+				var projection = projections[i];
+				if (projection._lineIndex > lineIndex - delta) { break; }
+				var lineCount = projection._model.getLineCount() - 1;
+				if (projection._lineIndex + lineCount > lineIndex - delta) {
+					var projectionLineIndex = lineIndex - (projection._lineIndex + delta);
+					return projection._model.getLineEnd (projectionLineIndex, includeDelimiter) + projection.start + offsetDelta;
+				}
+				offsetDelta += projection._model.getCharCount() - (projection.end - projection.start);
+				delta += lineCount - projection._lineCount;
+			}
+			return model.getLineEnd(lineIndex - delta, includeDelimiter) + offsetDelta;
+		},
+		/**
+		 * @see orion.textview.TextModel#getLineStart
+		 */
+		getLineStart: function(lineIndex) {
+			if (lineIndex < 0) { return -1; }
+			var model = this._model, projections = this._projections;
+			var delta = 0, offsetDelta = 0;
+			for (var i = 0; i < projections.length; i++) {
+				var projection = projections[i];
+				if (projection._lineIndex >= lineIndex - delta) { break; }
+				var lineCount = projection._model.getLineCount() - 1;
+				if (projection._lineIndex + lineCount >= lineIndex - delta) {
+					var projectionLineIndex = lineIndex - (projection._lineIndex + delta);
+					return projection._model.getLineStart (projectionLineIndex) + projection.start + offsetDelta;
+				}
+				offsetDelta += projection._model.getCharCount() - (projection.end - projection.start);
+				delta += lineCount - projection._lineCount;
+			}
+			return model.getLineStart(lineIndex - delta) + offsetDelta;
+		},
+		/**
+		 * @see orion.textview.TextModel#getText
+		 */
+		getText: function(start, end) {
+			if (start === undefined) { start = 0; }
+			var model = this._model, projections = this._projections;
+			var delta = 0, result = [], i, projection, charCount;
+			for (i = 0; i < projections.length; i++) {
+				projection = projections[i];
+				if (projection.start > start - delta) { break; }
+				charCount = projection._model.getCharCount();
+				if (projection.start + charCount > start - delta) {
+					if (end !== undefined && projection.start + charCount > end - delta) {
+						return projection._model.getText(start - (projection.start + delta), end - (projection.start + delta));
+					} else {
+						result.push(projection._model.getText(start - (projection.start + delta)));
+						start = projection.end + delta + charCount - (projection.end - projection.start);
+					}
+				}
+				delta += charCount - (projection.end - projection.start);
+			}
+			var offset = start - delta;
+			if (end !== undefined) {
+				for (; i < projections.length; i++) {
+					projection = projections[i];
+					if (projection.start > end - delta) { break; }
+					result.push(model.getText(offset, projection.start));
+					charCount = projection._model.getCharCount();
+					if (projection.start + charCount > end - delta) {
+						result.push(projection._model.getText(0, end - (projection.start + delta)));
+						return result.join("");
+					}
+					result.push(projection._model.getText());
+					offset = projection.end;
+					delta += charCount - (projection.end - projection.start);
+				}
+				result.push(model.getText(offset, end - delta));
+			} else {
+				for (; i < projections.length; i++) {
+					projection = projections[i];
+					result.push(model.getText(offset, projection.start));
+					result.push(projection._model.getText());
+					offset = projection.end;
+				}
+				result.push(model.getText(offset));
+			}
+			return result.join("");
+		},
+		/** @ignore */
+		_onChanging: function(text, start, removedCharCount, addedCharCount, removedLineCount, addedLineCount) {
+			var model = this._model, projections = this._projections, i, projection, delta = 0, lineDelta;
+			var end = start + removedCharCount;
+			for (; i < projections.length; i++) {
+				projection = projections[i];
+				if (projection.start > start) { break; }
+				delta += projection._model.getCharCount() - (projection.end - projection.start);
+			}
+			/*TODO add stuff saved by setText*/
+			var mapStart = start + delta, rangeStart = i;
+			for (; i < projections.length; i++) {
+				projection = projections[i];
+				if (projection.start > end) { break; }
+				delta += projection._model.getCharCount() - (projection.end - projection.start);
+				lineDelta += projection._model.getLineCount() - 1 - projection._lineCount;
+			}
+			/*TODO add stuff saved by setText*/
+			var mapEnd = end + delta, rangeEnd = i;
+			this.onChanging(mapStart, mapEnd - mapStart, addedCharCount/*TODO add stuff saved by setText*/, removedLineCount + lineDelta/*TODO add stuff saved by setText*/, addedLineCount/*TODO add stuff saved by setText*/);
+			projections.splice(projections, rangeEnd - rangeStart);
+			var count = text.length - (mapEnd - mapStart);
+			for (; i < projections.length; i++) {
+				projection = projections[i];
+				projection.start += count;
+				projection.end += count;
+				projection._lineIndex = model.getLineAtOffset(projection.start);
+			}
+		},
+		/**
+		 * @see orion.textview.TextModel#onChanging
+		 */
+		onChanging: function(modelChangingEvent) {
+			return this.dispatchEvent(modelChangingEvent);
+		},
+		/**
+		 * @see orion.textview.TextModel#onChanged
+		 */
+		onChanged: function(modelChangedEvent) {
+			return this.dispatchEvent(modelChangedEvent);
+		},
+		/**
+		 * @see orion.textview.TextModel#setLineDelimiter
+		 */
+		setLineDelimiter: function(lineDelimiter) {
+			this._model.setLineDelimiter(lineDelimiter);
+		},
+		/**
+		 * @see orion.textview.TextModel#setText
+		 */
+		setText: function(text, start, end) {
+			if (text === undefined) { text = ""; }
+			if (start === undefined) { start = 0; }
+			var eventStart = start, eventEnd = end;
+			var model = this._model, projections = this._projections;
+			var delta = 0, lineDelta = 0, i, projection, charCount, startProjection, endProjection, startLineDelta = 0;
+			for (i = 0; i < projections.length; i++) {
+				projection = projections[i];
+				if (projection.start > start - delta) { break; }
+				charCount = projection._model.getCharCount();
+				if (projection.start + charCount > start - delta) {
+					if (end !== undefined && projection.start + charCount > end - delta) {
+						projection._model.setText(text, start - (projection.start + delta), end - (projection.start + delta));
+						//TODO events - special case
+						return;
+					} else {
+						startLineDelta = projection._model.getLineCount() - 1 - projection._model.getLineAtOffset(start - (projection.start + delta));
+						startProjection = {
+							projection: projection,
+							start: start - (projection.start + delta)
+						};
+						start = projection.end + delta + charCount - (projection.end - projection.start);
+					}
+				}
+				lineDelta += projection._model.getLineCount() - 1 - projection._lineCount;
+				delta += charCount - (projection.end - projection.start);
+			}
+			var mapStart = start - delta, rangeStart = i, startLine = model.getLineAtOffset(mapStart) + lineDelta - startLineDelta;
+			if (end !== undefined) {
+				for (; i < projections.length; i++) {
+					projection = projections[i];
+					if (projection.start > end - delta) { break; }
+					charCount = projection._model.getCharCount();
+					if (projection.start + charCount > end - delta) {
+						lineDelta += projection._model.getLineAtOffset(end - (projection.start + delta));
+						charCount = end - (projection.start + delta);
+						end = projection.end + delta;
+						endProjection = {
+							projection: projection,
+							end: charCount
+						};
+						break;
+					}
+					lineDelta += projection._model.getLineCount() - 1 - projection._lineCount;
+					delta += charCount - (projection.end - projection.start);
+				}
+			} else {
+				for (; i < projections.length; i++) {
+					projection = projections[i];
+					lineDelta += projection._model.getLineCount() - 1 - projection._lineCount;
+					delta += projection._model.getCharCount() - (projection.end - projection.start);
+				}
+				end = eventEnd = model.getCharCount() + delta;
+			}
+			var mapEnd = end - delta, rangeEnd = i, endLine = model.getLineAtOffset(mapEnd) + lineDelta;
+			
+			//events
+			var removedCharCount = eventEnd - eventStart;
+			var removedLineCount = endLine - startLine;
+			var addedCharCount = text.length;
+			var addedLineCount = 0;
+			var cr = 0, lf = 0, index = 0;
+			while (true) {
+				if (cr !== -1 && cr <= index) { cr = text.indexOf("\r", index); }
+				if (lf !== -1 && lf <= index) { lf = text.indexOf("\n", index); }
+				if (lf === -1 && cr === -1) { break; }
+				if (cr !== -1 && lf !== -1) {
+					if (cr + 1 === lf) {
+						index = lf + 1;
+					} else {
+						index = (cr < lf ? cr : lf) + 1;
+					}
+				} else if (cr !== -1) {
+					index = cr + 1;
+				} else {
+					index = lf + 1;
+				}
+				addedLineCount++;
+			}
+			
+			var modelChangingEvent = {
+				type: "Changing",
+				text: text,
+				start: eventStart,
+				removedCharCount: removedCharCount,
+				addedCharCount: addedCharCount,
+				removedLineCount: removedLineCount,
+				addedLineCount: addedLineCount
+			};
+			this.onChanging(modelChangingEvent);
+			
+//			var changeLineCount = model.getLineAtOffset(mapEnd) - model.getLineAtOffset(mapStart) + addedLineCount;
+			model.setText(text, mapStart, mapEnd);
+			if (startProjection) {
+				projection = startProjection.projection;
+				projection._model.setText("", startProjection.start);
+			}		
+			if (endProjection) {
+				projection = endProjection.projection;
+				projection._model.setText("", 0, endProjection.end);
+				projection.start = projection.end;
+				projection._lineCount = 0;
+			}
+			projections.splice(rangeStart, rangeEnd - rangeStart);
+			var changeCount = text.length - (mapEnd - mapStart);
+			for (i = rangeEnd; i < projections.length; i++) {
+				projection = projections[i];
+				projection.start += changeCount;
+				projection.end += changeCount;
+//				if (projection._lineIndex + changeLineCount !== model.getLineAtOffset(projection.start)) {
+//					log("here");
+//				}
+				projection._lineIndex = model.getLineAtOffset(projection.start);
+//				projection._lineIndex += changeLineCount;
+			}
+			
+			var modelChangedEvent = {
+				type: "Changed",
+				start: eventStart,
+				removedCharCount: removedCharCount,
+				addedCharCount: addedCharCount,
+				removedLineCount: removedLineCount,
+				addedLineCount: addedLineCount
+			};
+			this.onChanged(modelChangedEvent);
+		}
+	};
+	mEventTarget.EventTarget.addMixin(ProjectionTextModel.prototype);
+
+	return {ProjectionTextModel: ProjectionTextModel};
+});
+/*******************************************************************************
+ * @license
+ * Copyright (c) 2010, 2011 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials are made 
+ * available under the terms of the Eclipse Public License v1.0 
+ * (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution 
+ * License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html). 
+ * 
  * Contributors: IBM Corporation - initial API and implementation
  ******************************************************************************/
 
 /*global define setTimeout clearTimeout setInterval clearInterval Node */
 
 define("orion/textview/tooltip", ['orion/textview/textView', 'orion/textview/textModel', 'orion/textview/projectionTextModel'], function(mTextView, mTextModel, mProjectionTextModel) {
 
 	/** @private */
--- a/browser/devtools/sourceeditor/source-editor-orion.jsm
+++ b/browser/devtools/sourceeditor/source-editor-orion.jsm
@@ -903,17 +903,18 @@ SourceEditor.prototype = {
     }
   },
 
   /**
    * Retrieve the list of Orion Annotations filtered by type for the given text range.
    *
    * @private
    * @param string aType
-   *        The annotation type to filter annotations for.
+   *        The annotation type to filter annotations for. Use one of the keys
+   *        in ORION_ANNOTATION_TYPES.
    * @param number aStart
    *        Offset from where to start finding the annotations.
    * @param number aEnd
    *        End offset for retrieving the annotations.
    * @return array
    *         The array of annotations, filtered by type, within the given text
    *         range.
    */
--- a/browser/devtools/sourceeditor/source-editor.jsm
+++ b/browser/devtools/sourceeditor/source-editor.jsm
@@ -263,34 +263,34 @@ SourceEditor.EVENTS = {
   FOCUS: "Focus",
 
   /**
    * The blur event is fired when the editor goes out of focus.
    */
   BLUR: "Blur",
 
   /**
-   * The MouseMove event is sent when the user moves the mouse over a line
-   * annotation. The event object properties:
+   * The MouseMove event is sent when the user moves the mouse over a line.
+   * The event object properties:
    *   - event - the DOM mousemove event object.
    *   - x and y - the mouse coordinates relative to the document being edited.
    */
   MOUSE_MOVE: "MouseMove",
 
   /**
-   * The MouseOver event is sent when the mouse pointer enters a line
-   * annotation. The event object properties:
+   * The MouseOver event is sent when the mouse pointer enters a line.
+   * The event object properties:
    *   - event - the DOM mouseover event object.
    *   - x and y - the mouse coordinates relative to the document being edited.
    */
   MOUSE_OVER: "MouseOver",
 
   /**
-   * This MouseOut event is sent when the mouse pointer exits a line
-   * annotation. The event object properties:
+   * This MouseOut event is sent when the mouse pointer exits a line.
+   * The event object properties:
    *   - event - the DOM mouseout event object.
    *   - x and y - the mouse coordinates relative to the document being edited.
    */
   MOUSE_OUT: "MouseOut",
 
   /**
    * The BreakpointChange event is fired when a new breakpoint is added or when
    * a breakpoint is removed - either through API use or through the editor UI.
--- a/browser/devtools/styleinspector/CssHtmlTree.jsm
+++ b/browser/devtools/styleinspector/CssHtmlTree.jsm
@@ -36,16 +36,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 ***** */
 
 const Ci = Components.interfaces;
+const Cc = Components.classes;
 const Cu = Components.utils;
 const FILTER_CHANGED_TIMEOUT = 300;
 
 const HTML_NS = "http://www.w3.org/1999/xhtml";
 const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/PluralForm.jsm");
@@ -156,31 +157,41 @@ function CssHtmlTree(aStyleInspector)
   this.styleWin = aStyleInspector.iframe;
   this.styleInspector = aStyleInspector;
   this.cssLogic = aStyleInspector.cssLogic;
   this.doc = aStyleInspector.document;
   this.win = aStyleInspector.window;
   this.getRTLAttr = this.win.getComputedStyle(this.win.gBrowser).direction;
   this.propertyViews = [];
 
+  // Create bound methods.
+  this.siBoundMenuUpdate = this.computedViewMenuUpdate.bind(this);
+  this.siBoundCopy = this.computedViewCopy.bind(this);
+  this.siBoundCopyDeclaration = this.computedViewCopyDeclaration.bind(this);
+  this.siBoundCopyProperty = this.computedViewCopyProperty.bind(this);
+  this.siBoundCopyPropertyValue = this.computedViewCopyPropertyValue.bind(this);
+
   // The document in which we display the results (csshtmltree.xul).
   this.styleDocument = this.styleWin.contentWindow.document;
 
+  this.styleDocument.addEventListener("copy", this.siBoundCopy);
+
   // Nodes used in templating
   this.root = this.styleDocument.getElementById("root");
   this.templateRoot = this.styleDocument.getElementById("templateRoot");
   this.propertyContainer = this.styleDocument.getElementById("propertyContainer");
   this.panel = aStyleInspector.panel;
 
   // No results text.
   this.noResults = this.styleDocument.getElementById("noResults");
 
   // The element that we're inspecting, and the document that it comes from.
   this.viewedElement = null;
   this.createStyleViews();
+  this.createContextMenu();
 }
 
 /**
  * Memoized lookup of a l10n string from a string bundle.
  * @param {string} aName The key to lookup.
  * @returns A localized version of the given key.
  */
 CssHtmlTree.l10n = function CssHtmlTree_l10n(aName)
@@ -226,16 +237,21 @@ CssHtmlTree.processTemplate = function C
 
 XPCOMUtils.defineLazyGetter(CssHtmlTree, "_strings", function() Services.strings
         .createBundle("chrome://browser/locale/devtools/styleinspector.properties"));
 
 XPCOMUtils.defineLazyGetter(CssHtmlTree, "HELP_LINK_TITLE", function() {
   return CssHtmlTree.HELP_LINK_TITLE = CssHtmlTree.l10n("helpLinkTitle");
 });
 
+XPCOMUtils.defineLazyGetter(this, "clipboardHelper", function() {
+  return Cc["@mozilla.org/widget/clipboardhelper;1"].
+    getService(Ci.nsIClipboardHelper);
+});
+
 CssHtmlTree.prototype = {
   // Cache the list of properties that have matched and unmatched properties.
   _matchedProperties: null,
   _unmatchedProperties: null,
 
   htmlComplete: false,
 
   // Used for cancelling timeouts in the style filter.
@@ -465,32 +481,229 @@ CssHtmlTree.prototype = {
       let result = this.cssLogic.hasUnmatchedSelectors([aProperty]);
       this._unmatchedProperties[aProperty] = result[aProperty];
     }
 
     return this._unmatchedProperties[aProperty];
   },
 
   /**
+   * Create a context menu.
+   */
+  createContextMenu: function SI_createContextMenu()
+  {
+    let popupSet = this.doc.getElementById("mainPopupSet");
+
+    let menu = this.doc.createElement("menupopup");
+    menu.addEventListener("popupshowing", this.siBoundMenuUpdate);
+    menu.id = "computed-view-context-menu";
+    popupSet.appendChild(menu);
+
+    // Copy selection
+    let label = CssHtmlTree.l10n("style.contextmenu.copyselection");
+    let accessKey = CssHtmlTree.l10n("style.contextmenu.copyselection.accesskey");
+    let item = this.doc.createElement("menuitem");
+    item.id = "computed-view-copy";
+    item.setAttribute("label", label);
+    item.setAttribute("accesskey", accessKey);
+    item.addEventListener("command", this.siBoundCopy);
+    menu.appendChild(item);
+
+    // Copy declaration
+    label = CssHtmlTree.l10n("style.contextmenu.copydeclaration");
+    accessKey = CssHtmlTree.l10n("style.contextmenu.copydeclaration.accesskey");
+    item = this.doc.createElement("menuitem");
+    item.id = "computed-view-copy-declaration";
+    item.setAttribute("label", label);
+    item.setAttribute("accesskey", accessKey);
+    item.addEventListener("command", this.siBoundCopyDeclaration);
+    menu.appendChild(item);
+
+    // Copy property name
+    label = CssHtmlTree.l10n("style.contextmenu.copyproperty");
+    accessKey = CssHtmlTree.l10n("style.contextmenu.copyproperty.accesskey");
+    item = this.doc.createElement("menuitem");
+    item.id = "computed-view-copy-property";
+    item.setAttribute("label", label);
+    item.setAttribute("accesskey", accessKey);
+    item.addEventListener("command", this.siBoundCopyProperty);
+    menu.appendChild(item);
+
+    // Copy property value
+    label = CssHtmlTree.l10n("style.contextmenu.copypropertyvalue");
+    accessKey = CssHtmlTree.l10n("style.contextmenu.copypropertyvalue.accesskey");
+    item = this.doc.createElement("menuitem");
+    item.id = "computed-view-copy-property-value";
+    item.setAttribute("label", label);
+    item.setAttribute("accesskey", accessKey);
+    item.addEventListener("command", this.siBoundCopyPropertyValue);
+    menu.appendChild(item);
+
+    this.styleWin.setAttribute("context", menu.id);
+  },
+
+  /**
+   * Update the context menu by disabling irrelevant menuitems and enabling
+   * relevant ones.
+   */
+  computedViewMenuUpdate: function si_computedViewMenuUpdate()
+  {
+    let win = this.styleDocument.defaultView;
+    let disable = win.getSelection().isCollapsed;
+    let menuitem = this.doc.querySelector("#computed-view-copy");
+    menuitem.disabled = disable;
+
+    let node = this.doc.popupNode;
+    if (!node.classList.contains("property-view")) {
+      while (node = node.parentElement) {
+        if (node.classList.contains("property-view")) {
+          break;
+        }
+      }
+    }
+    let disablePropertyItems = !node;
+    menuitem = this.doc.querySelector("#computed-view-copy-declaration");
+    menuitem.disabled = disablePropertyItems;
+    menuitem = this.doc.querySelector("#computed-view-copy-property");
+    menuitem.disabled = disablePropertyItems;
+    menuitem = this.doc.querySelector("#computed-view-copy-property-value");
+    menuitem.disabled = disablePropertyItems;
+  },
+
+  /**
+   * Copy selected text.
+   *
+   * @param aEvent The event object
+   */
+  computedViewCopy: function si_computedViewCopy(aEvent)
+  {
+    let win = this.styleDocument.defaultView;
+    let text = win.getSelection().toString();
+
+    // Tidy up block headings by moving CSS property names and their values onto
+    // the same line and inserting a colon between them.
+    text = text.replace(/(.+)\r?\n\s+/g, "$1: ");
+
+    // Remove any MDN link titles
+    text = text.replace(CssHtmlTree.HELP_LINK_TITLE, "");
+    clipboardHelper.copyString(text);
+
+    if (aEvent) {
+      aEvent.preventDefault();
+    }
+  },
+
+  /**
+   * Copy declaration.
+   *
+   * @param aEvent The event object
+   */
+  computedViewCopyDeclaration: function si_computedViewCopyDeclaration(aEvent)
+  {
+    let node = this.doc.popupNode;
+    if (!node.classList.contains("property-view")) {
+      while (node = node.parentElement) {
+        if (node.classList.contains("property-view")) {
+          break;
+        }
+      }
+    }
+    if (node) {
+      let name = node.querySelector(".property-name").textContent;
+      let value = node.querySelector(".property-value").textContent;
+
+      clipboardHelper.copyString(name + ": " + value + ";");
+    }
+  },
+
+  /**
+   * Copy property name.
+   *
+   * @param aEvent The event object
+   */
+  computedViewCopyProperty: function si_computedViewCopyProperty(aEvent)
+  {
+    let node = this.doc.popupNode;
+    if (!node.classList.contains("property-view")) {
+      while (node = node.parentElement) {
+        if (node.classList.contains("property-view")) {
+          break;
+        }
+      }
+    }
+    if (node) {
+      node = node.querySelector(".property-name");
+      clipboardHelper.copyString(node.textContent);
+    }
+  },
+
+  /**
+   * Copy property value.
+   *
+   * @param aEvent The event object
+   */
+  computedViewCopyPropertyValue: function si_computedViewCopyPropertyValue(aEvent)
+  {
+    let node = this.doc.popupNode;
+    if (!node.classList.contains("property-view")) {
+      while (node = node.parentElement) {
+        if (node.classList.contains("property-view")) {
+          break;
+        }
+      }
+    }
+    if (node) {
+      node = node.querySelector(".property-value");
+      clipboardHelper.copyString(node.textContent);
+    }
+  },
+
+  /**
    * Destructor for CssHtmlTree.
    */
   destroy: function CssHtmlTree_destroy()
   {
     delete this.viewedElement;
 
     // Remove event listeners
     this.onlyUserStylesCheckbox.removeEventListener("command",
       this.onlyUserStylesChanged);
     this.searchField.removeEventListener("command", this.filterChanged);
 
     // Cancel tree construction
     if (this._refreshProcess) {
       this._refreshProcess.cancel();
     }
 
+    // Remove context menu
+    let menu = this.doc.querySelector("#computed-view-context-menu");
+    if (menu) {
+      // Copy selected
+      let menuitem = this.doc.querySelector("#computed-view-copy");
+      menuitem.removeEventListener("command", this.siBoundCopy);
+
+      // Copy property
+      menuitem = this.doc.querySelector("#computed-view-copy-declaration");
+      menuitem.removeEventListener("command", this.siBoundCopyDeclaration);
+
+      // Copy property name
+      menuitem = this.doc.querySelector("#computed-view-copy-property");
+      menuitem.removeEventListener("command", this.siBoundCopyProperty);
+
+      // Copy property value
+      menuitem = this.doc.querySelector("#computed-view-copy-property-value");
+      menuitem.removeEventListener("command", this.siBoundCopyPropertyValue);
+
+      menu.removeEventListener("popupshowing", this.siBoundMenuUpdate);
+      menu.parentNode.removeChild(menu);
+    }
+
+    // Remove bound listeners
+    this.styleDocument.removeEventListener("copy", this.siBoundCopy);
+
     // Nodes used in templating
     delete this.root;
     delete this.propertyContainer;
     delete this.panel;
 
     // The document in which we display the results (csshtmltree.xul).
     delete this.styleDocument;
 
@@ -653,42 +866,45 @@ PropertyView.prototype = {
     return "property-content-hidden";
   },
 
   buildMain: function PropertyView_buildMain()
   {
     let doc = this.tree.doc;
     this.element = doc.createElementNS(HTML_NS, "tr");
     this.element.setAttribute("class", this.propertyHeaderClassName);
-    this.element.addEventListener("click", this.propertyRowClick.bind(this), false);
 
     this.propertyHeader = doc.createElementNS(HTML_NS, "td");
     this.element.appendChild(this.propertyHeader);
     this.propertyHeader.setAttribute("class", "property-header");
 
     this.matchedExpander = doc.createElementNS(HTML_NS, "div");
-    this.propertyHeader.appendChild(this.matchedExpander);
     this.matchedExpander.setAttribute("class", "match expander");
-
-    this.nameNode = doc.createElementNS(HTML_NS, "div");
-    this.propertyHeader.appendChild(this.nameNode);
-    this.nameNode.setAttribute("tabindex", "0");
-    this.nameNode.addEventListener("keydown", function(aEvent) {
+    this.matchedExpander.setAttribute("tabindex", "0");
+    this.matchedExpander.addEventListener("click",
+      this.matchedExpanderClick.bind(this), false);
+    this.matchedExpander.addEventListener("keydown", function(aEvent) {
       let keyEvent = Ci.nsIDOMKeyEvent;
       if (aEvent.keyCode == keyEvent.DOM_VK_F1) {
         this.mdnLinkClick();
       }
       if (aEvent.keyCode == keyEvent.DOM_VK_RETURN ||
-          aEvent.keyCode == keyEvent.DOM_VK_SPACE) {
-        this.propertyRowClick(aEvent);
+        aEvent.keyCode == keyEvent.DOM_VK_SPACE) {
+        this.matchedExpanderClick(aEvent);
       }
     }.bind(this), false);
+    this.propertyHeader.appendChild(this.matchedExpander);
 
+    this.nameNode = doc.createElementNS(HTML_NS, "div");
+    this.propertyHeader.appendChild(this.nameNode);
     this.nameNode.setAttribute("class", "property-name");
     this.nameNode.textContent = this.name;
+    this.nameNode.addEventListener("click", function(aEvent) {
+      this.matchedExpander.focus();
+    }.bind(this), false);
 
     let helpcontainer = doc.createElementNS(HTML_NS, "td");
     this.element.appendChild(helpcontainer);
     helpcontainer.setAttribute("class", "helplink-container");
 
     let helplink = doc.createElementNS(HTML_NS, "a");
     helpcontainer.appendChild(helplink);
     helplink.setAttribute("class", "helplink");
@@ -749,19 +965,19 @@ PropertyView.prototype = {
    * Refresh the panel matched rules.
    */
   refreshMatchedSelectors: function PropertyView_refreshMatchedSelectors()
   {
     let hasMatchedSelectors = this.hasMatchedSelectors;
     this.matchedSelectorsContainer.parentNode.hidden = !hasMatchedSelectors;
 
     if (hasMatchedSelectors) {
-      this.propertyHeader.parentNode.classList.add("expandable");
+      this.matchedExpander.classList.add("expandable");
     } else {
-      this.propertyHeader.parentNode.classList.remove("expandable");
+      this.matchedExpander.classList.remove("expandable");
     }
 
     if (this.matchedExpanded && hasMatchedSelectors) {
       CssHtmlTree.processTemplate(this.templateMatchedSelectors,
         this.matchedSelectorsContainer, this);
       this.matchedExpander.setAttribute("open", "");
     } else {
       this.matchedSelectorsContainer.innerHTML = "";
@@ -847,27 +1063,23 @@ PropertyView.prototype = {
 
     return this._unmatchedSelectorViews;
   },
 
   /**
    * The action when a user expands matched selectors.
    *
    * @param {Event} aEvent Used to determine the class name of the targets click
-   * event. If the class name is "helplink" then the event is allowed to bubble
-   * to the mdn link icon.
+   * event.
    */
-  propertyRowClick: function PropertyView_propertyRowClick(aEvent)
+  matchedExpanderClick: function PropertyView_matchedExpanderClick(aEvent)
   {
-    if (aEvent.target.className != "helplink") {
-      this.matchedExpanded = !this.matchedExpanded;
-      this.refreshAllSelectors();
-      this.nameNode.focus();
-      aEvent.preventDefault();
-    }
+    this.matchedExpanded = !this.matchedExpanded;
+    this.refreshAllSelectors();
+    aEvent.preventDefault();
   },
 
   /**
    * The action when a user expands unmatched selectors.
    */
   unmatchedSelectorsClick: function PropertyView_unmatchedSelectorsClick(aEvent)
   {
     this.unmatchedExpanded = !this.unmatchedExpanded;
@@ -981,16 +1193,24 @@ SelectorView.prototype = {
           aEvent.preventDefault();
         }.bind(this), false);
       result += ".style";
     }
 
     return result;
   },
 
+  maybeOpenStyleEditor: function(aEvent)
+  {
+    let keyEvent = Ci.nsIDOMKeyEvent;
+    if (aEvent.keyCode == keyEvent.DOM_VK_RETURN) {
+      this.openStyleEditor();
+    }
+  },
+
   /**
    * When a css link is clicked this method is called in order to either:
    *   1. Open the link in view source (for element style attributes).
    *   2. Open the link in the style editor.
    *
    * @param aEvent The click event
    */
   openStyleEditor: function(aEvent)
--- a/browser/devtools/styleinspector/CssLogic.jsm
+++ b/browser/devtools/styleinspector/CssLogic.jsm
@@ -922,17 +922,18 @@ CssLogic.shortSource = function CssLogic
   if (url.filePath) {
     return url.filePath;
   }
 
   if (url.query) {
     return url.query;
   }
 
-  return aSheet.href;
+  let dataUrl = aSheet.href.match(/^(data:[^,]*),/);
+  return dataUrl ? dataUrl[1] : aSheet.href;
 }
 
 /**
  * A safe way to access cached bits of information about a stylesheet.
  *
  * @constructor
  * @param {CssLogic} aCssLogic pointer to the CssLogic instance working with
  * this CssSheet object.
--- a/browser/devtools/styleinspector/CssRuleView.jsm
+++ b/browser/devtools/styleinspector/CssRuleView.jsm
@@ -18,16 +18,17 @@
  * The Initial Developer of the Original Code is
  * The Mozilla Foundation.
  * Portions created by the Initial Developer are Copyright (C) 2011
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
  *   Dave Camp <dcamp@mozilla.com> (Original Author)
  *   Rob Campbell <rcampbell@mozilla.com>
+ *   Mike Ratcliffe <mratcliffe@mozilla.com>
  *
  * Alternatively, the contents of this file may be used under the terms of
  * either the GNU General Public License Version 2 or later (the "GPL"), or
  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  * in which case the provisions of the GPL or the LGPL are applicable instead
  * of those above. If you wish to allow use of your version of this file only
  * under the terms of either the GPL or the LGPL, and not to allow others to
  * use your version of this file under the terms of the MPL, indicate your
@@ -802,34 +803,36 @@ CssRuleView.prototype = {
   /**
    * Creates editor UI for each of the rules in _elementStyle.
    */
   _createEditors: function CssRuleView_createEditors()
   {
     for each (let rule in this._elementStyle.rules) {
       // Don't hold a reference to this editor beyond the one held
       // by the node.
-      let editor = new RuleEditor(this.doc, rule);
+      let editor = new RuleEditor(this, rule);
       this.element.appendChild(editor.element);
     }
   },
 };
 
 /**
  * Create a RuleEditor.
  *
- * @param object aDoc
- *        The document holding this rule editor.
+ * @param CssRuleView aRuleView
+ *        The CssRuleView containg the document holding this rule editor and the
+ *        _selectionMode flag.
  * @param Rule aRule
  *        The Rule object we're editing.
  * @constructor
  */
-function RuleEditor(aDoc, aRule)
+function RuleEditor(aRuleView, aRule)
 {
-  this.doc = aDoc;
+  this.ruleView = aRuleView;
+  this.doc = this.ruleView.doc;
   this.rule = aRule;
 
   this._onNewProperty = this._onNewProperty.bind(this);
 
   this._create();
 }
 
 RuleEditor.prototype = {
@@ -888,18 +891,26 @@ RuleEditor.prototype = {
     this.closeBrace = createChild(code, "div", {
       class: "ruleview-ruleclose",
       tabindex: "0",
       textContent: "}"
     });
 
     // We made the close brace focusable, tabbing to it
     // or clicking on it should start the new property editor.
-    this.closeBrace.addEventListener("focus", function() {
-      this.newProperty();
+    this.closeBrace.addEventListener("focus", function(aEvent) {
+      if (!this.ruleView._selectionMode) {
+        this.newProperty();
+      }
+    }.bind(this), true);
+    this.closeBrace.addEventListener("mousedown", function(aEvent) {
+      aEvent.preventDefault();
+    }.bind(this), true);
+    this.closeBrace.addEventListener("click", function(aEvent) {
+      this.closeBrace.focus();
     }.bind(this), true);
   },
 
   /**
    * Create a text input for a property name.  If a non-empty property
    * name is given, we'll create a real TextProperty and add it to the
    * rule.
    */
@@ -1256,16 +1267,31 @@ TextPropertyEditor.prototype = {
  *       If any characters in advanceChars are typed, focus will advance
  *       to the next element.
  */
 function editableField(aOptions)
 {
   aOptions.element.addEventListener("focus", function() {
     new InplaceEditor(aOptions);
   }, false);
+
+  // In order to allow selection on the element, prevent focus on
+  // mousedown.  Focus on click instead.
+  aOptions.element.addEventListener("mousedown", function(evt) {
+    evt.preventDefault();
+  }, false);
+  aOptions.element.addEventListener("click", function(evt) {
+    let win = this.ownerDocument.defaultView;
+    let selection = win.getSelection();
+    if (selection.isCollapsed) {
+      aOptions.element.focus();
+    } else {
+      selection.removeAllRanges();
+    }
+  }, false);
 }
 var _editableField = editableField;
 
 function InplaceEditor(aOptions)
 {
   this.elt = aOptions.element;
   this.elt.inplaceEditor = this;
 
--- a/browser/devtools/styleinspector/StyleInspector.jsm
+++ b/browser/devtools/styleinspector/StyleInspector.jsm
@@ -50,17 +50,17 @@ var EXPORTED_SYMBOLS = ["StyleInspector"
  * StyleInspector Constructor Function.
  * @param {window} aContext, the chrome window context we're calling from.
  * @param {InspectorUI} aIUI (optional) An InspectorUI instance if called from the
  *        Highlighter.
  */
 function StyleInspector(aContext, aIUI)
 {
   this._init(aContext, aIUI);
-};
+}
 
 StyleInspector.prototype = {
 
   /**
    * Initialization method called from constructor.
    * @param {window} aContext, the chrome window context we're calling from.
    * @param {InspectorUI} aIUI (optional) An InspectorUI instance if called from
    *        the Highlighter.
--- a/browser/devtools/styleinspector/csshtmltree.xul
+++ b/browser/devtools/styleinspector/csshtmltree.xul
@@ -34,16 +34,17 @@
    - decision by deleting the provisions above and replace them with the notice
    - and other provisions required by the LGPL or the GPL. 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 ***** -->
 
 <?xml-stylesheet href="chrome://global/skin/global.css"?>
+<?xml-stylesheet href="chrome://browser/content/devtools/styleinspector.css" type="text/css"?>
 <?xml-stylesheet href="chrome://browser/skin/devtools/csshtmltree.css" type="text/css"?>
 
 <!DOCTYPE window [
   <!ENTITY % inspectorDTD SYSTEM "chrome://browser/locale/devtools/styleinspector.dtd">
   %inspectorDTD;
   <!ELEMENT loop ANY>
   <!ATTLIST li foreach CDATA #IMPLIED>
   <!ATTLIST div foreach CDATA #IMPLIED>
@@ -109,18 +110,21 @@ To visually debug the templates without 
   <div id="templateMatchedSelectors">
     <table>
       <loop foreach="selector in ${matchedSelectorViews}">
         <tr>
           <td dir="ltr" class="rule-text ${selector.statusClass}">
             ${selector.humanReadableText(__element)}
           </td>
           <td class="rule-link">
-            <a target="_blank" onclick="${selector.openStyleEditor}" class="link"
-               title="${selector.selectorInfo.href}">${selector.selectorInfo.source}</a>
+            <a target="_blank" class="link"
+               onclick="${selector.openStyleEditor}"
+               onkeydown="${selector.maybeOpenStyleEditor}"
+               title="${selector.selectorInfo.href}"
+               tabindex="0">${selector.selectorInfo.source}</a>
           </td>
         </tr>
       </loop>
     </table>
   </div>
 </div>
 
 </xul:window>
--- a/browser/devtools/styleinspector/styleinspector.css
+++ b/browser/devtools/styleinspector/styleinspector.css
@@ -30,21 +30,49 @@
  * 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 ***** */
 
+#root {
+  display: -moz-box;
+}
+
+.helplink {
+  display: block;
+}
+
+.expander,
+.property-name,
+.ruleview-propertyname,
+.ruleview-warning,
+.ruleview-expander {
+  display: inline-block;
+}
+
+#propertyContainer {
+  display: -moz-box;
+  -moz-box-orient: vertical;
+  -moz-box-flex: 1;
+  overflow-y: auto;
+  -moz-user-select: text;
+}
+
 .ruleview {
   overflow: auto;
+  -moz-user-select: text;
 }
 
-.ruleview-computedlist:not(.styleinspector-open) {
+.property-view-hidden,
+.property-content-hidden,
+.ruleview-computedlist:not(.styleinspector-open),
+.ruleview-warning[hidden] {
   display: none;
 }
 
 .ruleview-code {
   direction: ltr;
 }
 
 .ruleview-property:not(:hover) > .ruleview-enableproperty {
--- a/browser/devtools/styleinspector/test/Makefile.in
+++ b/browser/devtools/styleinspector/test/Makefile.in
@@ -60,16 +60,18 @@ include $(topsrcdir)/config/rules.mk
   browser_ruleview_manipulation.js \
   browser_ruleview_override.js \
   browser_ruleview_ui.js \
   browser_ruleview_focus.js \
   browser_bug705707_is_content_stylesheet.js \
   browser_bug722196_property_view_media_queries.js \
   browser_bug722196_rule_view_media_queries.js \
   browser_bug_592743_specificity.js \
+  browser_ruleview_bug_703643_context_menu_copy.js \
+  browser_computedview_bug_703643_context_menu_copy.js \
   head.js \
   $(NULL)
 
 _BROWSER_TEST_PAGES = \
   browser_bug683672.html \
   browser_bug705707_is_content_stylesheet.html \
   browser_bug705707_is_content_stylesheet_imported.css \
   browser_bug705707_is_content_stylesheet_imported2.css \
--- a/browser/devtools/styleinspector/test/browser_bug589375_keybindings.js
+++ b/browser/devtools/styleinspector/test/browser_bug589375_keybindings.js
@@ -50,36 +50,36 @@ function SI_test()
   Services.obs.removeObserver(SI_test, "StyleInspector-populated", false);
 
   info("checking keybindings");
 
   let iframe = stylePanel.iframe;
   let searchbar = stylePanel.cssHtmlTree.searchField;
   let propView = getFirstVisiblePropertyView();
   let rulesTable = propView.matchedSelectorsContainer;
-  let nameNode = propView.nameNode;
+  let matchedExpander = propView.matchedExpander;
 
-  info("Adding focus event handler to property name node");
-  nameNode.addEventListener("focus", function nameFocused() {
-    this.removeEventListener("focus", nameFocused);
-    info("property name is focused");
+  info("Adding focus event handler to property expander");
+  matchedExpander.addEventListener("focus", function expanderFocused() {
+    this.removeEventListener("focus", expanderFocused);
+    info("property expander is focused");
     info("checking expand / collapse");
     testKey(iframe.contentWindow, "VK_SPACE", rulesTable);
     testKey(iframe.contentWindow, "VK_RETURN", rulesTable);
 
     checkHelpLinkKeybinding();
     Services.obs.addObserver(finishUp, "StyleInspector-closed", false);
     stylePanel.close();
   });
 
   info("Adding focus event handler to search filter");
   searchbar.addEventListener("focus", function searchbarFocused() {
     this.removeEventListener("focus", searchbarFocused);
     info("search filter is focused");
-    info("tabbing to property name node");
+    info("tabbing to property expander node");
     EventUtils.synthesizeKey("VK_TAB", {}, iframe.contentWindow);
   });
 
   info("Making sure that the style inspector panel is focused");
   SimpleTest.waitForFocus(function windowFocused() {
     info("window is focused");
     info("focusing search filter");
     searchbar.focus();
new file mode 100644
--- /dev/null
+++ b/browser/devtools/styleinspector/test/browser_computedview_bug_703643_context_menu_copy.js
@@ -0,0 +1,163 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Tests that the style inspector works properly
+
+let doc;
+let stylePanel;
+let cssHtmlTree;
+
+function createDocument()
+{
+  doc.body.innerHTML = '<style type="text/css"> ' +
+    'span { font-variant: small-caps; color: #000000; } ' +
+    '.nomatches {color: #ff0000;}</style> <div id="first" style="margin: 10em; ' +
+    'font-size: 14pt; font-family: helvetica, sans-serif; color: #AAA">\n' +
+    '<h1>Some header text</h1>\n' +
+    '<p id="salutation" style="font-size: 12pt">hi.</p>\n' +
+    '<p id="body" style="font-size: 12pt">I am a test-case. This text exists ' +
+    'solely to provide some things to <span style="color: yellow">' +
+    'highlight</span> and <span style="font-weight: bold">count</span> ' +
+    'style list-items in the box at right. If you are reading this, ' +
+    'you should go do something else instead. Maybe read a book. Or better ' +
+    'yet, write some test-cases for another bit of code. ' +
+    '<span style="font-style: italic">some text</span></p>\n' +
+    '<p id="closing">more text</p>\n' +
+    '<p>even more text</p>' +
+    '</div>';
+  doc.title = "Computed view context menu test";
+
+  let span = doc.querySelector("span");
+  ok(span, "captain, we have the span");
+
+  stylePanel = new StyleInspector(window);
+  Services.obs.addObserver(runStyleInspectorTests, "StyleInspector-populated", false);
+  stylePanel.createPanel(false, function() {
+    stylePanel.open(span);
+  });
+}
+
+function runStyleInspectorTests()
+{
+  Services.obs.removeObserver(runStyleInspectorTests, "StyleInspector-populated", false);
+
+  ok(stylePanel.isOpen(), "style inspector is open");
+
+  cssHtmlTree = stylePanel.cssHtmlTree;
+
+  let contentDocument = stylePanel.iframe.contentDocument;
+  let prop = contentDocument.querySelector(".property-view");
+  ok(prop, "captain, we have the property-view node");
+
+  // We need the context menu to open in the correct place in order for
+  // popupNode to be propertly set.
+  EventUtils.synthesizeMouse(prop, 1, 1, { type: "contextmenu", button: 2 },
+    stylePanel.iframe.contentWindow);
+
+  checkCopyProperty()
+}
+
+function checkCopyProperty()
+{
+  info("Checking that cssHtmlTree.siBoundCopyDeclaration() returns the " +
+       "correct clipboard value");
+  let expectedPattern = "color: rgb\\(255, 255, 0\\);";
+  info("Expected pattern: " + expectedPattern);
+
+  SimpleTest.waitForClipboard(function CS_boundCopyPropCheck() {
+      return checkClipboardData(expectedPattern);
+    },
+    cssHtmlTree.siBoundCopyDeclaration,
+    checkCopyPropertyName, checkCopyPropertyName);
+}
+
+function checkCopyPropertyName()
+{
+  info("Checking that cssHtmlTree.siBoundCopyProperty() returns the " +
+       "correct clipboard value");
+  let expectedPattern = "color";
+  info("Expected pattern: " + expectedPattern);
+
+  SimpleTest.waitForClipboard(function CS_boundCopyPropNameCheck() {
+      return checkClipboardData(expectedPattern);
+    },
+    cssHtmlTree.siBoundCopyProperty,
+    checkCopyPropertyValue, checkCopyPropertyValue);
+}
+
+function checkCopyPropertyValue()
+{
+  info("Checking that cssHtmlTree.siBoundCopyPropertyValue() returns the " +
+       "correct clipboard value");
+  let expectedPattern = "rgb\\(255, 255, 0\\)";
+  info("Expected pattern: " + expectedPattern);
+
+  SimpleTest.waitForClipboard(function CS_boundCopyPropValueCheck() {
+      return checkClipboardData(expectedPattern);
+    },
+    cssHtmlTree.siBoundCopyPropertyValue,
+    checkCopySelection, checkCopySelection);
+}
+
+function checkCopySelection()
+{
+  let contentDocument = stylePanel.iframe.contentDocument;
+  let contentWindow = stylePanel.iframe.contentWindow;
+  let props = contentDocument.querySelectorAll(".property-view");
+  ok(props, "captain, we have the property-view nodes");
+
+  let range = document.createRange();
+  range.setStart(props[0], 0);
+  range.setEnd(props[3], 3);
+  contentWindow.getSelection().addRange(range);
+
+  info("Checking that cssHtmlTree.siBoundCopyPropertyValue() " +
+       " returns the correct clipboard value");
+
+  let expectedPattern = "color: rgb\\(255, 255, 0\\)[\\r\\n]+" +
+                 "font-family: helvetica,sans-serif[\\r\\n]+" +
+                 "font-size: 16px[\\r\\n]+" +
+                 "font-variant: small-caps[\\r\\n]*";
+  info("Expected pattern: " + expectedPattern);
+
+  SimpleTest.waitForClipboard(function CS_boundCopyCheck() {
+      return checkClipboardData(expectedPattern);
+    },
+    cssHtmlTree.siBoundCopy, closeStyleInspector, closeStyleInspector);
+}
+
+function checkClipboardData(aExpectedPattern)
+{
+  let actual = SpecialPowers.getClipboardData("text/unicode");
+  let expectedRegExp = new RegExp(aExpectedPattern, "g");
+  return expectedRegExp.test(actual);
+}
+
+function closeStyleInspector()
+{
+  Services.obs.addObserver(finishUp, "StyleInspector-closed", false);
+  stylePanel.close();
+}
+
+function finishUp()
+{
+  Services.obs.removeObserver(finishUp, "StyleInspector-closed", false);
+  ok(!stylePanel.isOpen(), "style inspector is closed");
+  doc = stylePanel = cssHtmlTree = null;
+  gBrowser.removeCurrentTab();
+  finish();
+}
+
+function test()
+{
+  waitForExplicitFinish();
+  gBrowser.selectedTab = gBrowser.addTab();
+  gBrowser.selectedBrowser.addEventListener("load", function(evt) {
+    gBrowser.selectedBrowser.removeEventListener(evt.type, arguments.callee, true);
+    doc = content.document;
+    waitForFocus(createDocument, content);
+  }, true);
+
+  content.location = "data:text/html,computed view context menu test";
+}
new file mode 100644
--- /dev/null
+++ b/browser/devtools/styleinspector/test/browser_ruleview_bug_703643_context_menu_copy.js
@@ -0,0 +1,209 @@
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+let doc;
+
+function createDocument()
+{
+  doc.body.innerHTML = '<style type="text/css"> ' +
+    'html { color: #000000; } ' +
+    'span { font-variant: small-caps; color: #000000; } ' +
+    '.nomatches {color: #ff0000;}</style> <div id="first" style="margin: 10em; ' +
+    'font-size: 14pt; font-family: helvetica, sans-serif; color: #AAA">\n' +
+    '<h1>Some header text</h1>\n' +
+    '<p id="salutation" style="font-size: 12pt">hi.</p>\n' +
+    '<p id="body" style="font-size: 12pt">I am a test-case. This text exists ' +
+    'solely to provide some things to <span style="color: yellow">' +
+    'highlight</span> and <span style="font-weight: bold">count</span> ' +
+    'style list-items in the box at right. If you are reading this, ' +
+    'you should go do something else instead. Maybe read a book. Or better ' +
+    'yet, write some test-cases for another bit of code. ' +
+    '<span style="font-style: italic">some text</span></p>\n' +
+    '<p id="closing">more text</p>\n' +
+    '<p>even more text</p>' +
+    '</div>';
+  doc.title = "Rule view context menu test";
+
+  openInspector();
+}
+
+function openInspector()
+{
+  ok(window.InspectorUI, "InspectorUI variable exists");
+  ok(!InspectorUI.inspecting, "Inspector is not highlighting");
+  ok(InspectorUI.store.isEmpty(), "Inspector.store is empty");
+
+  Services.obs.addObserver(inspectorUIOpen,
+    InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false);
+  InspectorUI.openInspectorUI();
+}
+
+function inspectorUIOpen()
+{
+  Services.obs.removeObserver(inspectorUIOpen,
+    InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false);
+
+  // Make sure the inspector is open.
+  ok(InspectorUI.inspecting, "Inspector is highlighting");
+  ok(!InspectorUI.treePanel.isOpen(), "Inspector Tree Panel is not open");
+  ok(!InspectorUI.isSidebarOpen, "Inspector Sidebar is not open");
+  ok(!InspectorUI.store.isEmpty(), "InspectorUI.store is not empty");
+  is(InspectorUI.store.length, 1, "Inspector.store.length = 1");
+
+  // Highlight a node.
+  let div = content.document.getElementsByTagName("div")[0];
+  InspectorUI.inspectNode(div);
+  InspectorUI.stopInspecting();
+  is(InspectorUI.selection, div, "selection matches the div element");
+
+  Services.obs.addObserver(testClip,
+    InspectorUI.INSPECTOR_NOTIFICATIONS.RULEVIEWREADY, false);
+
+  InspectorUI.showSidebar();
+  InspectorUI.openRuleView();
+}
+
+function testClip()
+{
+  Services.obs.removeObserver(testClip,
+    InspectorUI.INSPECTOR_NOTIFICATIONS.RULEVIEWREADY, false);
+
+  executeSoon(function() {
+    info("Checking that InspectorUI.ruleViewCopyRule() returns " +
+         "the correct clipboard value");
+    let expectedPattern = "element {[\\r\\n]+" +
+      "    margin: 10em;[\\r\\n]+" +
+      "    font-size: 14pt;[\\r\\n]+" +
+      "    font-family: helvetica,sans-serif;[\\r\\n]+" +
+      "    color: rgb\\(170, 170, 170\\);[\\r\\n]+" +
+      "}[\\r\\n]*";
+    info("Expected pattern: " + expectedPattern);
+
+    SimpleTest.waitForClipboard(function IUI_boundCopyPropCheck() {
+        return checkClipboardData(expectedPattern);
+      },
+      checkCopyRule, checkCopyProperty, checkCopyProperty);
+  });
+}
+
+function checkCopyRule() {
+  let ruleView = document.querySelector("#devtools-sidebar-iframe-ruleview");
+  let contentDoc = ruleView.contentDocument;
+  let props = contentDoc.querySelectorAll(".ruleview-property");
+
+  is(props.length, 5, "checking property length");
+
+  let prop = props[2];
+  let propName = prop.querySelector(".ruleview-propertyname").textContent;
+  let propValue = prop.querySelector(".ruleview-propertyvalue").textContent;
+
+  is(propName, "font-family", "checking property name");
+  is(propValue, "helvetica,sans-serif", "checking property value");
+
+  // We need the context menu to open in the correct place in order for
+  // popupNode to be propertly set.
+  EventUtils.synthesizeMouse(prop, 1, 1, { type: "contextmenu", button: 2 },
+    ruleView.contentWindow);
+
+  InspectorUI.ruleViewCopyRule();
+}
+
+function checkCopyProperty()
+{
+  info("Checking that InspectorUI.cssRuleViewBoundCopyDeclaration() returns " +
+       "the correct clipboard value");
+  let expectedPattern = "font-family: helvetica,sans-serif;";
+  info("Expected pattern: " + expectedPattern);
+
+  SimpleTest.waitForClipboard(function IUI_boundCopyPropCheck() {
+      return checkClipboardData(expectedPattern);
+    },
+    InspectorUI.cssRuleViewBoundCopyDeclaration,
+    checkCopyPropertyName, checkCopyPropertyName);
+}
+
+function checkCopyPropertyName()
+{
+  info("Checking that InspectorUI.cssRuleViewBoundCopyProperty() returns " +
+       "the correct clipboard value");
+  let expectedPattern = "font-family";
+  info("Expected pattern: " + expectedPattern);
+
+  SimpleTest.waitForClipboard(function IUI_boundCopyPropNameCheck() {
+      return checkClipboardData(expectedPattern);
+    },
+    InspectorUI.cssRuleViewBoundCopyProperty,
+    checkCopyPropertyValue, checkCopyPropertyValue);
+}
+
+function checkCopyPropertyValue()
+{
+  info("Checking that InspectorUI.cssRuleViewBoundCopyPropertyValue() " +
+       " returns the correct clipboard value");
+  let expectedPattern = "helvetica,sans-serif";
+  info("Expected pattern: " + expectedPattern);
+
+  SimpleTest.waitForClipboard(function IUI_boundCopyPropValueCheck() {
+      return checkClipboardData(expectedPattern);
+    },
+    InspectorUI.cssRuleViewBoundCopyPropertyValue,
+    checkCopySelection, checkCopySelection);
+}
+
+function checkCopySelection()
+{
+  let ruleView = document.querySelector("#devtools-sidebar-iframe-ruleview");
+  let contentDoc = ruleView.contentDocument;
+  let props = contentDoc.querySelectorAll(".ruleview-property");
+
+  let range = document.createRange();
+  range.setStart(props[0], 0);
+  range.setEnd(props[4], 8);
+  ruleView.contentWindow.getSelection().addRange(range);
+
+  info("Checking that InspectorUI.cssRuleViewBoundCopy()  returns the correct" +
+       "clipboard value");
+  let expectedPattern = "    margin: 10em;[\\r\\n]+" +
+                        "    font-size: 14pt;[\\r\\n]+" +
+                        "    font-family: helvetica,sans-serif;[\\r\\n]+" +
+                        "    color: rgb\\(170, 170, 170\\);[\\r\\n]+" +
+                        "}[\\r\\n]+" +
+                        "html {[\\r\\n]+" +
+                        "    color: rgb\\(0, 0, 0\\);[\\r\\n]*";
+  info("Expected pattern: " + expectedPattern);
+
+  SimpleTest.waitForClipboard(function IUI_boundCopyCheck() {
+      return checkClipboardData(expectedPattern);
+    },InspectorUI.cssRuleViewBoundCopy, finishup, finishup);
+}
+
+function checkClipboardData(aExpectedPattern)
+{
+  let actual = SpecialPowers.getClipboardData("text/unicode");
+  let expectedRegExp = new RegExp(aExpectedPattern, "g");
+  return expectedRegExp.test(actual);
+}
+
+function finishup()
+{
+  InspectorUI.closeInspectorUI();
+  gBrowser.removeCurrentTab();
+  doc = null;
+  finish();
+}
+
+function test()
+{
+  waitForExplicitFinish();
+
+  gBrowser.selectedTab = gBrowser.addTab();
+  gBrowser.selectedBrowser.addEventListener("load", function(evt) {
+    gBrowser.selectedBrowser.removeEventListener(evt.type, arguments.callee,
+      true);
+    doc = content.document;
+    waitForFocus(createDocument, content);
+  }, true);
+
+  content.location = "data:text/html,<p>rule view context menu test</p>";
+}
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -139,16 +139,17 @@
 @BINPATH@/components/content_htmldoc.xpt
 @BINPATH@/components/content_html.xpt
 @BINPATH@/components/content_xslt.xpt
 @BINPATH@/components/content_xtf.xpt
 @BINPATH@/components/cookie.xpt
 @BINPATH@/components/directory.xpt
 @BINPATH@/components/docshell.xpt
 @BINPATH@/components/dom.xpt
+@BINPATH@/components/dom_apps.xpt
 @BINPATH@/components/dom_base.xpt
 #ifdef MOZ_B2G_RIL
 @BINPATH@/components/dom_telephony.xpt
 @BINPATH@/components/dom_wifi.xpt
 @BINPATH@/components/dom_system_b2g.xpt
 #endif
 @BINPATH@/components/dom_battery.xpt
 #ifdef MOZ_B2G_BT
@@ -405,16 +406,18 @@
 #ifdef MOZ_SERVICES_SYNC
 @BINPATH@/components/SyncComponents.manifest
 @BINPATH@/components/Weave.js
 #endif
 @BINPATH@/components/TelemetryPing.js
 @BINPATH@/components/TelemetryPing.manifest
 @BINPATH@/components/messageWakeupService.js
 @BINPATH@/components/messageWakeupService.manifest
+@BINPATH@/components/Webapps.js
+@BINPATH@/components/Webapps.manifest
 
 @BINPATH@/components/ContactManager.js
 @BINPATH@/components/ContactManager.manifest
 
 ; Modules
 @BINPATH@/modules/*
 
 ; Safe Browsing
--- a/browser/locales/en-US/chrome/browser/browser.dtd
+++ b/browser/locales/en-US/chrome/browser/browser.dtd
@@ -208,18 +208,16 @@ can reach it easily. -->
   -  "Scratchpad" in your locale. You should feel free to find a close
   -  approximation to it or choose a word (or words) that means
   -  "simple discardable text editor". -->
 <!ENTITY scratchpad.label             "Scratchpad">
 <!ENTITY scratchpad.accesskey         "s">
 <!ENTITY scratchpad.keycode           "VK_F4">
 <!ENTITY scratchpad.keytext           "F4">
 
-<!ENTITY inspectButton.label            "Inspect">
-<!ENTITY inspectButton.accesskey        "I">
 <!ENTITY inspectCloseButton.tooltiptext "Close Inspector">
 
 <!ENTITY inspectorHTMLCopyInner.label       "Copy Inner HTML">
 <!ENTITY inspectorHTMLCopyInner.accesskey   "I">
 
 <!ENTITY inspectorHTMLCopyOuter.label       "Copy Outer HTML">
 <!ENTITY inspectorHTMLCopyOuter.accesskey   "O">
 
--- a/browser/locales/en-US/chrome/browser/browser.properties
+++ b/browser/locales/en-US/chrome/browser/browser.properties
@@ -329,16 +329,22 @@ syncPromoNotification.learnMoreLinkText=
 # and %2$S by the value of the toolkit.telemetry.server_owner preference.
 telemetryPrompt = Will you help improve %1$S by sending anonymous information about performance, hardware characteristics, feature usage, and browser customizations to %2$S?
 telemetryLinkLabel = Learn More
 telemetryYesButtonLabel2 = Yes, I want to help
 telemetryYesButtonAccessKey = Y
 telemetryNoButtonLabel = No
 telemetryNoButtonAccessKey = N
 
+# Webapps notification popup
+webapps.install = Install
+webapps.install.accesskey = I
+#LOCALIZATION NOTE (webapps.requestInstall) %1$S is the web app name, %2$S is the site from which the web app is installed
+webapps.requestInstall = Do you want to install "%1$S" from this site (%2$S)?
+
 # Keyword.URL reset prompt
 # LOCALIZATION NOTE (keywordPrompt.message):
 #  - %1$S is brandShortName
 #  - %2$S is a host name (e.g. "somewebsearch.com") from the current value of keyword.URL
 #  - %3$S is the name of the default search engine (e.g. "Google")
 keywordPrompt.message = %1$S is using '%2$S' for searches from the location bar. Would you like to restore the default search (%3$S)?
 
 # LOCALIZATION NOTE (keywordPrompt.yesButton): %1$S is the name of the default search engine
--- a/browser/locales/en-US/chrome/browser/devtools/inspector.properties
+++ b/browser/locales/en-US/chrome/browser/devtools/inspector.properties
@@ -19,9 +19,16 @@ confirmNavigationAway.buttonStayAccesske
 breadcrumbs.siblings=Siblings
 # LOCALIZATION NOTE (htmlPanel): Used in the Inspector tool's openInspectorUI
 # method when registering the HTML panel.
 
 # LOCALIZATION NOTE (ruleView.*): Button label, accesskey and tooltip text
 # associated with the Highlighter's CSS Rule View in the Style Sidebar.
 ruleView.label=Rules
 ruleView.accesskey=R
-ruleView.tooltiptext=View and Edit CSS
\ No newline at end of file
+ruleView.tooltiptext=View and Edit CSS
+
+# LOCALIZATION NOTE (inspectButton.tooltiptext):
+# This button appears in the Inspector Toolbar. inspectButton is stateful,
+# if it's pressed users can select an element with the mouse. Pressing the
+# "Return" key # changes that state. %S is the keyboard shortcut (VK_RETURN in
+# chrome://global/locale/keys.properties).
+inspectButton.tooltiptext=Select element with mouse (%S)
--- a/browser/locales/en-US/chrome/browser/devtools/styleinspector.properties
+++ b/browser/locales/en-US/chrome/browser/devtools/styleinspector.properties
@@ -44,8 +44,80 @@ style.highlighter.button.tooltip2=Inspec
 # quickly jump to the documentation from the Mozilla Developer Network site.
 # This is the link title shown in the hover tooltip.
 helpLinkTitle=Read the documentation for this property
 
 # LOCALIZATION NOTE (rule.warning.title): When an invalid property value is
 # entered into the rule view a warning icon is displayed. This text is used for
 # the title attribute of the warning icon.
 rule.warning.title=Invalid property value
+
+# LOCALIZATION NOTE (style.contextmenu.copyselection): The computed view's
+# context menu copy entry.
+style.contextmenu.copyselection=Copy selection
+
+# LOCALIZATION NOTE (style.contextmenu.copyselection.accesskey): The computed
+# view's context menu copy entry access key.
+style.contextmenu.copyselection.accesskey=C
+
+# LOCALIZATION NOTE (style.contextmenu.copydeclaration): The style inspector's
+# context menu copy property entry allows a complete CSS property to be copied.
+style.contextmenu.copydeclaration=Copy declaration line
+
+# LOCALIZATION NOTE (style.contextmenu.copydeclaration.accesskey): The style
+# inspector's context menu copy property access key.
+style.contextmenu.copydeclaration.accesskey=D
+
+# LOCALIZATION NOTE (style.contextmenu.copyproperty): The style inspector's
+# context menu copy property name entry allows a CSS property name to be copied.
+style.contextmenu.copyproperty=Copy property
+
+# LOCALIZATION NOTE (style.contextmenu.copyproperty.accesskey): The style
+# inspector's context menu copy property name access key.
+style.contextmenu.copyproperty.accesskey=P
+
+# LOCALIZATION NOTE (style.contextmenu.copypropertyvalue): The style inspector's
+# context menu copy property value entry allows a CSS property name to be copied.
+style.contextmenu.copypropertyvalue=Copy property value
+
+# LOCALIZATION NOTE (style.contextmenu.copypropertyvalue.accesskey): The style
+# inspector's context menu copy property value access key.
+style.contextmenu.copypropertyvalue.accesskey=U
+
+# LOCALIZATION NOTE (rule.contextmenu.copyselection): The rule view's context
+# menu copy entry.
+rule.contextmenu.copyselection=Copy selection
+
+# LOCALIZATION NOTE (rule.contextmenu.copyselection.accesskey): The rule view's
+# context menu copy entry access key.
+rule.contextmenu.copyselection.accesskey=C
+
+# LOCALIZATION NOTE (rule.contextmenu.copyrule): The rule view's context menu
+# copy rule entry allows a complete CSS rule to be copied.
+rule.contextmenu.copyrule=Copy rule
+
+# LOCALIZATION NOTE (rule.contextmenu.copyrule.accesskey): The rule view's
+# context menu copy rule access key.
+rule.contextmenu.copyrule.accesskey=R
+
+# LOCALIZATION NOTE (rule.contextmenu.copydeclaration): The rule view's context
+# menu copy property entry allows a complete CSS property to be copied.
+rule.contextmenu.copydeclaration=Copy declaration line
+
+# LOCALIZATION NOTE (rule.contextmenu.copydeclaration.accesskey): The rule view's
+# context menu copy property access key.
+rule.contextmenu.copydeclaration.accesskey=D
+
+# LOCALIZATION NOTE (rule.contextmenu.copyproperty): The rule view's context
+# menu copy property entry allows a CSS property name to be copied.
+rule.contextmenu.copyproperty=Copy property
+
+# LOCALIZATION NOTE (rule.contextmenu.copyproperty.accesskey): The rule
+# view's context menu copy property name access key.
+rule.contextmenu.copyproperty.accesskey=P
+
+# LOCALIZATION NOTE (rule.contextmenu.copypropertyvalue): The rule view's
+# context menu copy property entry allows a CSS property value to be copied.
+rule.contextmenu.copypropertyvalue=Copy property value
+
+# LOCALIZATION NOTE (rule.contextmenu.copypropertyvalue.accesskey): The rule
+# view's context menu copy property value access key.
+rule.contextmenu.copypropertyvalue.accesskey=U
--- a/browser/locales/en-US/chrome/browser/newTab.dtd
+++ b/browser/locales/en-US/chrome/browser/newTab.dtd
@@ -1,6 +1,2 @@
 <!-- These strings are used in the about:newtab page -->
 <!ENTITY newtab.pageTitle "New Tab">
-
-<!ENTITY newtab.show "Show the New Tab Page">
-<!ENTITY newtab.hide "Hide the New Tab Page">
-<!ENTITY newtab.reset "Reset the New Tab Page">
--- a/browser/locales/en-US/chrome/browser/newTab.properties
+++ b/browser/locales/en-US/chrome/browser/newTab.properties
@@ -1,3 +1,5 @@
 newtab.pin=Pin this site at its current position
 newtab.unpin=Unpin this site
 newtab.block=Remove this site
+newtab.show=Show the new tab page
+newtab.hide=Hide the new tab page
--- a/browser/modules/Makefile.in
+++ b/browser/modules/Makefile.in
@@ -48,16 +48,17 @@ TEST_DIRS += test
 
 EXTRA_JS_MODULES = \
 	openLocationLastURL.jsm \
 	NetworkPrioritizer.jsm \
 	NewTabUtils.jsm \
 	offlineAppCache.jsm \
 	TelemetryTimestamps.jsm \
 	KeywordURLResetPrompter.jsm \
+	webappsUI.jsm \
 	$(NULL)
 
 ifeq ($(MOZ_WIDGET_TOOLKIT),windows) 
 EXTRA_JS_MODULES += \
 	WindowsPreviewPerTab.jsm \
 	WindowsJumpLists.jsm \
 	$(NULL)
 endif
new file mode 100644
--- /dev/null
+++ b/browser/modules/webappsUI.jsm
@@ -0,0 +1,133 @@
+/* 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/. */
+
+let EXPORTED_SYMBOLS = ["webappsUI"];
+
+let Ci = Components.interfaces;
+let Cc = Components.classes;
+let Cu = Components.utils;
+
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/Webapps.jsm");
+
+let webappsUI = {
+  init: function webappsUI_init() {
+    Services.obs.addObserver(this, "webapps-ask-install", false);
+    Services.obs.addObserver(this, "webapps-launch", false);
+  },
+  
+  uninit: function webappsUI_uninit() {
+    Services.obs.removeObserver(this, "webapps-ask-install");
+    Services.obs.removeObserver(this, "webapps-launch");
+  },
+
+  observe: function webappsUI_observe(aSubject, aTopic, aData) {
+    let data = JSON.parse(aData);
+
+    switch(aTopic) {
+      case "webapps-ask-install":
+        let [chromeWin, browser] = this._getBrowserForId(data.oid);
+        if (chromeWin)
+          this.doInstall(data, browser, chromeWin);
+        break;
+      case "webapps-launch":
+        DOMApplicationRegistry.getManifestFor(data.origin, (function(aManifest) {
+          if (!aManifest)
+            return;
+          let manifest = new DOMApplicationManifest(aManifest, data.origin);
+          this.openURL(manifest.fullLaunchPath(), data.origin);
+        }).bind(this));
+        break;
+    }
+  },
+
+  openURL: function(aUrl, aOrigin) {
+    let browserEnumerator = Services.wm.getEnumerator("navigator:browser");  
+    let ss = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore);
+
+    // Check each browser instance for our URL
+    let found = false;
+    while (!found && browserEnumerator.hasMoreElements()) {
+      let browserWin = browserEnumerator.getNext();
+      let tabbrowser = browserWin.gBrowser;
+
+      // Check each tab of this browser instance
+      let numTabs = tabbrowser.tabs.length;
+      for (let index = 0; index < numTabs; index++) {
+        let tab = tabbrowser.tabs[index];
+        let appURL = ss.getTabValue(tab, "appOrigin");
+        if (appURL == aOrigin) {
+          // The URL is already opened. Select this tab.
+          tabbrowser.selectedTab = tab;
+          browserWin.focus();
+          found = true;
+          break;
+        }
+      }
+    }
+
+    // Our URL isn't open. Open it now.
+    if (!found) {
+      let recentWindow = Services.wm.getMostRecentWindow("navigator:browser");
+      if (recentWindow) {
+        // Use an existing browser window
+        let browser = recentWindow.gBrowser;
+        let tab = browser.addTab(aUrl);
+        browser.pinTab(tab);
+        browser.selectedTab = tab;
+        ss.setTabValue(tab, "appOrigin", aOrigin);
+      }
+    }
+  },
+
+  _getBrowserForId: function(aId) {
+    let someWindow = Services.wm.getMostRecentWindow(null);
+
+    if (someWindow) {
+      let windowUtils = someWindow.QueryInterface(Ci.nsIInterfaceRequestor)
+                                  .getInterface(Ci.nsIDOMWindowUtils);
+      let content = windowUtils.getOuterWindowWithId(aId);
+      if (content) {
+        let browser = content.QueryInterface(Ci.nsIInterfaceRequestor)
+                      .getInterface(Ci.nsIWebNavigation)
+                      .QueryInterface(Ci.nsIDocShell).chromeEventHandler;
+        let win = browser.ownerDocument.defaultView;
+        return [win, browser];
+      }
+    }
+
+    return [null, null];
+  },
+
+  doInstall: function(aData, aBrowser, aWindow) {
+    let bundle = aWindow.gNavigatorBundle;
+
+    let mainAction = {
+      label: bundle.getString("webapps.install"),
+      accessKey: bundle.getString("webapps.install.accesskey"),
+      callback: function(notification) {
+        installDone = true;
+        DOMApplicationRegistry.confirmInstall(aData);
+      }
+    };
+
+    let requestingURI = aWindow.makeURI(aData.from);
+    let manifest = new DOMApplicationManifest(aData.app.manifest, aData.app.origin);
+
+    let host;
+    try {
+      host = requestingURI.host;
+    } catch(e) {
+      host = requestingURI.spec;
+    }
+
+    let message = bundle.getFormattedString("webapps.requestInstall",
+                                            [manifest.name, host], 2);
+
+    aWindow.PopupNotifications.show(aBrowser, "webapps-install", message, "webapps-notification-icon",
+                                                       mainAction, null, { popupIconURL: manifest.iconURLForSize(64) });
+
+  }
+}
--- a/browser/themes/gnomestripe/browser.css
+++ b/browser/themes/gnomestripe/browser.css
@@ -1225,16 +1225,20 @@ toolbar[iconsize="small"] #feed-button {
   list-style-image: url(chrome://global/skin/icons/question-64.png);
 }
 
 .popup-notification-icon[popupid="password-save"],
 .popup-notification-icon[popupid="password-change"] {
   list-style-image: url(chrome://mozapps/skin/passwordmgr/key-64.png);
 }
 
+.popup-notification-icon[popupid="webapps-install"] {
+  list-style-image: url(chrome://browser/skin/webapps-64.png);
+}
+
 /* Notification icon box */
 #notification-popup-box {
   position: relative;
   background-color: #fff;
   background-clip: padding-box;
   padding-left: 4px;
   border-radius: 2.5px 0 0 2.5px;
   border-width: 0 8px 0 0;
@@ -1279,16 +1283,20 @@ toolbar[iconsize="small"] #feed-button {
 #indexedDB-notification-icon {
   list-style-image: url(chrome://global/skin/icons/question-16.png);
 }
 
 #password-notification-icon {
   list-style-image: url(chrome://mozapps/skin/passwordmgr/key-16.png);
 }
 
+#webapps-notification-icon {
+  list-style-image: url(chrome://browser/skin/webapps-16.png);
+}
+
 #treecolAutoCompleteImage {
   max-width : 36px; 
 }
 
 .ac-result-type-bookmark,
 .autocomplete-treebody::-moz-tree-image(bookmark, treecolAutoCompleteImage) {
   list-style-image: url("chrome://browser/skin/places/pageStarred.png");
   width: 16px;
@@ -1983,16 +1991,25 @@ panel[dimmed="true"] {
 
 #highlighter-veil-container[locked] > #highlighter-veil-middlebox > #highlighter-veil-transparentbox {
   box-shadow: 0 0 0 1px black;
   outline-color: white;
 }
 
 /* Highlighter toolbar */
 
+#inspector-inspect-toolbutton {
+  list-style-image: url("chrome://browser/skin/devtools/inspect-button.png");
+  -moz-image-region: rect(0px 16px 16px 0px);
+}
+
+#inspector-inspect-toolbutton[checked] {
+  -moz-image-region: rect(0px 32px 16px 16px);
+}
+
 #inspector-toolbar {
   border-top: 1px solid hsla(210, 8%, 5%, .65);
 }
 
 #devtools-side-splitter {
   -moz-appearance: none;
   border: 0;
   -moz-border-start: 1px solid #242b33;
--- a/browser/themes/gnomestripe/devtools/common.css
+++ b/browser/themes/gnomestripe/devtools/common.css
@@ -52,16 +52,20 @@
   text-shadow: 0 -1px 0 hsla(210,8%,5%,.45);
   border: 1px solid hsla(210,8%,5%,.45);
   border-radius: 3px;
   background: -moz-linear-gradient(hsla(212,7%,57%,.35), hsla(212,7%,57%,.1)) padding-box;
   box-shadow: 0 1px 0 hsla(210,16%,76%,.15) inset, 0 0 0 1px hsla(210,16%,76%,.15) inset, 0 1px 0 hsla(210,16%,76%,.15);
   margin: 0 3px;
 }
 
+.devtools-toolbarbutton:not([label]) {
+  min-width: 32px;
+}
+
 .devtools-toolbarbutton:not([checked]):hover:active {
   border-color: hsla(210,8%,5%,.6);
   background: -moz-linear-gradient(hsla(220,6%,10%,.3), hsla(212,7%,57%,.15) 65%, hsla(212,7%,57%,.3));
   box-shadow: 0 0 3px hsla(210,8%,5%,.25) inset, 0 1px 3px hsla(210,8%,5%,.25) inset, 0 1px 0 hsla(210,16%,76%,.15);
 }
 
 .devtools-toolbarbutton[checked] {
   color: hsl(208,100%,60%) !important;
--- a/browser/themes/gnomestripe/devtools/csshtmltree.css
+++ b/browser/themes/gnomestripe/devtools/csshtmltree.css
@@ -40,21 +40,16 @@
  * ***** END LICENSE BLOCK ***** */
 
 :root {
   -moz-appearance: none;
   background: -moz-Field;
   color: -moz-FieldText;
 }
 
-#root {
-  display: -moz-box;
-}
-
-
 .property-header {
   padding: 5px 0;
   white-space: nowrap;
   vertical-align: text-top;
 }
 
 /* Take away these two :visited rules to get a core dumper     */
 /* See https://bugzilla.mozilla.org/show_bug.cgi?id=575675#c30 */
@@ -68,17 +63,16 @@
 .helplink:visited {
   text-decoration: none;
 }
 .link:hover {
   text-decoration: underline;
 }
 
 .helplink {
-  display: block;
   height: 14px;
   width: 0;
   overflow: hidden;
   -moz-padding-start: 14px;
   background-image: url("chrome://browser/skin/devtools/goto-mdn.png");
   -moz-margin-end: 2px;
   cursor: pointer;
 }
@@ -92,54 +86,44 @@
   padding: 0;
 }
 
 .expander {
   -moz-appearance: treetwisty;
   padding-top: 12px;
   -moz-margin-start: 5px;
   -moz-margin-end: 5px;
-  display: inline-block;
   vertical-align: middle;
 }
 
 .expander[open] {
   -moz-appearance: treetwistyopen;
 }
 
-.expandable {
-  cursor: pointer;
-}
-
 .match {
   visibility: hidden;
 }
 
-.expandable > .property-header > .match {
+.expandable {
+  cursor: pointer;
   visibility: visible;
 }
 
 .property-name {
   font-size: 12px;
   color: -moz-FieldText;
-  display: inline-block;
 }
 .property-value {
   padding: 0;
   font-size: 10px;
   color: grey;
   vertical-align: text-top;
   width: 100%;
 }
 
-.property-view-hidden,
-.property-content-hidden {
-  display: none;
-}
-
 .rule-link {
   text-align: end;
   -moz-padding-start: 10px;
   cursor: pointer;
 }
 
 /* This rule is necessary because Templater.jsm breaks LTR TDs in RTL docs */
 .rule-text {
@@ -155,20 +139,16 @@
 .matched {
   text-decoration: line-through;
 }
 .parentmatch {
   color: #666;
 }
 
 #propertyContainer {
-  display: -moz-box;
-  -moz-box-orient: vertical;
-  -moz-box-flex: 1;
-  overflow-y: auto;
   border-collapse: collapse;
 }
 
 .darkrow {
   background-color: rgba(0,0,0,.022);
 }
 
 #noResults {
@@ -215,46 +195,45 @@
 }
 
 .ruleview-code {
   padding: 2px 5px;
 }
 
 .ruleview-warning {
   background: url("chrome://browser/skin/devtools/alerticon-warning.png");
-  display: inline-block;
   -moz-margin-start: 5px;
   vertical-align: middle;
   width: 13px;
   height: 12px;
 }
 
-.ruleview-warning[hidden] {
-  display: none;
+.ruleview-ruleopen {
+  -moz-padding-end: 5px;
 }
 
-.ruleview-ruleopen {
-  -moz-padding-end: 5px;
+.ruleview-ruleclose {
+  width: -moz-min-content;
+  padding-right: 20px;
 }
 
 .ruleview-propertylist {
   list-style: none;
   padding: 0;
   margin: 0;
 }
 
 .ruleview-enableproperty {
   height: 10px;
   width: 10px;
   -moz-margin-start: 2px;
   -moz-margin-end: 0;
 }
 
 .ruleview-expander {
-  display: inline-block;
   width: 8px;
   height: 8px;
   background: url("chrome://browser/skin/devtools/arrows.png") 24px 0;
   cursor: pointer;
   -moz-margin-start: 2px;
   -moz-margin-end: 5px;
 }
 
@@ -263,17 +242,16 @@
 }
 
 .ruleview-newproperty {
   /* (enable checkbox width: 12px) + (expander width: 15px) */
   -moz-margin-start: 27px;
 }
 
 .ruleview-propertyname {
-  display: inline-block;
   padding: 1px 0;
   cursor: text;
   color: #0060C0;
   text-decoration: inherit;
 }
 
 .ruleview-propertyvalue {
   cursor: text;
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..ebfd9586d201416665b64e4fe57af05c1a967edb
GIT binary patch
literal 1321
zc$@(#1=jkBP)<h;3K|Lk000e1NJLTq001BW000mO1^@s6cL04^00001b5ch_0Itp)
z=>Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2iyS_
z05usk)smk800gN?L_t(Y$K{l1Y*a-6$A9zQUOjeu&=$I-(1MhZQV@|K5n=%`MvM?t
zA|`@dO7J!qFc>BJ$;23=h#Yc<s2CDN3J3&>fIzuKkaEc(bPMg$?RMGSw{PBi;|CV9
z7z})OKEBCs<~RTOzk&bz5CHDHpa&=b@&Wff7zwlk5g84c0IXFykPT!3`aO6XNCFAQ
z3)_|cSh#q>m(xc-^<>rkSl4aZa^{tpZ!Q6@0?>LXcW71c^IWfc%Kh*+QgYdzGog2Z
z=q-WbQKLqdTecIsp9MyZ8d(k$-x3g!=76GX;?;zGU$N)tVxovN&r=x!Ihv-Y0A8Lp
zBeG*>ePcT6%;6p}ytdDZZx<G5nr;I*w`Za)SuxXbDd5_lhd&MPUSE$itUGC+t7zER
z+RoJjXUA+@L|d`|Tp3-k0Cw!GZ%~Q@0yG4Ez*35MdM*Zx%<Rw2Ta`juH}`8jao{KI
z*tWB(xI$3T&qFHdh%@_5jF@Q6>)hU{cmLV9m`njOP8<QkLQuZ9e%q7{b(@=(EM2~@
ztu1mzK}#Z$l-b!?sI>5HSf=s3l6Km!U3X@!EgW1?vN3L{_2=WPN=Q2_4sK{OP16uU
z_(&xk0PSA_Qh;N3gcMNNsi>GYX1>~E_l~VEe*EFwm&!^@ONS1B{OsyA-(5|M-XS2R
z1h?0*J3>l?FZ;uZR4<{dd3|!NhHAh1#NTT`$(8-vC&j~8*AQ)q|5pNDcV18jyk4)z
zv>j#o{eCfJ^2DMEFO2W}bKRznufJJ*3NYPyK}|+O4uCYx+jq@crLljVH)YDKWD0aa
zQMOCGRasUt%_z$IC3Nw^QgZSoYn8}Mzy!=O&y4N|Q~?1^(@elrkg^>W^LV|{=bszr
zTC-|d^_*Gnbj>_~DJ7<q1bg;=npC0$pWkbkQZuC_X-cjqOqy?)Ifl~4l~wlqs$$U4
z!Q%8j{bmvJf`iUA{!#1kcyQ@jwh+Qq^U&b)M;d-B%8Yr(&U$UuoVm$x`1)bsFCc71
zLzZ}Lb+16*M@|B+LZ6OD7yRAaI@%K`sLSM?RPuQ8V&i8{^SPrK@er|a`>g~b%T}&7
z0cqJzR9g0R%d)+Iv@FY-zhIGOS(Y`t_Az_dqeCnpgd(C8cKSDro6)unQcAAH3@*1P
zabgi)x))9=(y~=b>A2kP>in)1-IUwc%ISSEacHA+Gl4)b0Q5l674QM=MV&hNk2LHp
zdvDHM&ksMYYo0oJQrSlzyl)srGHd#b*N^?Wd+#0@4J81yAro@&<(A{}l%VS#gdTHO
zJseQoYqmPk@N(zGuJ85!Bc_Vng7JkuJ)UyJvk^<*$=kh69YA>fGTrJczy+YGw6u##
z8b)B@;-x2096$Q|F{698@-8(seNzH*wrt&g#!1Bt0Ij3V7R_g4qUlttxO}o%v^BN5
z9~kJ7?ku4WZk$ik=?f|zJ*GNW4ltCc%oettjD)waYlReddn#RdK@CL!$OUdFxFMjR
zpfJDxxUpkOmo8t~2sB#JQ2cJZ9taBc;1jtdV>w78A<iF7sOs8)sHo}dR1O#`&K+7r
fEPTn?{Dtuko9Z^s$RVN?00000NkvXXu0mjf0U~aI
--- a/browser/themes/gnomestripe/jar.mn
+++ b/browser/themes/gnomestripe/jar.mn
@@ -39,18 +39,18 @@ browser.jar:
   skin/classic/browser/feeds/feedIcon16.png           (feeds/feedIcon16.png)
   skin/classic/browser/feeds/videoFeedIcon.png        (feeds/feedIcon.png)
   skin/classic/browser/feeds/videoFeedIcon16.png      (feeds/feedIcon16.png)
   skin/classic/browser/feeds/audioFeedIcon.png        (feeds/feedIcon.png)
   skin/classic/browser/feeds/audioFeedIcon16.png      (feeds/feedIcon16.png)
   skin/classic/browser/feeds/subscribe.css            (feeds/subscribe.css)
   skin/classic/browser/feeds/subscribe-ui.css         (feeds/subscribe-ui.css)
   skin/classic/browser/newtab/newTab.css              (newtab/newTab.css)
-  skin/classic/browser/newtab/strip.png               (newtab/strip.png)
-  skin/classic/browser/newtab/toolbar.png             (newtab/toolbar.png)
+  skin/classic/browser/newtab/controls.png            (newtab/controls.png)
+  skin/classic/browser/newtab/noise.png               (newtab/noise.png)
   skin/classic/browser/places/bookmarksMenu.png       (places/bookmarksMenu.png)
   skin/classic/browser/places/bookmarksToolbar.png    (places/bookmarksToolbar.png)
   skin/classic/browser/places/calendar.png            (places/calendar.png)
 * skin/classic/browser/places/editBookmarkOverlay.css (places/editBookmarkOverlay.css)
   skin/classic/browser/places/livemark-item.png       (places/livemark-item.png)
   skin/classic/browser/places/pageStarred.png         (places/pageStarred.png)
   skin/classic/browser/places/starred48.png           (places/starred48.png)
   skin/classic/browser/places/unstarred48.png         (places/unstarred48.png)
@@ -124,23 +124,26 @@ browser.jar:
   skin/classic/browser/devtools/breadcrumbs/rtl-start-selected.png           (devtools/breadcrumbs/rtl-start-selected.png)
   skin/classic/browser/devtools/splitview.css         (devtools/splitview.css)
   skin/classic/browser/devtools/styleeditor.css       (devtools/styleeditor.css)
   skin/classic/browser/devtools/debugger.css          (devtools/debugger.css)
   skin/classic/browser/devtools/magnifying-glass.png  (devtools/magnifying-glass.png)
   skin/classic/browser/devtools/itemToggle.png        (devtools/itemToggle.png)
   skin/classic/browser/devtools/itemArrow-rtl.png     (devtools/itemArrow-rtl.png)
   skin/classic/browser/devtools/itemArrow-ltr.png     (devtools/itemArrow-ltr.png)
+  skin/classic/browser/devtools/inspect-button.png    (devtools/inspect-button.png)
 #ifdef MOZ_SERVICES_SYNC
   skin/classic/browser/sync-16-throbber.png
   skin/classic/browser/sync-16.png
   skin/classic/browser/sync-24-throbber.png
   skin/classic/browser/sync-32.png
   skin/classic/browser/sync-bg.png
   skin/classic/browser/sync-128.png
   skin/classic/browser/sync-desktopIcon.png
   skin/classic/browser/sync-mobileIcon.png
   skin/classic/browser/sync-notification-24.png
   skin/classic/browser/syncSetup.css
   skin/classic/browser/syncCommon.css
   skin/classic/browser/syncQuota.css
   skin/classic/browser/syncProgress.css
 #endif
+  skin/classic/browser/webapps-16.png
+  skin/classic/browser/webapps-64.png
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..14f382fbdd18a1209f3dcd63831014b5ad2fc428
GIT binary patch
literal 4180
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
--- a/browser/themes/gnomestripe/newtab/newTab.css
+++ b/browser/themes/gnomestripe/newtab/newTab.css
@@ -1,148 +1,131 @@
-#scrollbox {
-  padding-bottom: 18px;
-  background-color: #fff;
-}
-
-#body {
-  padding-top: 106px;
-  font-family: sans-serif;
-}
-
-.button {
-  padding: 0;
-  border: 0 none;
+:root {
+  -moz-appearance: none;
+  background-color: transparent;
 }
 
-/* TOOLBAR */
-#toolbar {
-  top: 8px;
-  right: 8px;
-  width: 13px;
-  height: 30px;
-  padding: 0;
-  margin: 0;
-}
-
-.toolbar-button {
-  background: transparent url(chrome://browser/skin/newtab/toolbar.png);
-}
-
-#toolbar-button-show {
-  width: 11px;
-  height: 11px;
-  background-position: -10px 0;
-}
-
-#toolbar-button-show:hover {
-  background-position: -10px -12px;
+/* SCROLLBOX */
+#newtab-scrollbox:not([page-disabled]) {
+  background-color: rgb(229,229,229);
+  background-image: url(chrome://browser/skin/newtab/noise.png),
+                    -moz-linear-gradient(rgba(255,255,255,.5), rgba(255,255,255,.2));
+  background-attachment: fixed;
 }
 
-#toolbar-button-show:active {
-  background-position: -10px -24px;
-}
-
-#toolbar-button-hide {
-  width: 10px;
-  height: 10px;
-}
-
-#toolbar-button-hide:hover {
-  background-position: 0 -12px;
-}
-
-#toolbar-button-hide:active {
-  background-position: 0 -24px;
+/* TOGGLE */
+#newtab-toggle {
+  width: 16px;
+  height: 16px;
+  padding: 0;
+  border: none;
+  background: -216px 0 transparent url(chrome://browser/skin/newtab/controls.png);
 }
 
-#toolbar-button-reset {
-  top: 17px;
-  width: 11px;
-  height: 12px;
-}
-
-#toolbar-button-reset {
-  background-position: -21px 0;
+#newtab-toggle[page-disabled] {
+  background-position: -232px 0;
 }
 
-#toolbar-button-reset:hover {
-  background-position: -21px -12px;
+/* ROWS */
+.newtab-row {
+  margin-bottom: 20px;
 }
 
-#toolbar-button-reset:active {
-  background-position: -21px -24px;
-}
-
-/* GRID */
-#grid {
-  padding: 1px;
-  margin: 0 auto;
+.newtab-row:last-child {
+  margin-bottom: 0;
 }
 
 /* CELLS */
-.cell {
-  outline: 1px dashed #ccc;
-  outline-offset: -1px;
+.newtab-cell {
+  -moz-margin-end: 20px;
+  background-color: rgba(255,255,255,.2);
+  border: 1px solid;
+  border-color: rgba(8,22,37,.12) rgba(8,22,37,.14) rgba(8,22,37,.16);
+  border-radius: 1px;
+  -moz-transition: border-color 100ms ease-out;
+}
+
+.newtab-cell:empty {
+  border: 1px dashed;
+  border-color: rgba(8,22,37,.15) rgba(8,22,37,.17) rgba(8,22,37,.19);
+}
+
+.newtab-cell:last-child {
+  -moz-margin-end: 0;
+}
+
+.newtab-cell:hover:not(:empty) {
+  border-color: rgba(8,22,37,.25) rgba(8,22,37,.27) rgba(8,22,37,.3);
 }
 
 /* SITES */
-.site {
-  background-color: #ececec;
-  -moz-transition: 200ms ease-out;
-  -moz-transition-property: top, left, box-shadow, opacity;
+.newtab-site {
+  text-decoration: none;
+  -moz-transition-property: top, left, opacity, box-shadow, background-color;
 }
 
-.site[dragged] {
-  -moz-transition-property: box-shadow;
+.newtab-site:hover,
+.newtab-site[dragged] {
+  box-shadow: 0 0 10px rgba(8,22,37,.3);
+}
+
+.newtab-site[dragged] {
+  -moz-transition-property: box-shadow, background-color;
+  background-color: rgb(242,242,242);
 }
 
-.site[ontop] {
-  box-shadow: 0 1px 4px #000;
-  outline: none;
+/* THUMBNAILS */
+.newtab-thumbnail {
+  background-origin: padding-box;
+  background-clip: padding-box;
+  background-repeat: no-repeat;
+  background-size: cover;
 }
 
-/* SITE TITLE */
-.site-title {
-  height: 2.4em;
-  width: 189px;
-  padding: 0 6px;
-  background-color: rgba(0,0,0,0.5);
-  border: solid transparent;
-  border-width: 6px 0;
-  color: #fff;
-  text-decoration: none;
-  line-height: 1.2em;
-  font-weight: 700;
+/* TITLES */
+.newtab-title {
+  padding: 0 8px;
+  background-color: rgba(248,249,251,.95);
+  color: #1f364c;
+  font-size: 12px;
+  line-height: 24px;
 }
 
-/* SITE STRIP */
-.site-strip {
-  padding: 4px 3px;
-  background-color: rgba(0,0,0,0.5);
+/* CONTROLS */
+.newtab-control {
+  width: 24px;
+  height: 24px;
+  padding: 1px 2px 3px;
+  border: none;
+  background: transparent url(chrome://browser/skin/newtab/controls.png);
 }
 
-.strip-button {
-  width: 17px;
-  height: 17px;
-  background: transparent url(chrome://browser/skin/newtab/strip.png);
+.newtab-control-pin:hover {
+  background-position: -24px 0;
 }
 
-.strip-button-pin:hover {
-  background-position: 0 -17px;
+.newtab-control-pin:active {
+  background-position: -48px 0;
 }
 
-.strip-button-pin:active,
-.site[pinned] .strip-button-pin {
-  background-position: 0 -34px;
+.newtab-control-pin[pinned] {
+  background-position: -72px 0;
+}
+
+.newtab-control-pin[pinned]:hover {
+  background-position: -96px 0;
+}
+
+.newtab-control-pin[pinned]:active {
+  background-position: -120px 0;
 }
 
-.strip-button-block {
-  background-position: -17px 0;
+.newtab-control-block {
+  background-position: -144px 0;
 }
 
-.strip-button-block:hover {
-  background-position: -17px -17px;
+.newtab-control-block:hover {
+  background-position: -168px 0;
 }
 
-.strip-button-block:active {
-  background-position: -17px -34px;
+.newtab-control-block:active {
+  background-position: -192px 0;
 }
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..01d340aaa9095a54d19071516cf9e0ca75b1f72e
GIT binary patch
literal 2118
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
deleted file mode 100644
index 2527df6e72b5c80b91e180af9644ba96a5c148d6..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 389f689aa2a7611f53977594c048f79c4991affd..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..cf59fa3ca1e903968f793d940b2dc651f6ac4e1f
GIT binary patch
literal 366
zc%17D@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|*pj^6T^Rm@
z;DWu&Cj&(|3p^r=85p>QL70(Y)*K0-AbW|YuPgg)Zhm0~jSb$HZUBYedAc};Se!n4
zX}y<Apa9#4`Sk&U%?dhM@$w0glU-SErd(CXGt9sEK>CGrL^$uhrbNRVc_tAd0UIN9
z!UW4s$5_Oi6ZN$+n3K3Ke_!#whaavRPJEf0EdRcuJC3_O;y~=&2yT4|w|U8ImtUy=
z4d1f*aCZgQA=76@;%AZs=A`R9kK6c>`S$D51AZ&&6Lg!`AB$_dv9_uANb=0>oN*~1
zmaaOXvQEf}o!#P4+9mk}Em^I{+~yWXCC(KHepTJpbpJ^DT{*c&(|%j@7Ydbj+Ad>$
zEZUf-Q>dzZk0<B7btBW~7ph|3v-ck8x?l6v?}>e%pyf;J<ieGucY!`<@O1TaS?83{
F1OTL^jTis`
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..442132d987b04e37e7cd24b45785e7b605be2e32
GIT binary patch
literal 1096
zc$@)91h@N%P)<h;3K|Lk000e1NJLTq002M$002M;1^@s6s%dfF00001b5ch_0Itp)
z=>Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2ipq|
z6AUK%MzGTW00YQLL_t(|+U=T2XcR#dhQGKVLPl|mOI(vk3}`@&UKA9gf(pUq;sV};
zfPxnxdXV695l<e>K@V<-1{4K7=tV(66csnz1w{l`P-Db}nD&rrcotnV-96pa6R97X
z>8|B%UG?g{*8>g?4h{|u4h{~9U?y+`s7!(O2;cxPzZ4bE0p|fvT%EuPpb6*`lkQUB
zDDXw2eHj>5D$4H{f_00_)2_$BE?@@GKY&g@pdM%i?gz~8)_y(UP?m~s3sBt4Be;Kk
zRYuTJ2Ye_n*Tiq9O~uy%T_J?j`~Co{L(p#me#W5h0UOd(d@k@Vf?%?LM~W*<m*q3C
zI7!7P0rx^GuJ@l-d%GAMi^=<m+E%eKLG$cUnO7zF5-NeaF=e>~)Wl)B9C#jL8OpXF
zz_Jv#;R5AE^BF>y^EI%x7^d~WHN#OS8MRn+#G6CNa~o(#lIb|$On}7?XLuC11;}#&
zs7^cTJEZpH=n@FTh5|Q$c6IMy1LeTp5|U?&&DGdXMRgwVTSabJVFvJC?Ejoi<-4^+
zXT0SVc*$@a=4>l3?~hwhevg57szxv=ZwfxPq@ehkh(TG-mhvV6Iufeh)B-PKhQT?T
z%A16^2@H?HyDxAyg5rrAVN6WsCPCUF@ZKIF6%NBYRx(r#JW9w2a_;OrJST>%2t`r;
z6)?4!ZLl`Mw{a>$e4m8!%}ILU<3;Yri-J}&JtcG>v(&QJdBxrL=h8ZZ>wu3jJo0rx
zqfxcVf=P{PlQqNh5qS7xI+Za~M@z`ctp_|5^BP3IOj#$3_9deIN=P0aJtcRm7yw)?
z5S6_W<ELoC2N}ShH%Ii{EfDqH0|whvyuViHT%^SczHpz!n6UxEn+)LhPZRww3{H4e
zK-^*l9<W|(+sD73#e57gygAbrkYgr^F>f({5o%KH*ff9#JOwJW>g@xdOg#_G<10)C
z(8o&p_tjXL72XnKtsB6<_8LOje2y}o0rWZL0rXn~=2$m?+>^h7d1B130v^1O0ra^8
z)Y$2mOXHcX0R3ywn-FJs7z0QepXvEjo^MrUW`IHkied~9MAt++R=S%c$_)3VN;mdD
zH@<1BAuCAU9X1Ux2-qavi0wV-^Yd53M%&S##KD+ZViX@>@&^Yf5sU)vL^MQH14x>8
zfRU6S76RWx8Y1d3=!e*i^a5-RacqT&1he}5o*3E_$OTT)%TWb9i;xcc<DO~RV6VqY
zQ8&;_VsZs^0W0{Av0EuB)7sjs%34{Wf}TG=%jXIl92^`R92^`R?BOqH7L8M0{IIqF
O0000<MNUMnLSTaKyWXDw
--- a/browser/themes/pinstripe/browser.css
+++ b/browser/themes/pinstripe/browser.css
@@ -2354,16 +2354,20 @@ toolbarbutton.chevron > .toolbarbutton-m
 #addons-notification-icon {
   list-style-image: url(chrome://mozapps/skin/extensions/extensionGeneric-16.png);
 }
 
 #password-notification-icon {
   list-style-image: url(chrome://mozapps/skin/passwordmgr/key-16.png);
 }
 
+#webapps-notification-icon {
+  list-style-image: url(chrome://browser/skin/webapps-16.png);
+}
+
 .popup-notification-icon {
   width: 64px;
   height: 64px;
   -moz-margin-end: 10px;
 }
 
 .popup-notification-icon[popupid="geolocation"] {
   list-style-image: url(chrome://browser/skin/Geolocation-64.png);
@@ -2420,16 +2424,20 @@ toolbarbutton.chevron > .toolbarbutton-m
   list-style-image: url(chrome://global/skin/icons/question-64.png);
 }
 
 .popup-notification-icon[popupid="password-save"],
 .popup-notification-icon[popupid="password-change"] {
   list-style-image: url(chrome://mozapps/skin/passwordmgr/key-64.png);
 }
 
+.popup-notification-icon[popupid="webapps-install"] {
+  list-style-image: url(chrome://browser/skin/webapps-64.png);
+}
+
 /* Popup Buttons */
 #identity-popup-more-info-button {
   @hudButton@
   margin: 10px 0 0;
   min-height: 0px;
 }
 
 #identity-popup-more-info-button:focus {
@@ -2716,16 +2724,25 @@ panel[dimmed="true"] {
 
 #highlighter-veil-container[locked] > #highlighter-veil-middlebox > #highlighter-veil-transparentbox {
   box-shadow: 0 0 0 1px black;
   outline-color: white;
 }
 
 /* Highlighter toolbar */
 
+#inspector-inspect-toolbutton {
+  list-style-image: url("chrome://browser/skin/devtools/inspect-button.png");
+  -moz-image-region: rect(0px 16px 16px 0px);
+}
+
+#inspector-inspect-toolbutton[checked] {
+  -moz-image-region: rect(0px 32px 16px 16px);
+}
+
 #inspector-toolbar {
   border-top: 1px solid hsla(210, 8%, 5%, .65);
   padding-top: 4px;
   padding-bottom: 4px;
 }
 
 #inspector-toolbar:-moz-locale-dir(ltr) {
   padding-left: 2px;
--- a/browser/themes/pinstripe/devtools/common.css
+++ b/browser/themes/pinstripe/devtools/common.css
@@ -57,16 +57,20 @@
   background: -moz-linear-gradient(hsla(212,7%,57%,.35), hsla(212,7%,57%,.1)) padding-box;
   box-shadow: 0 1px 0 hsla(210,16%,76%,.15) inset, 0 0 0 1px hsla(210,16%,76%,.15) inset, 0 1px 0 hsla(210,16%,76%,.15);
 }
 
 .devtools-toolbarbutton > .toolbarbutton-text {
   margin: 1px 6px;
 }
 
+.devtools-toolbarbutton:not([label]) {
+  min-width: 32px;
+}
+
 .devtools-toolbarbutton:not([checked]):hover:active {
   border-color: hsla(210,8%,5%,.6);
   background: -moz-linear-gradient(hsla(220,6%,10%,.3), hsla(212,7%,57%,.15) 65%, hsla(212,7%,57%,.3));
   box-shadow: 0 0 3px hsla(210,8%,5%,.25) inset, 0 1px 3px hsla(210,8%,5%,.25) inset, 0 1px 0 hsla(210,16%,76%,.15);
 }
 
 .devtools-toolbarbutton[checked] {
   color: hsl(208,100%,60%) !important;
--- a/browser/themes/pinstripe/devtools/csshtmltree.css
+++ b/browser/themes/pinstripe/devtools/csshtmltree.css
@@ -40,21 +40,16 @@
  * ***** END LICENSE BLOCK ***** */
 
 :root {
   -moz-appearance: none;
   background: -moz-Field;
   color: -moz-FieldText;
 }
 
-#root {
-  display: -moz-box;
-}
-
-
 .property-header {
   padding: 5px 0;
   white-space: nowrap;
   vertical-align: text-top;
 }
 
 /* Take away these two :visited rules to get a core dumper     */
 /* See https://bugzilla.mozilla.org/show_bug.cgi?id=575675#c30 */
@@ -68,17 +63,16 @@
 .helplink:visited {
   text-decoration: none;
 }
 .link:hover {
   text-decoration: underline;
 }
 
 .helplink {
-  display: block;
   height: 14px;
   width: 0;
   overflow: hidden;
   -moz-padding-start: 14px;
   background-image: url("chrome://browser/skin/devtools/goto-mdn.png");
   -moz-margin-end: 2px;
   cursor: pointer;
 }
@@ -94,54 +88,44 @@
 
 .expander {
   -moz-appearance: treetwisty;
   width: 12px;
   height: 12px;
   padding-top: 12px;
   -moz-margin-start: 5px;
   -moz-margin-end: 5px;
-  display: inline-block;
   vertical-align: middle;
 }
 
 .expander[open] {
   -moz-appearance: treetwistyopen;
 }
 
-.expandable {
-  cursor: pointer;
-}
-
 .match {
   visibility: hidden;
 }
 
-.expandable > .property-header > .match {
+.expandable {
+  cursor: pointer;
   visibility: visible;
 }
 
 .property-name {
   font-size: 12px;
   color: -moz-FieldText;
-  display: inline-block;
 }
 .property-value {
   padding: 0;
   font-size: 10px;
   color: grey;
   vertical-align: text-top;
   width: 100%;
 }
 
-.property-view-hidden,
-.property-content-hidden {
-  display: none;
-}
-
 .rule-link {
   text-align: end;
   -moz-padding-start: 10px;
   cursor: pointer;
 }
 
 /* This rule is necessary because Templater.jsm breaks LTR TDs in RTL docs */
 .rule-text {
@@ -157,20 +141,16 @@
 .matched {
   text-decoration: line-through;
 }
 .parentmatch {
   color: #666;
 }
 
 #propertyContainer {
-  display: -moz-box;
-  -moz-box-orient: vertical;
-  -moz-box-flex: 1;
-  overflow-y: auto;
   border-collapse: collapse;
 }
 
 .darkrow {
   background-color: rgba(0,0,0,.022);
 }
 
 #noResults {
@@ -217,46 +197,45 @@
 }
 
 .ruleview-code {
   padding: 2px 5px;
 }
 
 .ruleview-warning {
   background: url("chrome://browser/skin/devtools/alerticon-warning.png");
-  display: inline-block;
   -moz-margin-start: 5px;
   vertical-align: middle;
   width: 13px;
   height: 12px;
 }
 
-.ruleview-warning[hidden] {
-  display: none;
+.ruleview-ruleopen {
+  -moz-padding-end: 5px;
 }
 
-.ruleview-ruleopen {
-  -moz-padding-end: 5px;
+.ruleview-ruleclose {
+  width: -moz-min-content;
+  padding-right: 20px;
 }
 
 .ruleview-propertylist {
   list-style: none;
   padding: 0;
   margin: 0;
 }
 
 .ruleview-enableproperty {
   height: 10px;
   width: 10px;
   -moz-margin-start: 2px;
   -moz-margin-end: 0;
 }
 
 .ruleview-expander {
-  display: inline-block;
   width: 8px;
   height: 8px;
   background: url("chrome://browser/skin/devtools/arrows.png") 24px 0;
   cursor: pointer;
   -moz-margin-start: 2px;
   -moz-margin-end: 5px;
 }
 
@@ -265,17 +244,16 @@
 }
 
 .ruleview-newproperty {
   /* (enable checkbox width: 12px) + (expander width: 15px) */
   -moz-margin-start: 27px;
 }
 
 .ruleview-propertyname {
-  display: inline-block;
   padding: 1px 0;
   cursor: text;
   color: #0060C0;
   text-decoration: inherit;
 }
 
 .ruleview-propertyvalue {
   cursor: text;
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..ebfd9586d201416665b64e4fe57af05c1a967edb
GIT binary patch
literal 1321
zc$@(#1=jkBP)<h;3K|Lk000e1NJLTq001BW000mO1^@s6cL04^00001b5ch_0Itp)
z=>Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2iyS_
z05usk)smk800gN?L_t(Y$K{l1Y*a-6$A9zQUOjeu&=$I-(1MhZQV@|K5n=%`MvM?t
zA|`@dO7J!qFc>BJ$;23=h#Yc<s2CDN3J3&>fIzuKkaEc(bPMg$?RMGSw{PBi;|CV9
z7z})OKEBCs<~RTOzk&bz5CHDHpa&=b@&Wff7zwlk5g84c0IXFykPT!3`aO6XNCFAQ
z3)_|cSh#q>m(xc-^<>rkSl4aZa^{tpZ!Q6@0?>LXcW71c^IWfc%Kh*+QgYdzGog2Z
z=q-WbQKLqdTecIsp9MyZ8d(k$-x3g!=76GX;?;zGU$N)tVxovN&r=x!Ihv-Y0A8Lp
zBeG*>ePcT6%;6p}ytdDZZx<G5nr;I*w`Za)SuxXbDd5_lhd&MPUSE$itUGC+t7zER
z+RoJjXUA+@L|d`|Tp3-k0Cw!GZ%~Q@0yG4Ez*35MdM*Zx%<Rw2Ta`juH}`8jao{KI
z*tWB(xI$3T&qFHdh%@_5jF@Q6>)hU{cmLV9m`njOP8<QkLQuZ9e%q7{b(@=(EM2~@
ztu1mzK}#Z$l-b!?sI>5HSf=s3l6Km!U3X@!EgW1?vN3L{_2=WPN=Q2_4sK{OP16uU
z_(&xk0PSA_Qh;N3gcMNNsi>GYX1>~E_l~VEe*EFwm&!^@ONS1B{OsyA-(5|M-XS2R
z1h?0*J3>l?FZ;uZR4<{dd3|!NhHAh1#NTT`$(8-vC&j~8*AQ)q|5pNDcV18jyk4)z
zv>j#o{eCfJ^2DMEFO2W}bKRznufJJ*3NYPyK}|+O4uCYx+jq@crLljVH)YDKWD0aa
zQMOCGRasUt%_z$IC3Nw^QgZSoYn8}Mzy!=O&y4N|Q~?1^(@elrkg^>W^LV|{=bszr
zTC-|d^_*Gnbj>_~DJ7<q1bg;=npC0$pWkbkQZuC_X-cjqOqy?)Ifl~4l~wlqs$$U4
z!Q%8j{bmvJf`iUA{!#1kcyQ@jwh+Qq^U&b)M;d-B%8Yr(&U$UuoVm$x`1)bsFCc71
zLzZ}Lb+16*M@|B+LZ6OD7yRAaI@%K`sLSM?RPuQ8V&i8{^SPrK@er|a`>g~b%T}&7
z0cqJzR9g0R%d)+Iv@FY-zhIGOS(Y`t_Az_dqeCnpgd(C8cKSDro6)unQcAAH3@*1P
zabgi)x))9=(y~=b>A2kP>in)1-IUwc%ISSEacHA+Gl4)b0Q5l674QM=MV&hNk2LHp
zdvDHM&ksMYYo0oJQrSlzyl)srGHd#b*N^?Wd+#0@4J81yAro@&<(A{}l%VS#gdTHO
zJseQoYqmPk@N(zGuJ85!Bc_Vng7JkuJ)UyJvk^<*$=kh69YA>fGTrJczy+YGw6u##
z8b)B@;-x2096$Q|F{698@-8(seNzH*wrt&g#!1Bt0Ij3V7R_g4qUlttxO}o%v^BN5
z9~kJ7?ku4WZk$ik=?f|zJ*GNW4ltCc%oettjD)waYlReddn#RdK@CL!$OUdFxFMjR
zpfJDxxUpkOmo8t~2sB#JQ2cJZ9taBc;1jtdV>w78A<iF7sOs8)sHo}dR1O#`&K+7r
fEPTn?{Dtuko9Z^s$RVN?00000NkvXXu0mjf0U~aI
--- a/browser/themes/pinstripe/jar.mn
+++ b/browser/themes/pinstripe/jar.mn
@@ -49,18 +49,18 @@ browser.jar:
   skin/classic/browser/feeds/subscribe-ui.css               (feeds/subscribe-ui.css)
   skin/classic/browser/feeds/feedIcon.png                   (feeds/feedIcon.png)
   skin/classic/browser/feeds/feedIcon16.png                 (feeds/feedIcon16.png)
   skin/classic/browser/feeds/videoFeedIcon.png              (feeds/feedIcon.png)
   skin/classic/browser/feeds/videoFeedIcon16.png            (feeds/feedIcon16.png)
   skin/classic/browser/feeds/audioFeedIcon.png              (feeds/feedIcon.png)
   skin/classic/browser/feeds/audioFeedIcon16.png            (feeds/feedIcon16.png)
   skin/classic/browser/newtab/newTab.css                    (newtab/newTab.css)
-  skin/classic/browser/newtab/strip.png                     (newtab/strip.png)
-  skin/classic/browser/newtab/toolbar.png                   (newtab/toolbar.png)
+  skin/classic/browser/newtab/controls.png                  (newtab/controls.png)
+  skin/classic/browser/newtab/noise.png                     (newtab/noise.png)
   skin/classic/browser/setDesktopBackground.css
   skin/classic/browser/monitor.png
   skin/classic/browser/monitor_16-10.png
   skin/classic/browser/places/allBookmarks.png              (places/allBookmarks.png)
 * skin/classic/browser/places/places.css                    (places/places.css)
 * skin/classic/browser/places/organizer.css                 (places/organizer.css)
   skin/classic/browser/places/query.png                     (places/query.png)
   skin/classic/browser/places/bookmarksMenu.png             (places/bookmarksMenu.png)
@@ -165,16 +165,17 @@ browser.jar:
   skin/classic/browser/devtools/splitview.css               (devtools/splitview.css)
   skin/classic/browser/devtools/styleeditor.css             (devtools/styleeditor.css)
   skin/classic/browser/devtools/debugger.css                (devtools/debugger.css)
   skin/classic/browser/devtools/magnifying-glass.png        (devtools/magnifying-glass.png)
   skin/classic/browser/devtools/itemToggle.png              (devtools/itemToggle.png)
   skin/classic/browser/devtools/itemArrow-rtl.png           (devtools/itemArrow-rtl.png)
   skin/classic/browser/devtools/itemArrow-ltr.png           (devtools/itemArrow-ltr.png)
   skin/classic/browser/devtools/background-noise-toolbar.png (devtools/background-noise-toolbar.png)
+  skin/classic/browser/devtools/inspect-button.png          (devtools/inspect-button.png)
 #ifdef MOZ_SERVICES_SYNC
   skin/classic/browser/sync-throbber.png
   skin/classic/browser/sync-16.png
   skin/classic/browser/sync-32.png
   skin/classic/browser/sync-bg.png
   skin/classic/browser/sync-128.png
   skin/classic/browser/sync-desktopIcon.png
   skin/classic/browser/sync-mobileIcon.png
@@ -185,15 +186,17 @@ browser.jar:
   skin/classic/browser/syncProgress.css
 #endif
   skin/classic/browser/lion/keyhole-circle.png              (keyhole-circle-lion.png)
   skin/classic/browser/lion/Toolbar.png                     (Toolbar-lion.png)
   skin/classic/browser/lion/toolbarbutton-dropmarker.png    (toolbarbutton-dropmarker-lion.png)
   skin/classic/browser/lion/tabbrowser/alltabs-box-bkgnd-icon.png      (tabbrowser/alltabs-box-bkgnd-icon-lion.png)
   skin/classic/browser/lion/tabview/tabview.png                        (tabview/tabview-lion.png)
   skin/classic/browser/lion/places/toolbar.png              (places/toolbar-lion.png)
+  skin/classic/browser/webapps-16.png
+  skin/classic/browser/webapps-64.png
 
 % override chrome://browser/skin/keyhole-circle.png                        chrome://browser/skin/lion/keyhole-circle.png                           os=Darwin osversion>=10.7
 % override chrome://browser/skin/Toolbar.png                               chrome://browser/skin/lion/Toolbar.png                                  os=Darwin osversion>=10.7
 % override chrome://browser/skin/toolbarbutton-dropmarker.png              chrome://browser/skin/lion/toolbarbutton-dropmarker.png                 os=Darwin osversion>=10.7
 % override chrome://browser/skin/tabbrowser/alltabs-box-bkgnd-icon.png     chrome://browser/skin/lion/tabbrowser/alltabs-box-bkgnd-icon.png        os=Darwin osversion>=10.7
 % override chrome://browser/skin/tabview/tabview.png                       chrome://browser/skin/lion/tabview/tabview.png                          os=Darwin osversion>=10.7
 % override chrome://browser/skin/places/toolbar.png                        chrome://browser/skin/lion/places/toolbar.png                           os=Darwin osversion>=10.7
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..14f382fbdd18a1209f3dcd63831014b5ad2fc428
GIT binary patch
literal 4180
zc$@)L5UcNrP)<h;3K|Lk000e1NJLTq008&^000;W1^@s6*Of4d00001b5ch_0Itp)
z=>Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2iyW3
z3po`Ymn>!g01wnjL_t(|+U=ZqR1{af$G^37(<}-RL{S73g}8w$#L2jfU~q|vxCPB(
zo=J>boWv||#JIeXmzNmFGh>XH%zK$Pal_}9NnD7EQAq^RxS$CHP(X}gBtnY>Z0g=S
ze{^-zOLbRuE64LD)H(Nb)wged&hOs({qDW>tFGd1-@c8&j$JvJHx2+Ve_D!`joB0`
zf5Le4M+Tsc|1ZpM-@ZLKpLVUnXLsuvfdBXW2f1-_<17sm0f@hOB+<9&?p?uTG6?{{
z7~_<G%rOAa>-EIo<3mp5pKJsm$;H~xHhvrb*UD#i>)8kYyDJC4X!&PHUU_~>l2#1>
zMm?VPn7@BOAOJKp+%X>An{`mDCdx1#fd24-F9ycP_1{1l<r$@%tA;5xl;e5Eb38p?
zcwv6>xQS;0m;jJAejC4yZw*@8eXSZoLqmPKckfXR0OjSEgFMkhdGrzi=;Px0W$r!r
zRapMF=R>?%4m>hAzGlK>qcQ=EkVh~oe`VztoVytI!{sYp@nd^L)C?Wa&n^FxP20i_
z9Qw*D{<tx}tNFKQ)7<jkKYcSStK^PX{Ir;Wnm2~HxBugdbzxsrG<wCK80A~@Qk;AK
zd&kPcwiZ@<#h)41x#q2r?)k0lKfYgl9_E*q=N12#VZ&<TCQfwAuiLsMtnu(+ulT;n
z$u)5^XFBp3W7K&~uTcX4UteES$Iy;V08m|h-Q<bJ+bm6di6jXpzby=Al);;F^4r2-
zB1r-OUzzE|FS%3|%qZYZDY;ao%0HNMEEtTzn{qJcm@0oqX?-wb6yB5_rS+=(eH9H}
z^Y>LWsPea8s19a~!JD%ELbWQtwf)DCpC3#qg*U~JpRdYq$jR}V-;k4|#0R4++oqk_
zo@f9N9T{$@tf=&_tf=&ljtn<=qFHC~sL03wN-0k$QC*gdO&Wphoa0V9p@gH9@==kI
z0Tq>%jW$UND__#UPZ-z_1!pfh>6Ag8Pb5|TjA@gxZO3jWT_TbyzgbeZfBujz*l_xq
zlip0V*iTjY&&7q})54#e?Fa6Q{R@Z3V0~_hSN3c1A6J_e{~;!m*L)ClK5cIHo=>SF
z|MxFUALm$As|Ene-dtD>AXGlAUjEiAnl(#+3lfp^lro-D3M~OnDOC(|N*ON^Ne@7<
z=|GS82?P6qQmP!}DtyM&6PHg<pA5#BVvwuaPlzgi!O$+Clw!lFA6un=o{I|wr3{}I
z-e?v6!Vxi`jA8xpA6kX)K1SU0B@O+5FZ|Z9f9=QH?2>1T=gdgbssTXqlTQZ(1_f~d
zV63YP@I<o)I3+}6jB!ju0~uu)moyTLGGyl*bGVLR93@0_>_7`&OMo!OkT|G67^OIQ
z=7L9jM%4qHGK!hgQ^6?3wjFys<P!~ih6M>-!5GDcf~y|!LACS`Fe|<j7^B#9zPc6o
z46lrc0izTj9xwK=f9@?n)cE%r;A(u*3ieY*oswr-H2`QI6yz5f8CeSe6+n<DnrLH~
zz!>AyMzFC-BVp-;6zfcW%&viqF+ri>0%MFL%soGGP#mZnqzQxKKp4ZxGliP@YBPA+
z)JY(+lcY_X1WGBgc7CRrPl@D~KQAEyl5GFnq2VBeBD3I%2YmMq{7ifpB)R{eiR%PH
zC^nzJuAQ$E;PXfHw%R{`L@WrU*l@ha1HS$FeQVLnh>8eD#g(gAz3wBInpVCf+4q0D
zWy>J+=mB#1GU`{Ya;bT1fA2MG;NPnk8cIu1^u`-5HFtcaK~Jj&0KNu;DI_GM2>@=~
zxMA`{6K(DT%1aFDHn=T8ZP}E9+>vR=C)90lCl2gy$4?yCACyqLAa}=SZU&I2rcOfI
z)Jb+Wq)nX!$|$zm1i4$jn*g7i7>T(F-9T(s5pxr|fl`8vCo8q_scZf-1G?bZ0i8i@
z2C-)cbOvJto6lXlXZz=k>|+h;mVl21r39JBzSnks9sK8ytN)Dj*B7FDWCUJc_!0o1
zTX+|gl~>@MKdpye@8k0BlhS^(Lx5jSUyATme}r#zGy;|`HBZM;QD`hJ#nolY5u_76
z;ybPozFD*g32WCPFeV1E@4W{A@axe7bwx!eSiBhR1woZhDa{5jRzA%3L<4|cQQZtz
zu3YiA?0ce#&est34IGy|3Mok=ZSoW?-|zb}2Ya&*K@fEiMNw-a#qN06kO6pPP#lyT
zBsc#Qj-4ogUhe~)Ua##M$YBPbF*Oy_r#uOz6PUGgH@0lw1)dkc^Sm~`Lx9X1)*UY=
zMz!#5?EfgY7@5b50KEaw88q_k`{&s~;dpvLR~vpy`!*Mp;qUon;PnP@dS9>j3r6+D
z+>w23`lrSIjX7tr;m{c{oev=TXuJls=Zm6<|9t;1czkR!9v?df0Pxw~1K68=7&^TV
zDD`Z~?HJ(x98jK~jt;4l5jr{5oL$P=j$3<nBS;jT>Z|)Fb>MTzfAv*#pD_bH)6xI{
zC7U*(eA_m(69i~k(*OH{S>wFb0AS^kSM8E#|B?QhW=$c$?G65X|3R2cCX7!>veWl}
znS<=ZN1>BPV6DjsM+ZBan}@rNjTk;`sGXjhcM5r@&%xK%Pw{uI^8@yFY|h#NNovMZ
zQ&a8q?K}4%Yv*SWc){)R$jN>Ohxzcx1(2o&%o*O>PX9Q!2pdma27ChK$kFC#jx+4v
zzbOW3Y{1irz3lWYCrh#UOci+FcB(V8JO27F3qbESVovfvd;1Tc#fGC70KK2=`I5!}
zckV|))IlN?rsihrAR>r5c$3gl3Rr&td~-9H$ppyz{x0eYs)=@IKl1~?nM|NEpEH@@
z=c2CC_RhLb?2>2ci)JTj)c|1Bgr@=m0|U*;GovxU6HQbLK#t?E|H~W%wQq+<5(e7^
zxFCv}1J61`NcJa{<3;4=pMk%>KjQm6Y%_bGfljY?s;?zM<Y@o49edEB!$TN7a=2CB
zwqp-?o`;sKySx4YufvAiQiS>&@OXU7Ouiwv)I4i@*gsB}FoGVRo~lF#e;<tL6K<71
zJyi)#ZvZ2n^^a3f@j9$OQizW2{V;KGOiSP&DFj44;9(Fd`NN{QFCZek3(CsM0RUr0
z4M%LxXsp}#KldfTJEy0^r+atQ7n>7;4^0>kzdpTD{mwi0CBXY<WFR0q8b5#c9RMIA
zH5DD=;&9>Zw>A2tbMj291^|J9fqva0y43;zu9%Z&S~XtD0US$@9#QDqI~Mt;&VUdC
zLI^mXSFEeW0US$2co+2Q9*M$>#h`?O5(+^O6zgi4!5vHQm|hq>ARb4Lf74>Wi@I87
zK*ti_ts@@o*%|u_D?lg(p%jc473-?oPn=4huE7`@6^fkiuYwQ;LKq<G6zh6re`F^-
z8XJy%-~IrJ5J-f8alBXdJNm<d7iXbM=TKZKufUphnOL(f6PL;>(4})I7Q8siWB+NE
zB)by&=9|!khoiov1jQ>?qIl&>)R&Y%7aoq#H{W!suQ~xyYFGNMSOLF?2;3?z#)&0M
zaAL_4+$t`HUql4@u2|tz-_>0w+v14^00x7>6cQZL6cQZLWH1;^o@lB|^zKm+cz)(I
ze6lGE#U*99@zYI=du%kky<g~f4c;}Z6DFpN!mhmsP;uoNYHrnH#BYW=)zdtKILyco
z$MnSF-_OU|4>EE7!bM!Wb{$hEKj~CY`wV68B=KDz!n;qzW9hCF$gikDWsMQDNAv|H
z(tY9g=^Th9$+1|y|1{2At;LVEw~>|@dk=j3ejO0e5v!;F7E8D0Ag}BiDsR@|g`~kA
zugqNe&zcW1!6*gC^U&$cd6c#5KXTHvuK?}aUAS^3B*q|e90KKp*Y^sVxBZiwo^E9Y
z$a$JP1x;mD>6ARvssTVubd;g0s><K8?}=s!aHe#EEW!P^%~>cZD+ezKIB@8Q*3p(J
z?b|LvyM}eb6Dgyy>+@_>UAqBM)Zy#=(_oak&9=Ma+b+3dV|wDXMf33I^_eKVSOkva
z@qfGj0m7)3rMV0HC7a;xfOnr5fMq+sMqX7NAn5SX@gfjHv;?5C{Wb$z@6LhvLvnAd
z-gg@3s_y_gA8aYO3`&`n*;<t!9}$Z8o*Is~w;n=X`AxI^IcGsB^_uVK7lI&K*Tt6a
zlr*h;$645~<-}XRQ_{5Z9cN*K`8v<;J0;B>f7#+WPRTQ^8UU<Xy4WsxmhtxMnl;hp
zT9t4d$1Ka#prAlJKVusHwkZpxm&(o8tCWK0cw`?sVm?Vq_XaqQaU4e!bi#2QBeLVS
zZx?_kQhtYBpYKQ2kJaD>0fb24c^=2}PC=*BdBwNfCJPSkfY%nz#b4HM#D(vQz;T>8
zs5xNg?tg%DG4iVN<q^J9dtdz5lY_8)=hw)ussnftAOwsT@z>lUFc(>8t^AMx1OAZQ
z2W$2f;N0~)fMDiB&|zyq88{bNdCm3*`s3Z{!?AqJL342Pg4upfz{iKrfQfqV`1kln
z5jDOfNv+DK_xMK|SNwZqp7d4|<&lP5yI$R7e(Vj55X$|0>lWT$_aSQQZiDA}P@DV9
zoWpU;Xw5T@<Jh(9)lD!zDrXtRtbFsOFXd|MjQH<OS-8{C2wo8EMsmU7*qRUG)%iTn
zGeQVgQ}Z*Hu3UwmZvNa7+$}w!ID-p<pvo6OnM4^^U3(iZ{q+E<?lhZ&+h)dN4#(Ec
z<~a>~#w5zPo5nkMW6M{#(a6lfZQCD$UNK*wiO(@6QOZ@<8u8-#ov6CqWDag-W4~a3
z+`?Dye~bTk<H%b4hX4%3%m2G;IKRw12ztfmT=O5;##;g$fQjch`e@=1Bcl{x47IgJ
zm&qul@@V1^BhPabEziO-M}B;t7%Try!!PFG)5wqS6Qjx>J7%=e@^skEpK8qiHqiJq
z+1N3oRrzW0U5%7dPzF@iH-m!L{IpS8d{<TegoisCDWjkOt~WAJc`(&dCOq6x-TtA`
zMoO94eq)ntKhspwhDNLME&k(8(c(XTDJe!vBGc32my)8&4;eq+NEw4SC1m_~CH@25
zEHk(SplSP#-OHy>NzF)|IPMl9M35ie;MA5RJkL=<5Qt9HN!xeqUJjs1W?C9(<xiWE
znlb9P39ZI|dG@S~+0V^vHU9D`sToU>Vq1;BXi93vyu^rB<FA;Knvp(!P^<A<-GAo1
zoDnp8cI)xyFUaUNXU_fRKd_DCeA@8<^m6VX06<{JkdPpWNPdhlAI6xd(m;vhxVxet
z{&KUXrVfA+z#X}fCb?Q0zm5N^<g>f=^g#zWC*S|q%f(l2tX`hsRsS0xk5(qRQH}Dx
z?JH~A_-*`OCI3Occv11Ci#83jG>%ib6+mnns`^s9Hhvrb*TR3`0p9l7<Tn1j@Y`OS
etj>Rs+dl!+gL0AI@-6HD0000<MNUMnLSTYF%@Pp+
--- a/browser/themes/pinstripe/newtab/newTab.css
+++ b/browser/themes/pinstripe/newtab/newTab.css
@@ -1,147 +1,131 @@
-#scrollbox {
-  padding-bottom: 18px;
-  background-color: #fff;
-}
-
-#body {
-  padding-top: 106px;
-  font-family: sans-serif;
-}
-
-.button {
-  padding: 0;
-  border: 0 none;
+:root {
+  -moz-appearance: none;
+  background-color: transparent;
 }
 
-/* TOOLBAR */
-#toolbar {
-  top: 8px;
-  right: 8px;
-  width: 13px;
-  height: 30px;
-  padding: 0;
-  margin: 0;
-}
-
-.toolbar-button {
-  background: transparent url(chrome://browser/skin/newtab/toolbar.png);
-}
-
-#toolbar-button-show {
-  width: 11px;
-  height: 11px;
-  background-position: -10px 0;
-}
-
-#toolbar-button-show:hover {
-  background-position: -10px -12px;
+/* SCROLLBOX */
+#newtab-scrollbox:not([page-disabled]) {
+  background-color: rgb(229,229,229);
+  background-image: url(chrome://browser/skin/newtab/noise.png),
+                    -moz-linear-gradient(rgba(255,255,255,.5), rgba(255,255,255,.2));
+  background-attachment: fixed;
 }
 
-#toolbar-button-show:active {
-  background-position: -10px -24px;
-}
-
-#toolbar-button-hide {
-  width: 10px;
-  height: 10px;
-}
-
-#toolbar-button-hide:hover {
-  background-position: 0 -12px;
-}
-
-#toolbar-button-hide:active {
-  background-position: 0 -24px;
+/* TOGGLE */
+#newtab-toggle {
+  width: 16px;
+  height: 16px;
+  padding: 0;
+  border: none;
+  background: -216px 0 transparent url(chrome://browser/skin/newtab/controls.png);
 }
 
-#toolbar-button-reset {
-  top: 17px;
-  width: 11px;
-  height: 12px;
-}
-
-#toolbar-button-reset {
-  background-position: -21px 0;
+#newtab-toggle[page-disabled] {
+  background-position: -232px 0;
 }
 
-#toolbar-button-reset:hover {
-  background-position: -21px -12px;
+/* ROWS */
+.newtab-row {
+  margin-bottom: 20px;
 }
 
-#toolbar-button-reset:active {
-  background-position: -21px -24px;
-}
-
-/* GRID */
-#grid {
-  padding: 1px;
-  margin: 0 auto;
+.newtab-row:last-child {
+  margin-bottom: 0;
 }
 
 /* CELLS */
-.cell {
-  outline: 1px dashed #ccc;
-  outline-offset: -1px;
+.newtab-cell {
+  -moz-margin-end: 20px;
+  background-color: rgba(255,255,255,.2);
+  border: 1px solid;
+  border-color: rgba(8,22,37,.12) rgba(8,22,37,.14) rgba(8,22,37,.16);
+  border-radius: 1px;
+  -moz-transition: border-color 100ms ease-out;
+}
+
+.newtab-cell:empty {
+  border: 1px dashed;
+  border-color: rgba(8,22,37,.15) rgba(8,22,37,.17) rgba(8,22,37,.19);
+}
+
+.newtab-cell:last-child {
+  -moz-margin-end: 0;
+}
+
+.newtab-cell:hover:not(:empty) {
+  border-color: rgba(8,22,37,.25) rgba(8,22,37,.27) rgba(8,22,37,.3);
 }
 
 /* SITES */
-.site {
-  background-color: #ececec;
-  -moz-transition: 200ms ease-out;
-  -moz-transition-property: top, left, box-shadow, opacity;
+.newtab-site {
+  text-decoration: none;
+  -moz-transition-property: top, left, opacity, box-shadow, background-color;
 }
 
-.site[dragged] {
-  -moz-transition-property: box-shadow;
+.newtab-site:hover,
+.newtab-site[dragged] {
+  box-shadow: 0 0 10px rgba(8,22,37,.3);
+}
+
+.newtab-site[dragged] {
+  -moz-transition-property: box-shadow, background-color;
+  background-color: rgb(242,242,242);
 }
 
-.site[ontop] {
-  box-shadow: 0 1px 4px #000;
+/* THUMBNAILS */
+.newtab-thumbnail {
+  background-origin: padding-box;
+  background-clip: padding-box;
+  background-repeat: no-repeat;
+  background-size: cover;
 }
 
-/* SITE TITLE */
-.site-title {
-  height: 2.4em;
-  width: 189px;
-  padding: 0 6px;
-  background-color: rgba(0,0,0,0.5);
-  border: solid transparent;
-  border-width: 6px 0;
-  color: #fff;
-  text-decoration: none;
-  line-height: 1.2em;
-  font-weight: 700;
+/* TITLES */
+.newtab-title {
+  padding: 0 8px;
+  background-color: rgba(248,249,251,.95);
+  color: #1f364c;
+  font-size: 12px;
+  line-height: 24px;
 }
 
-/* SITE STRIP */
-.site-strip {
-  padding: 4px 3px;
-  background-color: rgba(0,0,0,0.5);
+/* CONTROLS */
+.newtab-control {
+  width: 24px;
+  height: 24px;
+  padding: 1px 2px 3px;
+  border: none;
+  background: transparent url(chrome://browser/skin/newtab/controls.png);
 }
 
-.strip-button {
-  width: 17px;
-  height: 17px;
-  background: transparent url(chrome://browser/skin/newtab/strip.png);
+.newtab-control-pin:hover {
+  background-position: -24px 0;
 }
 
-.strip-button-pin:hover {
-  background-position: 0 -17px;
+.newtab-control-pin:active {
+  background-position: -48px 0;
 }
 
-.strip-button-pin:active,
-.site[pinned] .strip-button-pin {
-  background-position: 0 -34px;
+.newtab-control-pin[pinned] {
+  background-position: -72px 0;
+}
+
+.newtab-control-pin[pinned]:hover {
+  background-position: -96px 0;
+}
+
+.newtab-control-pin[pinned]:active {
+  background-position: -120px 0;
 }
 
-.strip-button-block {
-  background-position: -17px 0;
+.newtab-control-block {
+  background-position: -144px 0;