Merge m-c to b2ginbound, a=merge
authorWes Kocher <wkocher@mozilla.com>
Fri, 21 Aug 2015 10:07:22 -0700
changeset 258677 455e01b30a1b3bfd487fd49fbbab55cb4a1ceb46
parent 258676 f7acd18cb7b65f1b63f148290c8cd2887065fe2c (current diff)
parent 258661 22c34579ae0720e7d3dc39a22b9d33f13bc0198b (diff)
child 258678 1643868c578979806b8e7303c59ab7023ae708ce
push id29261
push userryanvm@gmail.com
push dateSun, 23 Aug 2015 19:00:26 +0000
treeherdermozilla-central@c061dd1cf8dc [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone43.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge m-c to b2ginbound, a=merge
mobile/android/themes/core/images/addons-32.png
mobile/android/themes/core/images/arrowleft-16.png
mobile/android/themes/core/images/arrowright-16.png
mobile/android/themes/core/images/default-app-icon.png
mobile/android/themes/core/images/errorpage-larry-black.png
mobile/android/themes/core/images/errorpage-larry-white.png
--- a/browser/base/content/newtab/newTab.css
+++ b/browser/base/content/newtab/newTab.css
@@ -208,16 +208,18 @@ input[type=button] {
 
 .newtab-suggested-bounds {
   max-height: 34px; /* 34 / 17 = 2 lines maximum */
 }
 
 .newtab-title {
   left: 0;
   padding: 0 4px;
+  border: 1px solid #FFFFFF;
+  border-radius: 0px 0px 8px 8px;
 }
 
 .newtab-sponsored {
   background-color: #FFFFFF;
   border: 1px solid #E2E2E2;
   border-radius: 3px;
   color: #4A4A4A;
   cursor: pointer;
@@ -335,17 +337,17 @@ input[type=button] {
   opacity: 0.01;
 }
 
 /* SEARCH */
 #newtab-search-container {
   display: -moz-box;
   position: relative;
   -moz-box-pack: center;
-  margin: 15px 0px;
+  margin: 40px 0 15px;
 }
 
 #newtab-search-container[page-disabled] {
   opacity: 0;
   pointer-events: none;
 }
 
 #newtab-search-form {
--- a/browser/base/moz.build
+++ b/browser/base/moz.build
@@ -22,18 +22,16 @@ BROWSER_CHROME_MANIFESTS += [
     'content/test/popupNotifications/browser.ini',
     'content/test/referrer/browser.ini',
     'content/test/social/browser.ini',
 ]
 
 DEFINES['MOZ_APP_VERSION'] = CONFIG['MOZ_APP_VERSION']
 DEFINES['MOZ_APP_VERSION_DISPLAY'] = CONFIG['MOZ_APP_VERSION_DISPLAY']
 
-DEFINES['NIGHTLY_BUILD'] = CONFIG['NIGHTLY_BUILD']
-
 DEFINES['APP_LICENSE_BLOCK'] = '%s/content/overrides/app-license.html' % SRCDIR
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('windows', 'gtk2', 'gtk3', 'cocoa'):
     DEFINES['HAVE_SHELL_SERVICE'] = 1
     DEFINES['CONTEXT_COPY_IMAGE_CONTENTS'] = 1
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('windows', 'cocoa'):
     DEFINES['CAN_DRAW_IN_TITLEBAR'] = 1
--- a/browser/components/loop/content/css/contacts.css
+++ b/browser/components/loop/content/css/contacts.css
@@ -355,36 +355,30 @@ html[dir="rtl"] .contact > .dropdown-men
                       Can likely go away if we switched this pane to flexbox model */
   padding-top: 4px; /* Based on spacing in Mockup
                     replaced margin-top
                     See http://stackoverflow.com/questions/6204670/css-clean-solution-to-the-margin-collapse-issue-when-floating-an-element
                     */
 }
 
 .contacts-gravatar-promo {
-  position: relative;
-  border: 1px dashed #c1c1c1;
+  border: 1px solid #5cccee;
   border-radius: 2px;
-  background-color: #fbfbfb;
-  padding: 10px;
-  margin-top: 10px;
+  background-color: #fff;
+  font-size: 1.2rem;
+  margin: 1.5rem;
+  padding: 1.5rem 1rem;
+  position: relative;
 }
 
 .contacts-gravatar-promo > p {
-  margin-top: 2px;
-  margin-bottom: 8px;
-  margin-right: 4px;
+  margin-top: 0;
   word-wrap: break-word;
 }
 
-html[dir="rtl"] .contacts-gravatar-promo > p {
-  margin-right: 0;
-  margin-left: 4px;
-}
-
 .contacts-gravatar-promo > p > a {
   color: #0295df;
   text-decoration: none;
 }
 
 .contacts-gravatar-promo > p > a:hover {
   text-decoration: underline;
 }
@@ -395,16 +389,54 @@ html[dir="rtl"] .contacts-gravatar-promo
   right: 8px;
 }
 
 html[dir="rtl"] .contacts-gravatar-promo > .button-close {
   right: auto;
   left: 8px;
 }
 
+.contacts-gravatar-avatars {
+  height: 50px;
+  margin: 1.5rem auto;
+  text-align: center;
+  width: 200px;
+}
+
+.contacts-gravatar-avatars img {
+  margin: 0 1.5rem;
+  vertical-align: middle;
+  width: 50px;
+}
+
+/* Adjust the Firefox avatar because it has pointy ears. */
+.contacts-gravatar-avatars img:last-child {
+  transform: scale(1.08) translateY(-2px);
+}
+
+.contacts-gravatar-arrow {
+  border-color: #9b9b9b;
+  border-style: solid solid none none;
+  border-width: 2px;
+  display: inline-block;
+  height: 1.5rem;
+  -moz-margin-start: -.75rem;
+  transform: rotateZ(45deg);
+  vertical-align: middle;
+  width: 1.5rem;
+}
+
+html[dir="rtl"] .contacts-gravatar-arrow {
+  transform: rotateZ(225deg);
+}
+
+.contacts-gravatar-buttons {
+  padding: 0 .5rem;
+}
+
 .contact-controls {
   padding: 0 16px;
 }
 
 .contact-controls > .button {
   padding: .5em;
   border: none;
   border-radius: 5px;
--- a/browser/components/loop/content/css/panel.css
+++ b/browser/components/loop/content/css/panel.css
@@ -260,16 +260,22 @@ html[dir="rtl"] .tab-view li:nth-child(2
   background-position: top center;
   padding-top: 19%;
   padding-bottom: 3%;
   text-align: center;
   color: #4a4a4a;
   font-weight: lighter;
 }
 
+/* Don't show the empty contacts image if we're showing gravatar promo. */
+.contacts-gravatar-promo ~ .contact-list-empty {
+  background-image: none;
+  padding-top: 3%;
+}
+
 .contact-list-empty {
   padding-top: 27%;
 }
 
 .room-list-empty {
   margin: 5% 15px;
   background-image: url("../shared/img/empty_conversations.svg");
 }
--- a/browser/components/loop/content/js/contacts.js
+++ b/browser/components/loop/content/js/contacts.js
@@ -129,21 +129,27 @@ loop.contacts = (function(_, mozL10n) {
       });
       return (
         React.createElement("div", {className: "contacts-gravatar-promo"}, 
           React.createElement(Button, {additionalClass: "button-close", 
                   caption: "", 
                   onClick: this.handleCloseButtonClick}), 
           React.createElement("p", {dangerouslySetInnerHTML: {__html: message}, 
              onClick: this.handleLinkClick}), 
-          React.createElement(ButtonGroup, null, 
-            React.createElement(Button, {caption: mozL10n.get("gravatars_promo_button_nothanks"), 
+          React.createElement("div", {className: "contacts-gravatar-avatars"}, 
+            React.createElement("img", {src: "loop/shared/img/avatars.svg#orange-avatar"}), 
+            React.createElement("span", {className: "contacts-gravatar-arrow"}), 
+            React.createElement("img", {src: "loop/shared/img/firefox-avatar.svg"})
+          ), 
+          React.createElement(ButtonGroup, {additionalClass: "contacts-gravatar-buttons"}, 
+            React.createElement(Button, {additionalClass: "secondary", 
+                    caption: mozL10n.get("gravatars_promo_button_nothanks2"), 
                     onClick: this.handleCloseButtonClick}), 
-            React.createElement(Button, {additionalClass: "button-accept", 
-                    caption: mozL10n.get("gravatars_promo_button_use"), 
+            React.createElement(Button, {additionalClass: "secondary", 
+                    caption: mozL10n.get("gravatars_promo_button_use2"), 
                     onClick: this.handleUseButtonClick})
           )
         )
       );
     }
   });
 
   const ContactDropdown = React.createClass({displayName: "ContactDropdown",
@@ -661,20 +667,20 @@ loop.contacts = (function(_, mozL10n) {
       }
 
       // If no contacts to show and filter is not set, we don't have contacts.
       if (!shownContacts.available && !shownContacts.blocked &&
           !this.state.filter) {
         return (
           React.createElement("div", {className: "contact-list-empty"}, 
             React.createElement("p", {className: "panel-text-large"}, 
-              mozL10n.get("no_contacts_message_heading")
+              mozL10n.get("no_contacts_message_heading2")
             ), 
             React.createElement("p", {className: "panel-text-medium"}, 
-              mozL10n.get("no_contacts_import_or_add")
+              mozL10n.get("no_contacts_import_or_add2")
             )
           )
         );
       }
 
       return (
         React.createElement("div", null, 
           !this.state.filter ? React.createElement("div", {className: "contact-list-title"}, 
@@ -709,31 +715,29 @@ loop.contacts = (function(_, mozL10n) {
                                              mozL10n.get("import_contacts_button3"), 
               disabled: this.state.importBusy, 
               onClick: this.handleImportButtonClick}, 
               React.createElement("div", {className: cx({"contact-import-spinner": true,
                                  spinner: true,
               busy: this.state.importBusy})})
           ), 
           React.createElement(Button, {additionalClass: "primary", 
-            caption: mozL10n.get("new_contact_button"), 
+            caption: mozL10n.get("new_contact_button2"), 
             onClick: this.handleAddContactButtonClick})
         )
       );
     },
 
     _renderGravatarPromoMessage: function() {
       if (this.state.filter) {
         return null;
       }
 
       return (
-        React.createElement("div", {className: "content-area"}, 
-          React.createElement(GravatarPromo, {handleUse: this.handleUseGravatar})
-        )
+        React.createElement(GravatarPromo, {handleUse: this.handleUseGravatar})
       );
     },
 
     render: function() {
       return (
         React.createElement("div", null, 
           this._renderContactsFilter(), 
           this._renderGravatarPromoMessage(), 
--- a/browser/components/loop/content/js/contacts.jsx
+++ b/browser/components/loop/content/js/contacts.jsx
@@ -129,21 +129,27 @@ loop.contacts = (function(_, mozL10n) {
       });
       return (
         <div className="contacts-gravatar-promo">
           <Button additionalClass="button-close"
                   caption=""
                   onClick={this.handleCloseButtonClick} />
           <p dangerouslySetInnerHTML={{__html: message}}
              onClick={this.handleLinkClick}></p>
-          <ButtonGroup>
-            <Button caption={mozL10n.get("gravatars_promo_button_nothanks")}
+          <div className="contacts-gravatar-avatars">
+            <img src="loop/shared/img/avatars.svg#orange-avatar" />
+            <span className="contacts-gravatar-arrow" />
+            <img src="loop/shared/img/firefox-avatar.svg" />
+          </div>
+          <ButtonGroup additionalClass="contacts-gravatar-buttons">
+            <Button additionalClass="secondary"
+                    caption={mozL10n.get("gravatars_promo_button_nothanks2")}
                     onClick={this.handleCloseButtonClick}/>
-            <Button additionalClass="button-accept"
-                    caption={mozL10n.get("gravatars_promo_button_use")}
+            <Button additionalClass="secondary"
+                    caption={mozL10n.get("gravatars_promo_button_use2")}
                     onClick={this.handleUseButtonClick}/>
           </ButtonGroup>
         </div>
       );
     }
   });
 
   const ContactDropdown = React.createClass({
@@ -661,20 +667,20 @@ loop.contacts = (function(_, mozL10n) {
       }
 
       // If no contacts to show and filter is not set, we don't have contacts.
       if (!shownContacts.available && !shownContacts.blocked &&
           !this.state.filter) {
         return (
           <div className="contact-list-empty">
             <p className="panel-text-large">
-              {mozL10n.get("no_contacts_message_heading")}
+              {mozL10n.get("no_contacts_message_heading2")}
             </p>
             <p className="panel-text-medium">
-              {mozL10n.get("no_contacts_import_or_add")}
+              {mozL10n.get("no_contacts_import_or_add2")}
             </p>
           </div>
         );
       }
 
       return (
         <div>
           {!this.state.filter ? <div className="contact-list-title">
@@ -709,31 +715,29 @@ loop.contacts = (function(_, mozL10n) {
                                              mozL10n.get("import_contacts_button3")}
               disabled={this.state.importBusy}
               onClick={this.handleImportButtonClick} >
               <div className={cx({"contact-import-spinner": true,
                                  spinner: true,
               busy: this.state.importBusy})} />
           </Button>
           <Button additionalClass="primary"
-            caption={mozL10n.get("new_contact_button")}
+            caption={mozL10n.get("new_contact_button2")}
             onClick={this.handleAddContactButtonClick} />
         </ButtonGroup>
       );
     },
 
     _renderGravatarPromoMessage: function() {
       if (this.state.filter) {
         return null;
       }
 
       return (
-        <div className="content-area">
-          <GravatarPromo handleUse={this.handleUseGravatar}/>
-        </div>
+        <GravatarPromo handleUse={this.handleUseGravatar} />
       );
     },
 
     render: function() {
       return (
         <div>
           {this._renderContactsFilter()}
           {this._renderGravatarPromoMessage()}
new file mode 100644
--- /dev/null
+++ b/browser/components/loop/content/shared/img/firefox-avatar.svg
@@ -0,0 +1,1 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 252 252"><defs><path id="a" d="M243.5 134.6c0 63.8-51.7 115.5-115.5 115.5S12.5 198.4 12.5 134.6 64.3 19.1 128 19.1c66.5 0 115.5 51.7 115.5 115.5z"/></defs><radialGradient id="b" cx="127.044" cy="77.862" r="193.396" gradientUnits="userSpaceOnUse"><stop offset=".329" stop-color="#00A0DB"/><stop offset=".419" stop-color="#009AD6"/><stop offset=".573" stop-color="#008AC8"/><stop offset=".773" stop-color="#0072B5"/><stop offset="1" stop-color="#00549F"/></radialGradient><use xlink:href="#a" overflow="visible" fill="url(#b)"/><clipPath id="c"><use xlink:href="#a" overflow="visible"/></clipPath><linearGradient id="d" gradientUnits="userSpaceOnUse" x1="31.093" y1="233.596" x2="187.469" y2="233.596"><stop offset=".298" stop-color="#E36024"/><stop offset=".911" stop-color="#F7941E"/></linearGradient><path clip-path="url(#c)" fill="url(#d)" d="M183.4 286.5c-13.8 14.8-26.4 8.1-66.2 5.3-39.8-2.8-86-38.3-86-38.3s-2.5-19.3 12.6-29.2c11.4-7.5 29.1-15.3 27.5-20-4-11.5-10.2-32.8-10.2-32.8l87.3 4.4c-18-2.1-10 36.7-3.3 40.2 11.5 6.1 25.9 8.5 29.7 17.2 3.8 8.7 11.9 28.7 12.5 33.5.7 4.8-2.1 17.8-3.9 19.7z"/><path clip-path="url(#c)" fill="#FFF" d="M142.1 213.7s3.3 12.1-13.4 12.7c-15.2.6-21-2.3-21-2.3l7.3 27.3 54.7-.7S169 238 163 230s-16.7-14.7-16.7-14.7"/><path clip-path="url(#c)" fill="#4E4F53" d="M140 206.4s17.3 14.1 29 17.1c9 2.3 14 22 14.7 26.7.7 4.7-8.2 10.8-8.2 10.8s-5 4-23-27c-2.4-4.2-10.4-20.1-10.4-20.1s0-.2-2.1-7.5z"/><linearGradient id="e" gradientUnits="userSpaceOnUse" x1="66.878" y1="195.286" x2="137.047" y2="195.286"><stop offset="0" stop-color="#C13831"/><stop offset="1" stop-color="#E36024"/></linearGradient><path clip-path="url(#c)" fill="url(#e)" d="M79.5 219.9s10.8-14.8 27.5-20.5c13.1-4.4 30-3.9 30-3.9s-61.2-39.8-69.5-18.6c-3.5 9 7.7 45 12 43z"/><path clip-path="url(#c)" fill="#47484C" d="M43.4 214.4l27.2-12.1s6.4 8.1 21.5 13.5c30.7 10.9 28.5-3 33.5 16 2.5 9.4 12 30.5 12 30.5s-85.6 2.1-94.2-47.9z"/><g opacity=".4" clip-path="url(#c)"><path fill="#4E4F53" d="M128.5 263.1c-72.1 0-130.8-58.7-130.8-130.8 0-72 58.7-130.7 130.8-130.7 72.1 0 130.8 58.7 130.8 130.8-.1 72.1-58.7 130.7-130.8 130.7zm0-246.3c-63.7 0-115.6 51.8-115.6 115.6 0 63.7 51.8 115.6 115.6 115.6S244 196.1 244 132.4c0-63.8-51.8-115.6-115.5-115.6z"/></g><radialGradient id="f" cx="113.661" cy="247.717" r=".008" gradientTransform="rotate(175.555 104.257 -10.732)" gradientUnits="userSpaceOnUse"><stop offset=".17" stop-opacity="0"/><stop offset="1"/></radialGradient><path opacity=".7" fill="url(#f)" d="M113.2 226.3h.1"/><radialGradient id="g" cx="75.917" cy="233.589" r=".221" gradientTransform="rotate(175.555 104.257 -10.732)" gradientUnits="userSpaceOnUse"><stop offset=".17" stop-opacity="0"/><stop offset="1"/></radialGradient><path opacity=".7" fill="url(#g)" d="M149.9 209c-.1.4-.1.6-.1.6s0-.2.1-.6z"/><radialGradient id="h" cx="124.057" cy="234.417" r="1.632" gradientTransform="rotate(175.555 104.257 -10.732)" gradientUnits="userSpaceOnUse"><stop offset=".17" stop-opacity="0"/><stop offset="1"/></radialGradient><path opacity=".7" fill="url(#h)" d="M99.6 213.4c1.4.2 2.9.5 4.5.9-1.6-.4-3.1-.7-4.5-.9z"/><linearGradient id="i" gradientUnits="userSpaceOnUse" x1="134.177" y1="179.697" x2="134.177" y2="22.849" gradientTransform="rotate(-3.738 -189.536 488.95)"><stop offset=".298" stop-color="#E36024"/><stop offset=".911" stop-color="#F7941E"/></linearGradient><path fill="url(#i)" d="M176 91.7s6.8-14.3 13.2-27.9c14.4-30.7 7.9-36.9 7.9-36.9s-19.1 2.9-51.2 14.7c-17.6 6.5-24.4 9.1-26.8 9.1-18-.2-20.5 0-20.5 0s-1.9-8.4-7.2-26.1c-6-20-15.3-20-15.3-20s-36.6 25.5-58.4 68c-10.7 20.9 15.8 81 15.8 81s28 7.4 32.7 6c0 0 6.7 1.9 14.1 1.7 7.5-.1 26.2-6.5 30.2-8.7 4-2.1 21.1-4.2 27.9-3.2 6.8 1.1 40-19.3 44.4-20.7 4.4-1.4 12.2-4 11.7-6.2s-20.3-2.2-22.4-7.8c-2.6-6.4 3.9-23 3.9-23z"/><linearGradient id="j" gradientUnits="userSpaceOnUse" x1="141.561" y1="143.094" x2="205.1" y2="143.094" gradientTransform="rotate(-3.738 -189.536 488.95)"><stop offset="0" stop-color="#E36024"/><stop offset=".911" stop-color="#F7941E"/></linearGradient><path fill="url(#j)" d="M148.3 104.5s-4.4 14.6 9.6 15.9c17.1 1.7 37.1 1.8 37.1 1.8l-32.1 13-23.7-3 7.9-2.5s-11.2-2.3-8.6-8.5c3.2-8 9.8-16.7 9.8-16.7z"/><linearGradient id="k" gradientUnits="userSpaceOnUse" x1="53.506" y1="132.916" x2="53.506" y2="132.75" gradientTransform="rotate(-3.738 -189.536 488.95)"><stop offset=".084" stop-color="#E36024"/><stop offset=".895" stop-color="#F7941E"/></linearGradient><path fill="url(#k)" d="M29.7 117.7l.1.1"/><path fill="#F7941E" d="M66.5 72.3s12.1-5.9 23.6-7.1c11.4-1.2 16.5-1.1 16.5-1.1l-12.3 3.3s32.6-.8 46.1 7.5c12.5 7.7 18.2 17.4 18.2 17.4l13-7.4s-23.4-20.5-45.7-32.6c-9.2-5-27.2-1.7-27.2-1.7S97 39.2 95.5 39.3c-1.6.1-33.7 21.3-37.5 25.5-3.7 4.3 8.5 7.5 8.5 7.5z"/><path fill-rule="evenodd" clip-rule="evenodd" fill="#FFF" d="M211.4 121.4s2.3 1.6.5 7.3c-1.9 5.7-2.4 14.9-2.4 14.9l9.8-2.6 1.1 7.3s-2.4 1.5-4.1 2.1c-1.6.7 3.8 6.5 3.8 6.5s-2.1 9.3-6.4 11.9c-4.3 2.5-8.8 2.3-13.7 4.9-4.9 2.6-8.8 6.8-14.8 8.3-6.1 1.5-6.6 1.8-6.6 1.8s-3.7 1.1-6.9 4.5c-12.6 13.5-18.5 11.6-37.2 8.8-47.8-7.3-54.2-3.5-73.3 1.1-6.7 1.6-4.5-7.1-19.8-6.7-37.5.6-28.4-39.1-28.4-39.1l16.2 2.3 2-20.8s6.2 7.4 13.4 11.1c10.4 5.3 22.7 16 30.4 14.7 35-5.6 48.5-10.6 54.7-10.9 8-.4 25.8-3.2 25.8-3.2s-13.3-12.6 3.9-17.3c36.7-9.9 36.8-3.9 36.8-3.9l-2 10.8 3.1-3.5s3.1.5 5.4.3c2.2-.1 8.7-10.6 8.7-10.6z"/><path fill="#4E4F53" d="M130 63.8l-.4 16.4-6.1 1.1s-.4 5.2-18.5 7.7-20.6 5.3-20.6 5.3l4.9-16.9s7-3.2 16.1-5.6c9.1-2.3 16.7-4.1 16.7-4.1l1.2-5.9 6.7 2z"/><radialGradient id="l" cx="85.854" cy="159.606" r=".429" gradientTransform="rotate(176.262 105.6 -10.294)" gradientUnits="userSpaceOnUse"><stop offset=".17" stop-opacity="0"/><stop offset="1"/></radialGradient><path opacity=".7" fill="url(#l)" d="M134.5 137.1c.5.1.9.3 1.1.5 0 .1-.4-.1-1.1-.5z"/><radialGradient id="m" cx="120.16" cy="158.924" r="1.638" gradientTransform="rotate(176.262 105.6 -10.294)" gradientUnits="userSpaceOnUse"><stop offset=".17" stop-opacity="0"/><stop offset="1"/></radialGradient><path opacity=".7" fill="url(#m)" d="M102.5 138.3c-1.3.5-2.7.9-4 1.4 1.5-.5 2.9-.9 4.4-1.4h-.4z"/><linearGradient id="n" gradientUnits="userSpaceOnUse" x1="-42.225" y1="152.926" x2="31.852" y2="125.401" gradientTransform="matrix(.6616 .0612 -.0833 .9002 216.714 24.8)"><stop offset="0" stop-color="#FFF"/><stop offset="1" stop-color="#FFE5C2"/></linearGradient><path fill="url(#n)" d="M223.5 129c-1 3.7-6.2 10.3-11 9.9 1.4-2.7 5.1-19.1-1.5-25.5-.3 3.1-3.9 12.6-8.8 16.6.8-2 1.2-4.4 1.4-6.3-5 9.2-22 16-9.6 13.4 12.4-2.6 14.2-4.4 14.2-4.4s-2.6 11-5.7 15.3c6.3 4.1 14.4-3 14.4-3s-3 16.4-11.2 21.3c3 1.1 4.2.5 5.1.4-.8 2.3-9.8 7.5-29.4-.7.7 2.4 3.3 5.7 6.3 8.1-7.8 1.4-24.2.6-12.9 6.3 17.1 8.5 26.3 2.1 26.3 2.1s-4.9-1.8-7.1-7.2c33.4 12.2 33.6-41.3 29.5-46.3z"/><path fill="#4E4F53" d="M192.7 121.7c3.7-.2 7.9 3.6 5.1 10.5-2.8 6.9-7.1 15.6-18.1 15.8-18.2.4-29.5-15.3-26.6-17.9 3-2.4 10-6.6 39.6-8.4z"/><path opacity=".3" fill="#D7D3C8" d="M159 128.9c-2.3 1.2-2 2.2.3 3 4.8 1.7 4.9.5 8.8-.5 3.9-1 20.2-4 23.3-4.9 4.7-1.3 4.8-3.8 3.2-3.8-3.2.1-26.1 1.5-35.6 6.2z"/><path fill="#4E4F53" d="M167.4 60.1s-3.2-1-7.4-7.1c-1.7-2.4-3.6-8.6-3.6-11.5.7 1.1 1.5 2.4 2.3 3.2-1.7-3.5-1.4-6-1.4-7.2 5-1.7 12.6-5.5 15.6-6.7 19.5-8 27.9-16.2 28.8-3 .4 6-5.2 19.7-11.3 32.8-7.5.7-23-.5-23-.5zM96.8 42.2c-1.6 2.8-4.5 7.3-7.6 8.6-4.7 1.9-27.9 18.7-31.7 18.9-2.1.1-7.2-2.6-10.1-20.3-.5 3.1-.6 6.8-.5 8.5-3.6-7.6-3.7-18.2-2.6-25.2 16.8-19.5 30.9-31 36.5-31.3 5.6-.2 12.2 24.2 16 40.8zm27 64.1s-2.4 3.6-8.4 5.5c-6.1 2-2.4 0-2.4 0s-1.7-2.6-2.7-6.4c-1-3.8-1.8-9.6-1.8-9.6s2.5 10.2 6.6 12.4c2.2 1.1 8.7-1.9 8.7-1.9z"/><path fill="#4E4F53" d="M84.8 117.3s8.9-6.5 15.8-8.6c8.6-2.7 11.9-.4 11.9-.4l2.8 3.5c.1 0-20 5.4-30.5 5.5z"/><path fill="#4E4F53" d="M91.3 115.8s-.8 5.9 3.1 11.1c1.4 1.8 2.9 1.6 4.8 2.1 10.1 2.8 14.7-3.5 15.5-4.9 3.9-6.4 1-13.2 1-13.2l-24.4 4.9z"/><path fill="#FFF" d="M174.5 88.1s-5-4.6-9-8.1c-4-3.4-9.4-7.2-9.4-7.2s4.4-4.5 11.8-8.5c-3.1.2-5.5.9-7.4 1.6 9.4-13.2 28.4-38.1 34.2-35.9 8.1 3.2-20.2 58.1-20.2 58.1z"/><linearGradient id="o" gradientUnits="userSpaceOnUse" x1="-122.4" y1="85.738" x2="-84.513" y2="85.738" gradientTransform="rotate(175.613 34.788 -7.833)"><stop offset="0" stop-color="#FFF" stop-opacity="0"/><stop offset="1" stop-color="#FFE5C2"/></linearGradient><path fill="url(#o)" d="M194.2 31.4c-6.5-1.6-30.5 32.3-30.5 32.3s6.7-.6 8.5-.2c-7.4 4.4-11.3 7.3-11.2 7.9 0 .6 6.6.2 6.6.2s-4.8 1.9-4.8 2.3c0 .4 2.8.4 2.8.4s-5.1 2.1-4.8 2.1c.3 0 11.8 10.2 11.8 10.2s29.8-53.2 21.6-55.2z"/><g fill="#4E4F53"><path d="M168 95.1s0 4.7.4 7.8c.4 3.1 1.5 5.4 1.5 5.4s-3.2 1.2 1.9.3c5-.9 5.4-3.7 5.4-3.7s-4.1 1.9-5.2.8c-2.7-2.8-4-10.6-4-10.6z"/><path d="M149.8 110.3s3.5-3.8 9.2-5.9c6.6-2.4 10-.9 10-.9l2.7 5.1s-9.6 1.6-21.9 1.7z"/><path d="M170.3 107.4s-.1 3.6-2.1 7.4c-.6 1.2-1.1 3.9-10.5 2.3-2.8-.5-3.4 0-4.2-1.6-.8-1.6-.9-6-.9-6s5.2-5.5 17.7-2.1z"/></g><path fill="#4E4F53" d="M181.9 85.5s-1.3 12.7-5.7 11.7-7.3-1.6-13.3-2.2c-6-.6-5.8-8.2-5.6-12.7s2.5-7.8 2.5-7.8l2.3 3.6c0-.1 10.9-.7 19.8 7.4z"/><linearGradient id="p" gradientUnits="userSpaceOnUse" x1="162.558" y1="194.954" x2="279.188" y2="151.618" gradientTransform="rotate(179.64 143.258 -6.38)"><stop offset="0" stop-color="#FFF"/><stop offset="1" stop-color="#FFE5C2"/></linearGradient><path fill="url(#p)" d="M12.7 139.1c1 4.2 7.9 12 15.3 11.9-1.8-3.1-5.1-21.5 5.5-28 .1 3.4 4.2 14.2 11.1 19.1-.9-2.3-1.3-4.9-1.3-7.1 6.4 10.6 19.8 22.8 33.8 22.7 13.6-.1 21-8.8 22.9-9.7 1.9-.9 25 0 30.3-.8-1.6-.8-3.1-1.1-5.6-2.1 16-4.6 27.5-3.9 30.1-1.4l3.2 2.1c-.1 0-19.7 6.4-36.5 7.7-16.8 1.3-22.1 1.5-22.1 1.5s-16.7 12-35 7.9c-18.3-4-29.2-18.6-29.2-18.6s2.4 12.4 6.5 17.3c-10 3.9-21.2-4.6-21.2-4.6s2.4 18.4 14 24.5c-4.7.9-6.3.1-7.7 0 .9 2.6 13.7 9.2 44.1 2-1.4 2.5-5.7 6-10.5 8.3 7.5 1.4 24.1-6.3 47.7-.7 17.5 4.2-1.9-1.5-29.2 8.8-26 9.8-39.7-.2-39.7-.2s7.7-1.5 11.6-7.2c-51.7 10.6-44.9-48.2-38.1-53.4z"/><path fill="#FFF" d="M94.5 55.7S86 8.2 78.9 8.4c-3.8.1-12.7 15.7-19.6 31.9 1.4-.8 2.8-2.4 4.3-3.8-10.9 23.2-22.3 56-20 57.1 10.5-19.1 24.9-19.9 26.6-20.3 0-1.4-2.8-3.3-6.5-4.7 1.5-2.5 25.7-9.5 25.7-9.5l-5-.4c-.1 0 9.6-3 10.1-3z"/><linearGradient id="q" gradientUnits="userSpaceOnUse" x1="-9.023" y1="31.718" x2="35.95" y2="31.718" gradientTransform="matrix(-.9995 .0324 .0286 .883 81.18 18.737)"><stop offset="0" stop-color="#FFF" stop-opacity="0"/><stop offset="1" stop-color="#FFE5C2"/></linearGradient><path fill="url(#q)" d="M78.8 10.8c-4.3.2-15.6 24-15.6 24s1.8-1.4 4-2.2c-2 3.2-11.7 25.8-19.9 51.3 2.8-2.3 8.2-9.5 19.2-11.6-1.5-1.2-3.8-2.5-7.1-2.6 3.3-5.5 18.6-8.3 23.6-9.6-3.1-.7-6.6-.5-10.5-.3.6-1.1 18.5-5.1 18.9-5.5.4-.4-6.6-43.7-12.6-43.5z"/><path fill="#FFF" d="M101.9 113s-5.7.3-7.1 3.6c-1.4 3.3 1.3 5.8 1.3 5.8s2.6.3 3.7-2.4c1.8-4.1-1.9-5 2.1-7zm58.9-5.8s-4.2.2-5.3 2.7c-1 2.4 1 4.3 1 4.3s1.9.2 2.7-1.8c1.4-3.1-1.4-3.8 1.6-5.2z"/><path fill="#4E4F53" d="M176.9 163.1s-7.4 1.9-15.3 3.8c-7.9 2-17.3-1.3-17.3-1.3s11.7 7.4 17.5 6.3c5.7-1.1 15.1-8.8 15.1-8.8z"/></svg>
\ No newline at end of file
--- a/browser/components/loop/jar.mn
+++ b/browser/components/loop/jar.mn
@@ -90,16 +90,17 @@ browser.jar:
   content/browser/loop/shared/img/telefonica.png                (content/shared/img/telefonica.png)
   content/browser/loop/shared/img/hello_logo.svg                (content/shared/img/hello_logo.svg)
   content/browser/loop/shared/img/telefonica@2x.png             (content/shared/img/telefonica@2x.png)
   content/browser/loop/shared/img/ellipsis-v.svg                (content/shared/img/ellipsis-v.svg)
   content/browser/loop/shared/img/empty_contacts.svg            (content/shared/img/empty_contacts.svg)
   content/browser/loop/shared/img/empty_conversations.svg       (content/shared/img/empty_conversations.svg)
   content/browser/loop/shared/img/empty_search.svg              (content/shared/img/empty_search.svg)
   content/browser/loop/shared/img/avatars.svg                   (content/shared/img/avatars.svg)
+  content/browser/loop/shared/img/firefox-avatar.svg            (content/shared/img/firefox-avatar.svg)
 
   # Shared scripts
   content/browser/loop/shared/js/actions.js             (content/shared/js/actions.js)
   content/browser/loop/shared/js/conversationStore.js   (content/shared/js/conversationStore.js)
   content/browser/loop/shared/js/store.js               (content/shared/js/store.js)
   content/browser/loop/shared/js/roomStates.js          (content/shared/js/roomStates.js)
   content/browser/loop/shared/js/fxOSActiveRoomStore.js (content/shared/js/fxOSActiveRoomStore.js)
   content/browser/loop/shared/js/activeRoomStore.js     (content/shared/js/activeRoomStore.js)
--- a/browser/components/loop/test/desktop-local/contacts_test.js
+++ b/browser/components/loop/test/desktop-local/contacts_test.js
@@ -198,16 +198,19 @@ describe("loop.contacts", function() {
           mozLoop: navigator.mozLoop,
           notifications: notifications,
           startForm: function() {}
         }));
 
       var promo = listView.getDOMNode().querySelector(".contacts-gravatar-promo");
       expect(promo).to.not.equal(null);
 
+      var avatars = listView.getDOMNode().querySelectorAll(".contacts-gravatar-avatars img");
+      expect(avatars).to.have.length(2, "two example avatars are shown");
+
       checkGravatarContacts(false);
     });
 
     it("should not show the gravatars promo box when the 'contacts.gravatars.promo' pref is set", function() {
       sandbox.stub(navigator.mozLoop, "getLoopPref", function(pref) {
         if (pref === "contacts.gravatars.promo") {
           return false;
         } else if (pref === "contacts.gravatars.show") {
@@ -233,56 +236,87 @@ describe("loop.contacts", function() {
       listView = TestUtils.renderIntoDocument(
         React.createElement(loop.contacts.ContactsList, {
           mozLoop: navigator.mozLoop,
           notifications: notifications,
           startForm: function() {}
         }));
 
       React.addons.TestUtils.Simulate.click(listView.getDOMNode().querySelector(
-        ".contacts-gravatar-promo .button-accept"));
+        ".contacts-gravatar-promo .secondary:last-child"));
 
       sinon.assert.calledTwice(navigator.mozLoop.setLoopPref);
 
       var promo = listView.getDOMNode().querySelector(".contacts-gravatar-promo");
       expect(promo).to.equal(null);
     });
 
     it("should should set the prefs correctly when the 'use' button is clicked", function() {
       listView = TestUtils.renderIntoDocument(
         React.createElement(loop.contacts.ContactsList, {
           mozLoop: navigator.mozLoop,
           notifications: notifications,
           startForm: function() {}
         }));
 
       React.addons.TestUtils.Simulate.click(listView.getDOMNode().querySelector(
-        ".contacts-gravatar-promo .button-accept"));
+        ".contacts-gravatar-promo .secondary:last-child"));
 
       sinon.assert.calledTwice(navigator.mozLoop.setLoopPref);
       sinon.assert.calledWithExactly(navigator.mozLoop.setLoopPref, "contacts.gravatars.promo", false);
       sinon.assert.calledWithExactly(navigator.mozLoop.setLoopPref, "contacts.gravatars.show", true);
     });
 
     it("should hide the gravatars promo box when the 'close' button is clicked", function() {
       listView = TestUtils.renderIntoDocument(
         React.createElement(loop.contacts.ContactsList, {
           mozLoop: navigator.mozLoop,
           notifications: notifications,
           startForm: function() {}
         }));
 
       React.addons.TestUtils.Simulate.click(listView.getDOMNode().querySelector(
+        ".contacts-gravatar-promo .secondary:first-child"));
+
+      var promo = listView.getDOMNode().querySelector(".contacts-gravatar-promo");
+      expect(promo).to.equal(null);
+    });
+
+    it("should set prefs correctly when the 'close' button is clicked", function() {
+      listView = TestUtils.renderIntoDocument(
+        React.createElement(loop.contacts.ContactsList, {
+          mozLoop: navigator.mozLoop,
+          notifications: notifications,
+          startForm: function() {}
+        }));
+
+      React.addons.TestUtils.Simulate.click(listView.getDOMNode().querySelector(
+        ".contacts-gravatar-promo .secondary:first-child"));
+
+      sinon.assert.calledOnce(navigator.mozLoop.setLoopPref);
+      sinon.assert.calledWithExactly(navigator.mozLoop.setLoopPref,
+        "contacts.gravatars.promo", false);
+    });
+
+    it("should hide the gravatars promo box when the 'close' X button is clicked", function() {
+      listView = TestUtils.renderIntoDocument(
+        React.createElement(loop.contacts.ContactsList, {
+          mozLoop: navigator.mozLoop,
+          notifications: notifications,
+          startForm: function() {}
+        }));
+
+      React.addons.TestUtils.Simulate.click(listView.getDOMNode().querySelector(
         ".contacts-gravatar-promo .button-close"));
 
       var promo = listView.getDOMNode().querySelector(".contacts-gravatar-promo");
       expect(promo).to.equal(null);
     });
 
-    it("should set prefs correctly when the 'close' button is clicked", function() {
+    it("should set prefs correctly when the 'close' X button is clicked", function() {
       listView = TestUtils.renderIntoDocument(
         React.createElement(loop.contacts.ContactsList, {
           mozLoop: navigator.mozLoop,
           notifications: notifications,
           startForm: function() {}
         }));
 
       React.addons.TestUtils.Simulate.click(listView.getDOMNode().querySelector(
@@ -321,19 +355,19 @@ describe("loop.contacts", function() {
       });
 
       it("should show the no contacts view", function() {
         expect(node.querySelector(".contact-list-empty")).to.not.eql(null);
       });
 
       it("should display the no contacts strings", function() {
         sinon.assert.calledWithExactly(mozL10nGetSpy,
-                                       "no_contacts_message_heading");
+                                       "no_contacts_message_heading2");
         sinon.assert.calledWithExactly(mozL10nGetSpy,
-                                       "no_contacts_import_or_add");
+                                       "no_contacts_import_or_add2");
       });
     });
 
     describe("#RenderWithContacts", function() {
       beforeEach(function() {
         sandbox.stub(navigator.mozLoop.contacts, "getAll", function(cb) {
           cb(null, [].concat(fakeFewerContacts));
         });
--- a/browser/devtools/debugger/test/browser.ini
+++ b/browser/devtools/debugger/test/browser.ini
@@ -82,16 +82,18 @@ support-files =
   doc_native-event-handler.html
   doc_no-page-sources.html
   doc_pause-exceptions.html
   doc_pretty-print.html
   doc_pretty-print-2.html
   doc_pretty-print-3.html
   doc_pretty-print-on-paused.html
   doc_promise-get-allocation-stack.html
+  doc_promise-get-fulfillment-stack.html
+  doc_promise-get-rejection-stack.html
   doc_promise.html
   doc_random-javascript.html
   doc_recursion-stack.html
   doc_scope-variable.html
   doc_scope-variable-2.html
   doc_scope-variable-3.html
   doc_scope-variable-4.html
   doc_script-eval.html
@@ -344,16 +346,20 @@ skip-if = e10s && debug
 [browser_dbg_pretty-print-on-paused.js]
 skip-if = e10s && debug
 [browser_dbg_progress-listener-bug.js]
 skip-if = e10s && debug
 [browser_dbg_promises-allocation-stack.js]
 skip-if = e10s && debug
 [browser_dbg_promises-chrome-allocation-stack.js]
 skip-if = true # Bug 1177730
+[browser_dbg_promises-fulfillment-stack.js]
+skip-if = e10s && debug
+[browser_dbg_promises-rejection-stack.js]
+skip-if = e10s && debug
 [browser_dbg_reload-preferred-script-01.js]
 skip-if = e10s && debug
 [browser_dbg_reload-preferred-script-02.js]
 skip-if = e10s && debug
 [browser_dbg_reload-preferred-script-03.js]
 skip-if = e10s && debug
 [browser_dbg_reload-same-script.js]
 skip-if = e10s && debug
new file mode 100644
--- /dev/null
+++ b/browser/devtools/debugger/test/browser_dbg_promises-fulfillment-stack.js
@@ -0,0 +1,100 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * Test that we can get a stack to a promise's fulfillment point.
+ */
+
+"use strict";
+
+const TAB_URL = EXAMPLE_URL + "doc_promise-get-fulfillment-stack.html";
+const { PromisesFront } = require("devtools/server/actors/promises");
+let events = require("sdk/event/core");
+
+const TEST_DATA = [
+  {
+    functionDisplayName: "returnPromise/<",
+    line: 19,
+    column: 37
+  },
+  {
+    functionDisplayName: "returnPromise",
+    line: 19,
+    column: 14
+  },
+  {
+    functionDisplayName: "makePromise",
+    line: 14,
+    column: 15
+  },
+];
+
+function test() {
+  Task.spawn(function* () {
+    DebuggerServer.init();
+    DebuggerServer.addBrowserActors();
+
+    const [ tab,, panel ] = yield initDebugger(TAB_URL);
+
+    let client = new DebuggerClient(DebuggerServer.connectPipe());
+    yield connect(client);
+
+    let { tabs } = yield listTabs(client);
+    let targetTab = findTab(tabs, TAB_URL);
+    yield attachTab(client, targetTab);
+
+    yield testGetFulfillmentStack(client, targetTab, tab);
+
+    yield close(client);
+    yield closeDebuggerAndFinish(panel);
+  }).then(null, error => {
+    ok(false, "Got an error: " + error.message + "\n" + error.stack);
+  });
+}
+
+function* testGetFulfillmentStack(client, form, tab) {
+  let front = PromisesFront(client, form);
+
+  yield front.attach();
+  yield front.listPromises();
+
+  // Get the grip for promise p
+  let onNewPromise = new Promise(resolve => {
+    events.on(front, "new-promises", promises => {
+      for (let p of promises) {
+        if (p.preview.ownProperties.name &&
+            p.preview.ownProperties.name.value === "p") {
+          resolve(p);
+        }
+      }
+    });
+  });
+
+  callInTab(tab, "makePromise");
+
+  let grip = yield onNewPromise;
+  ok(grip, "Found our promise p");
+
+  let objectClient = new ObjectClient(client, grip);
+  ok(objectClient, "Got Object Client");
+
+  yield new Promise(resolve => {
+    objectClient.getPromiseFulfillmentStack(response => {
+      ok(response.fulfillmentStack.length, "Got promise allocation stack.");
+
+      for (let i = 0; i < TEST_DATA.length; i++) {
+        let stack = response.fulfillmentStack[i];
+        let data = TEST_DATA[i];
+        is(stack.source.url, TAB_URL, "Got correct source URL.");
+        is(stack.functionDisplayName, data.functionDisplayName,
+           "Got correct function display name.");
+        is(stack.line, data.line, "Got correct stack line number.");
+        is(stack.column, data.column, "Got correct stack column number.");
+      }
+
+      resolve();
+    });
+  });
+
+  yield front.detach();
+}
new file mode 100644
--- /dev/null
+++ b/browser/devtools/debugger/test/browser_dbg_promises-rejection-stack.js
@@ -0,0 +1,100 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * Test that we can get a stack to a promise's rejection point.
+ */
+
+"use strict";
+
+const TAB_URL = EXAMPLE_URL + "doc_promise-get-rejection-stack.html";
+const { PromisesFront } = require("devtools/server/actors/promises");
+let events = require("sdk/event/core");
+
+const TEST_DATA = [
+  {
+    functionDisplayName: "returnPromise/<",
+    line: 19,
+    column: 47
+  },
+  {
+    functionDisplayName: "returnPromise",
+    line: 19,
+    column: 14
+  },
+  {
+    functionDisplayName: "makePromise",
+    line: 14,
+    column: 15
+  },
+];
+
+function test() {
+  Task.spawn(function* () {
+    DebuggerServer.init();
+    DebuggerServer.addBrowserActors();
+
+    const [ tab,, panel ] = yield initDebugger(TAB_URL);
+
+    let client = new DebuggerClient(DebuggerServer.connectPipe());
+    yield connect(client);
+
+    let { tabs } = yield listTabs(client);
+    let targetTab = findTab(tabs, TAB_URL);
+    yield attachTab(client, targetTab);
+
+    yield testGetRejectionStack(client, targetTab, tab);
+
+    yield close(client);
+    yield closeDebuggerAndFinish(panel);
+  }).then(null, error => {
+    ok(false, "Got an error: " + error.message + "\n" + error.stack);
+  });
+}
+
+function* testGetRejectionStack(client, form, tab) {
+  let front = PromisesFront(client, form);
+
+  yield front.attach();
+  yield front.listPromises();
+
+  // Get the grip for promise p
+  let onNewPromise = new Promise(resolve => {
+    events.on(front, "new-promises", promises => {
+      for (let p of promises) {
+        if (p.preview.ownProperties.name &&
+            p.preview.ownProperties.name.value === "p") {
+          resolve(p);
+        }
+      }
+    });
+  });
+
+  callInTab(tab, "makePromise");
+
+  let grip = yield onNewPromise;
+  ok(grip, "Found our promise p");
+
+  let objectClient = new ObjectClient(client, grip);
+  ok(objectClient, "Got Object Client");
+
+  yield new Promise(resolve => {
+    objectClient.getPromiseRejectionStack(response => {
+      ok(response.rejectionStack.length, "Got promise allocation stack.");
+
+      for (let i = 0; i < TEST_DATA.length; i++) {
+        let stack = response.rejectionStack[i];
+        let data = TEST_DATA[i];
+        is(stack.source.url, TAB_URL, "Got correct source URL.");
+        is(stack.functionDisplayName, data.functionDisplayName,
+           "Got correct function display name.");
+        is(stack.line, data.line, "Got correct stack line number.");
+        is(stack.column, data.column, "Got correct stack column number.");
+      }
+
+      resolve();
+    });
+  });
+
+  yield front.detach();
+}
new file mode 100644
--- /dev/null
+++ b/browser/devtools/debugger/test/doc_promise-get-fulfillment-stack.html
@@ -0,0 +1,24 @@
+<!-- Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/ -->
+<!DOCTYPE html>
+
+<html>
+  <head>
+    <meta charset="utf-8"/>
+    <title>Promise test page</title>
+  </head>
+
+  <body>
+    <script type="text/javascript">
+    function makePromise() {
+      var p = returnPromise();
+      p.name = "p";
+    }
+
+    function returnPromise() {
+      return new Promise(resolve => resolve("hello"));
+    }
+    </script>
+  </body>
+
+</html>
new file mode 100644
--- /dev/null
+++ b/browser/devtools/debugger/test/doc_promise-get-rejection-stack.html
@@ -0,0 +1,24 @@
+<!-- Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/ -->
+<!DOCTYPE html>
+
+<html>
+  <head>
+    <meta charset="utf-8"/>
+    <title>Promise test page</title>
+  </head>
+
+  <body>
+    <script type="text/javascript">
+    function makePromise() {
+      var p = returnPromise();
+      p.name = "p";
+    }
+
+    function returnPromise() {
+      return new Promise((resolve, reject) => reject("hello"));
+    }
+    </script>
+  </body>
+
+</html>
--- a/browser/devtools/eyedropper/eyedropper-child.js
+++ b/browser/devtools/eyedropper/eyedropper-child.js
@@ -1,20 +1,24 @@
 /* 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 { interfaces: Ci } = Components;
+
 addMessageListener("Eyedropper:RequestContentScreenshot", sendContentScreenshot);
 
 function sendContentScreenshot() {
   let canvas = content.document.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
+  let scale = content.getInterface(Ci.nsIDOMWindowUtils).fullZoom;
   let width = content.innerWidth;
   let height = content.innerHeight;
-  canvas.width = width;
-  canvas.height = height;
+  canvas.width = width * scale;
+  canvas.height = height * scale;
   canvas.mozOpaque = true;
 
   let ctx = canvas.getContext("2d");
 
+  ctx.scale(scale, scale);
   ctx.drawWindow(content, content.scrollX, content.scrollY, width, height, "#fff");
 
   sendAsyncMessage("Eyedropper:Screenshot", canvas.toDataURL());
 }
--- a/browser/devtools/performance/modules/logic/marker-utils.js
+++ b/browser/devtools/performance/modules/logic/marker-utils.js
@@ -357,17 +357,17 @@ const Formatters = {
       return {
         [L10N.getStr("marker.field.causeName")]: cause
       };
     }
   },
 
   GCFields: function (marker) {
     let fields = Object.create(null);
-    let cause = marker.cause;
+    let cause = marker.causeName;
     let label = L10N.getStr(`marker.gcreason.label.${cause}`) || cause;
 
     fields[L10N.getStr("marker.field.causeName")] = label;
 
     if ("nonincrementalReason" in marker) {
       fields[L10N.getStr("marker.field.nonIncrementalCause")] = marker.nonincrementalReason;
     }
 
--- a/browser/devtools/performance/performance-controller.js
+++ b/browser/devtools/performance/performance-controller.js
@@ -85,17 +85,17 @@ const EVENTS = {
   // Emitted by the PerformanceView on clear button click
   UI_CLEAR_RECORDINGS: "Performance:UI:ClearRecordings",
 
   // Emitted by the PerformanceView on record button click
   UI_START_RECORDING: "Performance:UI:StartRecording",
   UI_STOP_RECORDING: "Performance:UI:StopRecording",
 
   // Emitted by the PerformanceView on import button click
-  UI_RECORDING_IMPORTED: "Performance:UI:ImportRecording",
+  UI_IMPORT_RECORDING: "Performance:UI:ImportRecording",
   // Emitted by the RecordingsView on export button click
   UI_EXPORT_RECORDING: "Performance:UI:ExportRecording",
 
   // When a new recording is being tracked in the panel.
   NEW_RECORDING: "Performance:NewRecording",
 
   // When a recording is started or stopped or stopping via the PerformanceController
   RECORDING_STATE_CHANGE: "Performance:RecordingStateChange",
@@ -221,17 +221,17 @@ let PerformanceController = {
 
     this._prefs = require("devtools/performance/global").PREFS;
     this._prefs.on("pref-changed", this._onPrefChanged);
 
     gFront.on("*", this._onFrontEvent);
     ToolbarView.on(EVENTS.PREF_CHANGED, this._onPrefChanged);
     PerformanceView.on(EVENTS.UI_START_RECORDING, this.startRecording);
     PerformanceView.on(EVENTS.UI_STOP_RECORDING, this.stopRecording);
-    PerformanceView.on(EVENTS.UI_RECORDING_IMPORTED, this.importRecording);
+    PerformanceView.on(EVENTS.UI_IMPORT_RECORDING, this.importRecording);
     PerformanceView.on(EVENTS.UI_CLEAR_RECORDINGS, this.clearRecordings);
     RecordingsView.on(EVENTS.UI_EXPORT_RECORDING, this.exportRecording);
     RecordingsView.on(EVENTS.RECORDING_SELECTED, this._onRecordingSelectFromView);
 
     gDevTools.on("pref-changed", this._onThemeChanged);
   }),
 
   /**
@@ -239,17 +239,17 @@ let PerformanceController = {
    */
   destroy: function() {
     this._prefs.off("pref-changed", this._onPrefChanged);
 
     gFront.off("*", this._onFrontEvent);
     ToolbarView.off(EVENTS.PREF_CHANGED, this._onPrefChanged);
     PerformanceView.off(EVENTS.UI_START_RECORDING, this.startRecording);
     PerformanceView.off(EVENTS.UI_STOP_RECORDING, this.stopRecording);
-    PerformanceView.off(EVENTS.UI_RECORDING_IMPORTED, this.importRecording);
+    PerformanceView.off(EVENTS.UI_IMPORT_RECORDING, this.importRecording);
     PerformanceView.off(EVENTS.UI_CLEAR_RECORDINGS, this.clearRecordings);
     RecordingsView.off(EVENTS.UI_EXPORT_RECORDING, this.exportRecording);
     RecordingsView.off(EVENTS.RECORDING_SELECTED, this._onRecordingSelectFromView);
 
     gDevTools.off("pref-changed", this._onThemeChanged);
   },
 
   /**
--- a/browser/devtools/performance/test/browser_perf_recordings-io-01.js
+++ b/browser/devtools/performance/test/browser_perf_recordings-io-01.js
@@ -2,17 +2,17 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Tests if the performance tool is able to save and load recordings.
  */
 
 let test = Task.async(function*() {
   var { target, panel, toolbox } = yield initPerformance(SIMPLE_URL);
-  var { EVENTS, PerformanceController, DetailsView, DetailsSubview } = panel.panelWin;
+  var { EVENTS, PerformanceController, PerformanceView, DetailsView, DetailsSubview } = panel.panelWin;
 
   // Enable allocations to test the memory-calltree and memory-flamegraph.
   Services.prefs.setBoolPref(ALLOCATIONS_PREF, true);
   Services.prefs.setBoolPref(MEMORY_PREF, true);
   Services.prefs.setBoolPref(FRAMERATE_PREF, true);
 
   // Need to allow widgets to be updated while hidden, otherwise we can't use
   // `waitForWidgetsRendered`.
@@ -44,17 +44,17 @@ let test = Task.async(function*() {
 
   yield exported;
   ok(true, "The recording data appears to have been successfully saved.");
 
   // Import recording.
 
   let rerendered = waitForWidgetsRendered(panel);
   let imported = once(PerformanceController, EVENTS.RECORDING_IMPORTED);
-  yield PerformanceController.importRecording("", file);
+  PerformanceView.emit(EVENTS.UI_IMPORT_RECORDING, file);
 
   yield imported;
   ok(true, "The recording data appears to have been successfully imported.");
 
   yield rerendered;
   ok(true, "The imported data was re-rendered.");
 
   // Verify imported recording.
--- a/browser/devtools/performance/test/unit/test_marker-utils.js
+++ b/browser/devtools/performance/test/unit/test_marker-utils.js
@@ -34,20 +34,20 @@ add_task(function () {
 
   fields = Utils.getMarkerFields({ name: "DOMEvent", eventPhase: Ci.nsIDOMEvent.AT_TARGET, type: "mouseclick" });
   equal(fields.length, 2, "getMarkerFields() returns multiple fields when using a fields function");
   equal(fields[0].label, "Event Type:", "getMarkerFields() correctly returns fields via function (1)");
   equal(fields[0].value, "mouseclick", "getMarkerFields() correctly returns fields via function (2)");
   equal(fields[1].label, "Phase:", "getMarkerFields() correctly returns fields via function (3)");
   equal(fields[1].value, "Target", "getMarkerFields() correctly returns fields via function (4)");
 
-  fields = Utils.getMarkerFields({ name: "GarbageCollection", cause: "ALLOC_TRIGGER" });
+  fields = Utils.getMarkerFields({ name: "GarbageCollection", causeName: "ALLOC_TRIGGER" });
   equal(fields[0].value, "Too Many Allocations", "Uses L10N for GC reasons");
 
-  fields = Utils.getMarkerFields({ name: "GarbageCollection", cause: "NOT_A_GC_REASON" });
+  fields = Utils.getMarkerFields({ name: "GarbageCollection", causeName: "NOT_A_GC_REASON" });
   equal(fields[0].value, "NOT_A_GC_REASON", "Defaults to enum for GC reasons when not L10N'd");
 
   equal(Utils.getMarkerFields({ name: "Javascript", causeName: "Some Platform Field" })[0].value, "(Gecko)",
     "Correctly obfuscates JS markers when platform data is off.");
   Services.prefs.setBoolPref(PLATFORM_DATA_PREF, true);
   equal(Utils.getMarkerFields({ name: "Javascript", causeName: "Some Platform Field" })[0].value, "Some Platform Field",
     "Correctly deobfuscates JS markers when platform data is on.");
 
--- a/browser/locales/en-US/chrome/browser/loop/loop.properties
+++ b/browser/locales/en-US/chrome/browser/loop/loop.properties
@@ -83,19 +83,19 @@ settings_menu_item_signin=Sign In
 settings_menu_button_tooltip=Settings
 
 # Contact Strings (Panel)
 
 ## LOCALIZATION NOTE(contacts_search_placeholder): This is the placeholder text for
 ## the search field.
 contacts_search_placesholder2=Search…
 
-## LOCALIZATION NOTE (new_contact_button): This is the button to open the
+## LOCALIZATION NOTE (new_contact_button2): This is the button to open the
 ## new contact sub-panel.
-new_contact_button=New Contact
+new_contact_button2=Add new contact
 ## LOCALIZATION NOTE (contact_form_*_placeholder):
 ## These are the placeholders for the inputs for entering or editing a contact
 ## Click the 'New Contact' button to see the fields.
 contact_form_name_placeholder=Name
 contact_form_email_placeholder=Email
 contact_form_fxos_phone_placeholder=Firefox OS Phone
 contact_form_phone_placeholder2=Phone
 
@@ -126,22 +126,22 @@ import_contacts_failure_message=Some con
 ## when user's contacts have been successfully imported.
 ## Semicolon-separated list of plural forms. See:
 ## http://developer.mozilla.org/en/docs/Localization_and_Plurals
 ## In this item, don't translate the part between {{..}}
 import_contacts_success_message={{total}} contact was successfully imported.;{{total}} contacts were successfully imported.
 ## LOCALIZATION NOTE(sync_contacts_button): This button is displayed in place of
 ## importing_contacts_button once contacts have been imported once.
 sync_contacts_button=Sync Contacts
-## LOCALIZATION NOTE(no_contacts_message_heading): Title shown when user has no
+## LOCALIZATION NOTE(no_contacts_message_heading2): Title shown when user has no
 ## contacts in his address book
-no_contacts_message_heading=No contacts yet
-## LOCALIZATION NOTE(no_contacts_import_or_add): Subheading inviting the user
+no_contacts_message_heading2=No contacts yet.
+## LOCALIZATION NOTE(no_contacts_import_or_add2): Subheading inviting the user
 ## to add people to his contact list
-no_contacts_import_or_add=Import or add someone
+no_contacts_import_or_add2=Add someone!
 ## LOCALIZATION NOTE(no_conversations_message_heading): Title shown when user
 ## has no conversations available.
 no_conversations_message_heading=There are no conversations yet
 ## LOCALIZATION NOTE(no_converastions_start_message): Subheading inviting the
 ## user to start a new conversation.
 no_conversations_start_message=start a new conversation!
 ## LOCALIZATION NOTE(no_search_results_message_heading): Title to show when
 ## search returned no matching results.
@@ -203,18 +203,18 @@ audio_call_menu_button=Audio Conversatio
 ## pop-up menu next to the contact's name.
 video_call_menu_button=Video Conversation
 
 ## LOCALIZATION NOTE(gravatars_promo_message): The {{learn_more}} part will be
 ## replaced with a link with the text content of gravatars_promo_message_learnmore.
 gravatars_promo_message=You can automatically add profile icons to your contacts \
   by sharing their email addresses with Gravatar. {{learn_more}}.
 gravatars_promo_message_learnmore=Learn more
-gravatars_promo_button_nothanks=No Thanks
-gravatars_promo_button_use=Use Profile Icons
+gravatars_promo_button_nothanks2=No, thanks
+gravatars_promo_button_use2=Use profile icons
 
 # Conversation Window Strings
 
 initiate_call_button_label2=Ready to start your conversation?
 incoming_call_title2=Conversation Request
 incoming_call_accept_button=Accept
 incoming_call_accept_audio_only_tooltip=Accept with voice
 incoming_call_accept_audio_video_tooltip=Accept with video
--- a/browser/themes/shared/newtab/newTab.inc.css
+++ b/browser/themes/shared/newtab/newTab.inc.css
@@ -162,17 +162,20 @@
 
 .newtab-suggested[active] {
   background-color: rgba(51, 51, 51, 0.95);
   border: 0;
   color: white;
 }
 
 .newtab-site:hover .newtab-title {
-  color: #222;
+  color: white;
+  background-color: black;
+  border: 1px solid black;
+  border-top: 1px solid white;
 }
 
 .newtab-site[pinned] .newtab-title {
   -moz-padding-start: 24px;
 }
 
 .newtab-site[pinned] .newtab-title::before {
   background-image: -moz-image-rect(url("chrome://browser/skin/newtab/controls.svg"), 7, 278, 28, 266);
--- a/layout/base/PositionedEventTargeting.cpp
+++ b/layout/base/PositionedEventTargeting.cpp
@@ -71,17 +71,17 @@ namespace mozilla {
 struct EventRadiusPrefs
 {
   uint32_t mVisitedWeight; // in percent, i.e. default is 100
   uint32_t mSideRadii[4]; // TRBL order, in millimetres
   bool mEnabled;
   bool mRegistered;
   bool mTouchOnly;
   bool mRepositionEventCoords;
-  bool mTouchClusterDetectionDisabled;
+  bool mTouchClusterDetectionEnabled;
   uint32_t mLimitReadableSize;
 };
 
 static EventRadiusPrefs sMouseEventRadiusPrefs;
 static EventRadiusPrefs sTouchEventRadiusPrefs;
 
 static const EventRadiusPrefs*
 GetPrefsFor(EventClassID aEventClassID)
@@ -120,18 +120,18 @@ GetPrefsFor(EventClassID aEventClassID)
           "ui.mouse.radius.inputSource.touchOnly", true);
     } else {
       prefs->mTouchOnly = false;
     }
 
     nsPrintfCString repositionPref("ui.%s.radius.reposition", prefBranch);
     Preferences::AddBoolVarCache(&prefs->mRepositionEventCoords, repositionPref.get(), false);
 
-    nsPrintfCString touchClusterPref("ui.zoomedview.disabled", prefBranch);
-    Preferences::AddBoolVarCache(&prefs->mTouchClusterDetectionDisabled, touchClusterPref.get(), true);
+    nsPrintfCString touchClusterPref("ui.zoomedview.enabled", prefBranch);
+    Preferences::AddBoolVarCache(&prefs->mTouchClusterDetectionEnabled, touchClusterPref.get(), false);
 
     nsPrintfCString limitReadableSizePref("ui.zoomedview.limitReadableSize", prefBranch);
     Preferences::AddUintVarCache(&prefs->mLimitReadableSize, limitReadableSizePref.get(), 8);
   }
 
   return prefs;
 }
 
@@ -446,17 +446,17 @@ GetClosest(nsIFrame* aRoot, const nsPoin
  * In both cases, the frame is considered as clickable.
  *
  * Frames with a too small size will return false.
  * In this case, the frame is considered not clickable.
  */
 static bool
 IsElementClickableAndReadable(nsIFrame* aFrame, WidgetGUIEvent* aEvent, const EventRadiusPrefs* aPrefs)
 {
-  if (aPrefs->mTouchClusterDetectionDisabled) {
+  if (!aPrefs->mTouchClusterDetectionEnabled) {
     return true;
   }
 
   if (aEvent->mClass != eMouseEventClass) {
     return true;
   }
 
   uint32_t limitReadableSize = aPrefs->mLimitReadableSize;
@@ -573,17 +573,17 @@ FindFrameTargetedByInputEvent(WidgetGUIE
 
   int32_t elementsInCluster = 0;
 
   nsIFrame* closestClickable =
     GetClosest(aRootFrame, aPointRelativeToRootFrame, targetRect, prefs,
                restrictToDescendants, clickableAncestor, candidates,
                &elementsInCluster);
   if (closestClickable) {
-    if ((!prefs->mTouchClusterDetectionDisabled && elementsInCluster > 1) ||
+    if ((prefs->mTouchClusterDetectionEnabled && elementsInCluster > 1) ||
         (!IsElementClickableAndReadable(closestClickable, aEvent, prefs))) {
       if (aEvent->mClass == eMouseEventClass) {
         WidgetMouseEventBase* mouseEventBase = aEvent->AsMouseEventBase();
         mouseEventBase->hitCluster = true;
       }
     }
     target = closestClickable;
   }
--- a/mobile/android/app/mobile.js
+++ b/mobile/android/app/mobile.js
@@ -409,17 +409,17 @@ pref("devtools.debugger.unix-domain-sock
 pref("devtools.remote.usb.enabled", false);
 pref("devtools.remote.wifi.enabled", false);
 
 pref("font.size.inflation.minTwips", 0);
 
 // When true, zooming will be enabled on all sites, even ones that declare user-scalable=no.
 pref("browser.ui.zoom.force-user-scalable", false);
 
-pref("ui.zoomedview.disabled", false);
+pref("ui.zoomedview.enabled", true);
 pref("ui.zoomedview.limitReadableSize", 8); // value in layer pixels
 pref("ui.zoomedview.defaultZoomFactor", 2);
 pref("ui.zoomedview.simplified", true); // Do not display all the zoomed view controls
 
 pref("ui.touch.radius.enabled", false);
 pref("ui.touch.radius.leftmm", 3);
 pref("ui.touch.radius.topmm", 5);
 pref("ui.touch.radius.rightmm", 3);
--- a/mobile/android/base/BrowserApp.java
+++ b/mobile/android/base/BrowserApp.java
@@ -772,17 +772,17 @@ public class BrowserApp extends GeckoApp
             showTabQueuePromptIfApplicable(intent);
         } else if (GuestSession.NOTIFICATION_INTENT.equals(action)) {
             GuestSession.handleIntent(this, intent);
         } else if (TabQueueHelper.LOAD_URLS_ACTION.equals(action)) {
             Telemetry.sendUIEvent(TelemetryContract.Event.ACTION, TelemetryContract.Method.NOTIFICATION, "tabqueue");
         }
 
         if (HardwareUtils.isTablet()) {
-            mTabStrip = (Refreshable) (((ViewStub) findViewById(R.id.new_tablet_tab_strip)).inflate());
+            mTabStrip = (Refreshable) (((ViewStub) findViewById(R.id.tablet_tab_strip)).inflate());
         }
 
         ((GeckoApp.MainLayout) mMainLayout).setTouchEventInterceptor(new HideOnTouchListener());
         ((GeckoApp.MainLayout) mMainLayout).setMotionEventInterceptor(new MotionEventInterceptor() {
             @Override
             public boolean onInterceptMotionEvent(View view, MotionEvent event) {
                 // If we get a gamepad panning MotionEvent while the focus is not on the layerview,
                 // put the focus on the layerview and carry on
--- a/mobile/android/base/GeckoApp.java
+++ b/mobile/android/base/GeckoApp.java
@@ -1957,16 +1957,18 @@ public abstract class GeckoApp
                 if (rec != null) {
                     rec.setCurrentSession(currentSession);
                     rec.processDelayed();
                 } else {
                     Log.w(LOGTAG, "Can't record session: rec is null.");
                 }
             }
         });
+
+        RestrictedProfiles.update(this);
     }
 
     @Override
     public void onWindowFocusChanged(boolean hasFocus) {
         super.onWindowFocusChanged(hasFocus);
 
         if (!mWindowFocusInitialized && hasFocus) {
             mWindowFocusInitialized = true;
--- a/mobile/android/base/Makefile.in
+++ b/mobile/android/base/Makefile.in
@@ -306,17 +306,17 @@ android_res_files := $(filter-out $(not_
 # suggestedsites.json. The trailing semi-colon defines an empty
 # recipe: defining no recipe at all causes Make to treat the target
 # differently, in a way that defeats our dependencies.
 res/values/strings.xml: .locales.deps ;
 res/raw/browsersearch.json: .locales.deps ;
 res/raw/suggestedsites.json: .locales.deps ;
 
 all_resources = \
-  $(CURDIR)/AndroidManifest.xml \
+  $(abspath $(CURDIR)/AndroidManifest.xml) \
   $(android_res_files) \
   $(ANDROID_GENERATED_RESFILES) \
   $(NULL)
 
 # For GeckoView, we want a zip of an Android res/ directory that
 # merges the contents of all the ANDROID_RES_DIRS.  The inner res/
 # directory must have the Android resource two-layer hierarchy.
 
@@ -415,18 +415,18 @@ endef
 # packaging.  It doesn't write the normal ap_, or R.java, since we
 # don't want the packaging step to write anything that would make a
 # further no-op build do work.  See also
 # toolkit/mozapps/installer/packager.mk.
 
 # .aapt.deps: $(all_resources)
 $(eval $(call aapt_command,.aapt.deps,$(all_resources),gecko.ap_,generated/,./))
 
-# .aapt.nodeps: $(CURDIR)/AndroidManifest.xml FORCE
-$(eval $(call aapt_command,.aapt.nodeps,$(CURDIR)/AndroidManifest.xml FORCE,gecko-nodeps.ap_,gecko-nodeps/,gecko-nodeps/))
+# .aapt.nodeps: $(abspath $(CURDIR)/AndroidManifest.xml) FORCE
+$(eval $(call aapt_command,.aapt.nodeps,$(abspath $(CURDIR)/AndroidManifest.xml) FORCE,gecko-nodeps.ap_,gecko-nodeps/,gecko-nodeps/))
 
 # Override the Java settings with some specific android settings
 include $(topsrcdir)/config/android-common.mk
 
 update-generated-wrappers:
 	@cp $(CURDIR)/jni-stubs.inc $(topsrcdir)/mozglue/android
 	@cp $(CURDIR)/GeneratedJNIWrappers.cpp $(CURDIR)/GeneratedJNIWrappers.h $(CURDIR)/GeneratedJNINatives.h $(topsrcdir)/widget/android
 	@echo Updated generated JNI code
@@ -441,16 +441,17 @@ update-generated-wrappers:
 	$(REPORT_BUILD)
 	$(MAKE) -C ../locales
 	$(MAKE) -C ../chrome
 	$(MAKE) -C ../components
 	$(MAKE) -C ../modules
 	$(MAKE) -C ../app
 	$(MAKE) -C ../themes/core
 	$(MAKE) -C ../installer stage-package
+	$(MKDIR) -p $(@D)
 	rsync --update $(DIST)/fennec/$(notdir $(OMNIJAR_NAME)) $@
 	$(RM) $(DIST)/fennec/$(notdir $(OMNIJAR_NAME))
 
 # Targets built very early during a Gradle build.
 gradle-targets: .aapt.deps
 
 gradle-omnijar: $(abspath $(DIST)/fennec/$(OMNIJAR_NAME))
 
--- a/mobile/android/base/RestrictedProfiles.java
+++ b/mobile/android/base/RestrictedProfiles.java
@@ -38,34 +38,42 @@ public class RestrictedProfiles {
     public static synchronized RestrictionConfiguration createConfiguration(Context context) {
         if (configuration != null) {
             // This method is synchronized and another thread might already have created the configuration.
             return configuration;
         }
 
         if (isGuestProfile(context)) {
             return new GuestProfileConfiguration();
-        } else if(isRestrictedProfile(context)) {
+        } else if (isRestrictedProfile(context)) {
             return new RestrictedProfileConfiguration(context);
         } else {
             return new DefaultConfiguration();
         }
     }
 
     private static boolean isGuestProfile(Context context) {
+        if (configuration != null) {
+            return configuration instanceof GuestProfileConfiguration;
+        }
+
         GeckoAppShell.GeckoInterface geckoInterface = GeckoAppShell.getGeckoInterface();
         if (geckoInterface != null) {
             return geckoInterface.getProfile().inGuestMode();
         }
 
         return GeckoProfile.get(context).inGuestMode();
     }
 
     @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
     public static boolean isRestrictedProfile(Context context) {
+        if (configuration != null) {
+            return configuration instanceof RestrictedProfileConfiguration;
+        }
+
         if (Versions.preJBMR2) {
             // Early versions don't support restrictions at all
             return false;
         }
 
         final UserManager mgr = (UserManager) context.getSystemService(Context.USER_SERVICE);
         final Bundle restrictions = new Bundle();
         restrictions.putAll(mgr.getApplicationRestrictions(context.getPackageName()));
@@ -76,16 +84,20 @@ public class RestrictedProfiles {
                 // At least one restriction is enabled -> We are a restricted profile
                 return true;
             }
         }
 
         return false;
     }
 
+    public static void update(Context context) {
+        getConfiguration(context).update();
+    }
+
     private static Restriction geckoActionToRestriction(int action) {
         for (Restriction rest : Restriction.values()) {
             if (rest.id == action) {
                 return rest;
             }
         }
 
         throw new IllegalArgumentException("Unknown action " + action);
--- a/mobile/android/base/locales/en-US/android_strings.dtd
+++ b/mobile/android/base/locales/en-US/android_strings.dtd
@@ -410,18 +410,18 @@ size. -->
 <!ENTITY doorhanger_login_select_title "Copy password from">
 
 <!ENTITY pref_titlebar_mode "Title bar">
 <!ENTITY pref_titlebar_mode_title "Show page title">
 <!ENTITY pref_titlebar_mode_url "Show page address">
 
 <!-- Localization note (pref_prevent_magnifying_glass): Label for setting that controls
      whether or not the magnifying glass is disabled. -->
-<!ENTITY pref_prevent_magnifying_glass "Disable magnifying glass ">
-<!ENTITY pref_prevent_magnifying_glass_summary "Prevent magnifying glass for use in resolving ambiguous screen touches">
+<!ENTITY pref_magnifying_glass_enabled "Magnify small areas">
+<!ENTITY pref_magnifying_glass_enabled_summary "Enlarge links and form fields when tapping near them">
 
 <!-- Localization note (pref_scroll_title_bar2): Label for setting that controls
      whether or not the dynamic toolbar is enabled. -->
 <!ENTITY pref_scroll_title_bar2 "Full-screen browsing">
 <!ENTITY pref_scroll_title_bar_summary "Hide the &brandShortName; title bar when scrolling down a page">
 
 <!ENTITY pref_tab_queue_title2 "Open multiple links">
 <!ENTITY pref_tab_queue_summary3 "Save them until the next time you open &brandShortName;">
--- a/mobile/android/base/preferences/GeckoPreferences.java
+++ b/mobile/android/base/preferences/GeckoPreferences.java
@@ -112,17 +112,17 @@ OnSharedPreferenceChangeListener
 
     // These match keys in resources/xml*/preferences*.xml
     private static final String PREFS_SEARCH_RESTORE_DEFAULTS = NON_PREF_PREFIX + "search.restore_defaults";
     private static final String PREFS_DATA_REPORTING_PREFERENCES = NON_PREF_PREFIX + "datareporting.preferences";
     private static final String PREFS_TELEMETRY_ENABLED = "toolkit.telemetry.enabled";
     private static final String PREFS_CRASHREPORTER_ENABLED = "datareporting.crashreporter.submitEnabled";
     private static final String PREFS_MENU_CHAR_ENCODING = "browser.menu.showCharacterEncoding";
     private static final String PREFS_MP_ENABLED = "privacy.masterpassword.enabled";
-    private static final String PREFS_DISABLE_ZOOMED_VIEW = "ui.zoomedview.disabled";
+    private static final String PREFS_ZOOMED_VIEW_ENABLED = "ui.zoomedview.enabled";
     private static final String PREFS_UPDATER_AUTODOWNLOAD = "app.update.autodownload";
     private static final String PREFS_UPDATER_URL = "app.update.url.android";
     private static final String PREFS_GEO_REPORTING = NON_PREF_PREFIX + "app.geo.reportdata";
     private static final String PREFS_GEO_LEARN_MORE = NON_PREF_PREFIX + "geo.learn_more";
     private static final String PREFS_HEALTHREPORT_LINK = NON_PREF_PREFIX + "healthreport.link";
     private static final String PREFS_DEVTOOLS_REMOTE_USB_ENABLED = "devtools.remote.usb.enabled";
     private static final String PREFS_DEVTOOLS_REMOTE_WIFI_ENABLED = "devtools.remote.wifi.enabled";
     private static final String PREFS_DISPLAY_REFLOW_ON_ZOOM = "browser.zoom.reflowOnZoom";
@@ -730,17 +730,17 @@ OnSharedPreferenceChangeListener
             } else {
                 pref.setOnPreferenceChangeListener(this);
                 if (PREFS_UPDATER_AUTODOWNLOAD.equals(key)) {
                     if (!AppConstants.MOZ_UPDATER) {
                         preferences.removePreference(pref);
                         i--;
                         continue;
                     }
-                } else if (PREFS_DISABLE_ZOOMED_VIEW.equals(key)) {
+                } else if (PREFS_ZOOMED_VIEW_ENABLED.equals(key)) {
                     // Only enable the ZoomedView / magnifying pref on Nightly.
                     if (!AppConstants.NIGHTLY_BUILD) {
                         preferences.removePreference(pref);
                         i--;
                         continue;
                     }
                 } else if (PREFS_DISPLAY_REFLOW_ON_ZOOM.equals(key)) {
                     // Remove UI for reflow on release builds.
--- a/mobile/android/base/resources/color-large-v11/tab_strip_item_bg.xml
+++ b/mobile/android/base/resources/color-large-v11/tab_strip_item_bg.xml
@@ -27,20 +27,20 @@
     <item android:state_checked="true"
           gecko:state_light="true"
           android:color="@color/background_normal_lwt" />
     <item android:state_checked="true"
           gecko:state_dark="true"
           android:color="@color/background_normal_lwt" />
     <item android:state_pressed="true"
           gecko:state_light="true"
-          android:color="@color/new_tablet_highlight_lwt" />
+          android:color="@color/tablet_highlight_lwt" />
     <item android:state_pressed="true"
           gecko:state_dark="true"
-          android:color="@color/new_tablet_highlight_dark_lwt" />
+          android:color="@color/tablet_highlight_dark_lwt" />
 
     <item android:state_checked="true"
           android:color="@color/toolbar_grey" />
 
     <item android:state_pressed="true"
           android:color="@color/tabs_tray_grey_pressed" />
 
     <item android:color="@android:color/transparent"/>
--- a/mobile/android/base/resources/drawable-large-v11/browser_toolbar_action_bar_button.xml
+++ b/mobile/android/base/resources/drawable-large-v11/browser_toolbar_action_bar_button.xml
@@ -5,69 +5,69 @@
 
 <selector xmlns:android="http://schemas.android.com/apk/res/android"
           xmlns:gecko="http://schemas.android.com/apk/res-auto">
 
    <item gecko:state_private="true"
          android:state_pressed="true"
          android:state_enabled="true">
 
-        <inset android:insetTop="@dimen/new_tablet_browser_toolbar_menu_item_inset_vertical"
-               android:insetBottom="@dimen/new_tablet_browser_toolbar_menu_item_inset_vertical"
-               android:insetLeft="@dimen/new_tablet_browser_toolbar_menu_item_inset_horizontal"
-               android:insetRight="@dimen/new_tablet_browser_toolbar_menu_item_inset_horizontal">
+        <inset android:insetTop="@dimen/tablet_browser_toolbar_menu_item_inset_vertical"
+               android:insetBottom="@dimen/tablet_browser_toolbar_menu_item_inset_vertical"
+               android:insetLeft="@dimen/tablet_browser_toolbar_menu_item_inset_horizontal"
+               android:insetRight="@dimen/tablet_browser_toolbar_menu_item_inset_horizontal">
             <shape android:shape="rectangle">
                 <solid android:color="@color/text_and_tabs_tray_grey"/>
-                <corners android:radius="@dimen/new_tablet_browser_toolbar_menu_item_corner_radius"/>
+                <corners android:radius="@dimen/tablet_browser_toolbar_menu_item_corner_radius"/>
             </shape>
         </inset>
 
     </item>
 
     <item gecko:state_private="true"
           android:state_focused="true"
           android:state_pressed="false">
 
-        <inset android:insetTop="@dimen/new_tablet_browser_toolbar_menu_item_inset_vertical"
-               android:insetBottom="@dimen/new_tablet_browser_toolbar_menu_item_inset_vertical"
-               android:insetLeft="@dimen/new_tablet_browser_toolbar_menu_item_inset_horizontal"
-               android:insetRight="@dimen/new_tablet_browser_toolbar_menu_item_inset_horizontal">
+        <inset android:insetTop="@dimen/tablet_browser_toolbar_menu_item_inset_vertical"
+               android:insetBottom="@dimen/tablet_browser_toolbar_menu_item_inset_vertical"
+               android:insetLeft="@dimen/tablet_browser_toolbar_menu_item_inset_horizontal"
+               android:insetRight="@dimen/tablet_browser_toolbar_menu_item_inset_horizontal">
             <shape android:shape="rectangle">
                 <solid android:color="@color/placeholder_active_grey"/>
-                <corners android:radius="@dimen/new_tablet_browser_toolbar_menu_item_corner_radius"/>
+                <corners android:radius="@dimen/tablet_browser_toolbar_menu_item_corner_radius"/>
             </shape>
         </inset>
 
     </item>
 
     <item android:state_pressed="true"
           android:state_enabled="true">
 
-        <inset android:insetTop="@dimen/new_tablet_browser_toolbar_menu_item_inset_vertical"
-               android:insetBottom="@dimen/new_tablet_browser_toolbar_menu_item_inset_vertical"
-               android:insetLeft="@dimen/new_tablet_browser_toolbar_menu_item_inset_horizontal"
-               android:insetRight="@dimen/new_tablet_browser_toolbar_menu_item_inset_horizontal">
+        <inset android:insetTop="@dimen/tablet_browser_toolbar_menu_item_inset_vertical"
+               android:insetBottom="@dimen/tablet_browser_toolbar_menu_item_inset_vertical"
+               android:insetLeft="@dimen/tablet_browser_toolbar_menu_item_inset_horizontal"
+               android:insetRight="@dimen/tablet_browser_toolbar_menu_item_inset_horizontal">
             <shape android:shape="rectangle">
                 <solid android:color="@color/toolbar_grey_pressed"/>
-                <corners android:radius="@dimen/new_tablet_browser_toolbar_menu_item_corner_radius"/>
+                <corners android:radius="@dimen/tablet_browser_toolbar_menu_item_corner_radius"/>
             </shape>
         </inset>
 
     </item>
 
     <item android:state_focused="true"
           android:state_pressed="false">
 
-        <inset android:insetTop="@dimen/new_tablet_browser_toolbar_menu_item_inset_vertical"
-               android:insetBottom="@dimen/new_tablet_browser_toolbar_menu_item_inset_vertical"
-               android:insetLeft="@dimen/new_tablet_browser_toolbar_menu_item_inset_horizontal"
-               android:insetRight="@dimen/new_tablet_browser_toolbar_menu_item_inset_horizontal">
+        <inset android:insetTop="@dimen/tablet_browser_toolbar_menu_item_inset_vertical"
+               android:insetBottom="@dimen/tablet_browser_toolbar_menu_item_inset_vertical"
+               android:insetLeft="@dimen/tablet_browser_toolbar_menu_item_inset_horizontal"
+               android:insetRight="@dimen/tablet_browser_toolbar_menu_item_inset_horizontal">
             <shape android:shape="rectangle">
-                <solid android:color="@color/new_tablet_highlight_focused"/>
-                <corners android:radius="@dimen/new_tablet_browser_toolbar_menu_item_corner_radius"/>
+                <solid android:color="@color/tablet_highlight_focused"/>
+                <corners android:radius="@dimen/tablet_browser_toolbar_menu_item_corner_radius"/>
             </shape>
         </inset>
 
     </item>
 
     <item>
         <shape android:shape="rectangle">
             <solid android:color="@android:color/transparent"/>
--- a/mobile/android/base/resources/drawable-large-v11/tab_strip_button.xml
+++ b/mobile/android/base/resources/drawable-large-v11/tab_strip_button.xml
@@ -4,38 +4,38 @@
    - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
 
 <selector xmlns:android="http://schemas.android.com/apk/res/android"
           xmlns:gecko="http://schemas.android.com/apk/res-auto">
 
     <item android:state_pressed="true"
           android:state_enabled="true">
 
-        <inset android:insetTop="@dimen/new_tablet_tab_strip_button_inset"
-               android:insetBottom="@dimen/new_tablet_tab_strip_button_inset"
-               android:insetLeft="@dimen/new_tablet_tab_strip_button_inset"
-               android:insetRight="@dimen/new_tablet_tab_strip_button_inset">
+        <inset android:insetTop="@dimen/tablet_tab_strip_button_inset"
+               android:insetBottom="@dimen/tablet_tab_strip_button_inset"
+               android:insetLeft="@dimen/tablet_tab_strip_button_inset"
+               android:insetRight="@dimen/tablet_tab_strip_button_inset">
             <shape android:shape="rectangle">
                 <solid android:color="@color/highlight_dark"/>
-                <corners android:radius="@dimen/new_tablet_browser_toolbar_menu_item_corner_radius"/>
+                <corners android:radius="@dimen/tablet_browser_toolbar_menu_item_corner_radius"/>
             </shape>
         </inset>
 
     </item>
 
     <item android:state_focused="true"
           android:state_pressed="false">
 
-        <inset android:insetTop="@dimen/new_tablet_tab_strip_button_inset"
-               android:insetBottom="@dimen/new_tablet_tab_strip_button_inset"
-               android:insetLeft="@dimen/new_tablet_tab_strip_button_inset"
-               android:insetRight="@dimen/new_tablet_tab_strip_button_inset">
+        <inset android:insetTop="@dimen/tablet_tab_strip_button_inset"
+               android:insetBottom="@dimen/tablet_tab_strip_button_inset"
+               android:insetLeft="@dimen/tablet_tab_strip_button_inset"
+               android:insetRight="@dimen/tablet_tab_strip_button_inset">
             <shape android:shape="rectangle">
-                <solid android:color="@color/new_tablet_highlight_focused"/>
-                <corners android:radius="@dimen/new_tablet_browser_toolbar_menu_item_corner_radius"/>
+                <solid android:color="@color/tablet_highlight_focused"/>
+                <corners android:radius="@dimen/tablet_browser_toolbar_menu_item_corner_radius"/>
             </shape>
         </inset>
 
     </item>
 
     <item>
         <shape android:shape="rectangle">
             <solid android:color="@android:color/transparent"/>
--- a/mobile/android/base/resources/drawable-large-v11/url_bar_nav_button.xml
+++ b/mobile/android/base/resources/drawable-large-v11/url_bar_nav_button.xml
@@ -19,17 +19,17 @@
 
     <!-- pressed state -->
     <item android:state_pressed="true"
           android:drawable="@color/toolbar_grey_pressed"/>
 
     <!-- focused state -->
     <item android:state_focused="true"
           android:state_pressed="false"
-          android:drawable="@color/new_tablet_highlight_focused"/>
+          android:drawable="@color/tablet_highlight_focused"/>
 
     <!-- private browsing mode -->
     <item gecko:state_private="true"
           android:drawable="@color/tabs_tray_grey_pressed"/>
 
     <!-- normal mode -->
     <item android:drawable="@color/toolbar_grey"/>
 
--- a/mobile/android/base/resources/layout-large-v11/browser_toolbar.xml
+++ b/mobile/android/base/resources/layout-large-v11/browser_toolbar.xml
@@ -6,17 +6,17 @@
 <merge xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:gecko="http://schemas.android.com/apk/res-auto">
 
     <ImageView android:id="@+id/url_bar_entry"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout_alignLeft="@+id/back"
                android:layout_toLeftOf="@id/menu_items"
-               android:layout_marginLeft="@dimen/new_tablet_nav_button_width_half"
+               android:layout_marginLeft="@dimen/tablet_nav_button_width_half"
                android:layout_marginTop="10dp"
                android:layout_marginBottom="10dp"
                android:duplicateParentState="true"
                android:clickable="false"
                android:focusable="false"
                android:background="@drawable/url_bar_entry"/>
 
     <!-- The attributes statically defined here are for the expanded
@@ -46,24 +46,24 @@
             android:paddingBottom="0dp"
             android:layout_marginTop="11.5dp"
             android:layout_marginBottom="11.5dp"
             android:layout_gravity="center_vertical"
             android:layout_centerVertical="true"
             android:src="@drawable/ic_menu_forward"
             android:background="@drawable/url_bar_nav_button"
             android:alpha="0"
-            android:layout_width="@dimen/new_tablet_nav_button_width_plus_half"
-            android:layout_marginLeft="@dimen/new_tablet_nav_button_width_half"
+            android:layout_width="@dimen/tablet_nav_button_width_plus_half"
+            android:layout_marginLeft="@dimen/tablet_nav_button_width_half"
             android:paddingLeft="18dp"/>
 
     <org.mozilla.gecko.toolbar.BackButton android:id="@id/back"
                                           style="@style/UrlBar.ImageButton"
-                                          android:layout_width="@dimen/new_tablet_nav_button_width"
-                                          android:layout_height="@dimen/new_tablet_nav_button_width"
+                                          android:layout_width="@dimen/tablet_nav_button_width"
+                                          android:layout_height="@dimen/tablet_nav_button_width"
                                           android:layout_centerVertical="true"
                                           android:layout_marginLeft="12dp"
                                           android:layout_alignParentLeft="true"
                                           android:src="@drawable/ic_menu_back"
                                           android:contentDescription="@string/back"
                                           android:background="@drawable/url_bar_nav_button"/>
 
     <org.mozilla.gecko.toolbar.ToolbarEditLayout android:id="@+id/edit_layout"
--- a/mobile/android/base/resources/layout-large-v11/tab_strip_inner.xml
+++ b/mobile/android/base/resources/layout-large-v11/tab_strip_inner.xml
@@ -12,15 +12,15 @@
         android:layout_weight="1"
         android:paddingTop="8dp"/>
 
     <!-- The right margin creates a "dead area" on the right side of the button
          which we compensate for with a touch delegate. See TabStrip -->
     <org.mozilla.gecko.widget.ThemedImageButton
         android:id="@+id/add_tab"
         style="@style/UrlBar.ImageButton"
-        android:layout_width="@dimen/new_tablet_tab_strip_height"
+        android:layout_width="@dimen/tablet_tab_strip_height"
         android:src="@drawable/tab_strip_add_tab"
         android:contentDescription="@string/new_tab"
         android:layout_marginRight="9dp"
         android:background="@drawable/tab_strip_button"/>
 
 </merge>
--- a/mobile/android/base/resources/layout-large-v11/tab_strip_item.xml
+++ b/mobile/android/base/resources/layout-large-v11/tab_strip_item.xml
@@ -2,12 +2,12 @@
 <!-- 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/. -->
 
 <!-- The paddings are asymmetric here to compensate the padding around the
      the close button within the TabStripItemView -->
 <org.mozilla.gecko.tabs.TabStripItemView
     xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="@dimen/new_tablet_tab_strip_item_width"
+    android:layout_width="@dimen/tablet_tab_strip_item_width"
     android:layout_height="match_parent"
     android:paddingLeft="28dp"
     android:paddingRight="12dp"/>
--- a/mobile/android/base/resources/layout-large-v11/tabs_panel_back_button.xml
+++ b/mobile/android/base/resources/layout-large-v11/tabs_panel_back_button.xml
@@ -7,11 +7,11 @@
                                            xmlns:gecko="http://schemas.android.com/apk/res-auto"
                                            android:id="@+id/nav_back"
                                            android:layout_width="@dimen/tabs_panel_button_width"
                                            android:layout_height="match_parent"
                                            android:minWidth="@dimen/tabs_panel_button_width"
                                            android:src="@drawable/tabs_panel_nav_back"
                                            android:contentDescription="@string/back"
                                            android:background="@drawable/action_bar_button_inverse"
-                                           gecko:dividerVerticalPadding="@dimen/new_tablet_tab_panel_divider_vertical_padding"
+                                           gecko:dividerVerticalPadding="@dimen/tablet_tab_panel_divider_vertical_padding"
                                            gecko:rightDivider="@drawable/tab_indicator_divider"/>
 
--- a/mobile/android/base/resources/layout-v11/tablet_tabs_item_cell.xml
+++ b/mobile/android/base/resources/layout-v11/tablet_tabs_item_cell.xml
@@ -11,19 +11,19 @@
                                            android:layout_height="wrap_content"
                                            android:gravity="center"
                                            android:orientation="vertical">
 
     <LinearLayout android:layout_width="fill_parent"
                   android:layout_height="wrap_content"
                   android:orientation="horizontal"
                   android:duplicateParentState="true"
-                  android:paddingLeft="@dimen/new_tablet_tab_highlight_stroke_width"
-                  android:paddingRight="@dimen/new_tablet_tab_highlight_stroke_width"
-                  android:paddingBottom="@dimen/new_tablet_tab_highlight_stroke_width">
+                  android:paddingLeft="@dimen/tablet_tab_highlight_stroke_width"
+                  android:paddingRight="@dimen/tablet_tab_highlight_stroke_width"
+                  android:paddingBottom="@dimen/tablet_tab_highlight_stroke_width">
 
        <org.mozilla.gecko.widget.FadedSingleColorTextView
                android:id="@+id/title"
                android:layout_width="0dip"
                android:layout_height="wrap_content"
                android:layout_weight="1.0"
                style="@style/TabLayoutItemTextAppearance"
                android:textSize="14sp"
@@ -57,20 +57,20 @@
 
     </LinearLayout>
 
     <!-- We set state_private on this View dynamically in TabsGridLayout. -->
     <org.mozilla.gecko.widget.TabThumbnailWrapper
             android:id="@+id/wrapper"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:padding="@dimen/new_tablet_tab_highlight_stroke_width"
+            android:padding="@dimen/tablet_tab_highlight_stroke_width"
             android:background="@drawable/tab_thumbnail"
             android:duplicateParentState="true">
 
         <org.mozilla.gecko.widget.ThumbnailView android:id="@+id/thumbnail"
-                                                android:layout_width="@dimen/new_tablet_tab_thumbnail_width"
-                                                android:layout_height="@dimen/new_tablet_tab_thumbnail_height"
+                                                android:layout_width="@dimen/tablet_tab_thumbnail_width"
+                                                android:layout_height="@dimen/tablet_tab_thumbnail_height"
                                                 />
 
     </org.mozilla.gecko.widget.TabThumbnailWrapper>
 
 </org.mozilla.gecko.tabs.TabsLayoutItemView>
--- a/mobile/android/base/resources/layout/gecko_app.xml
+++ b/mobile/android/base/resources/layout/gecko_app.xml
@@ -18,17 +18,17 @@
          android:id="@+id/main_layout"
          android:layout_width="match_parent"
          android:layout_height="match_parent"
          android:background="@android:color/transparent">
 
         <RelativeLayout android:id="@+id/gecko_layout"
                         android:layout_width="match_parent"
                         android:layout_height="match_parent"
-                        android:layout_below="@+id/new_tablet_tab_strip"
+                        android:layout_below="@+id/tablet_tab_strip"
                         android:layout_above="@+id/find_in_page">
 
             <include layout="@layout/shared_ui_components"/>
 
             <ViewStub android:id="@+id/zoomed_view_stub"
                       android:inflatedId="@+id/zoomed_view"
                       android:layout="@layout/zoomed_view"
                       android:layout_width="wrap_content"
@@ -93,21 +93,21 @@
              the root view, BrowserToolbar should be specified as low in the
              view hierarchy as possible. -->
 
         <LinearLayout android:id="@id/browser_chrome"
                       android:layout_width="match_parent"
                       android:layout_height="wrap_content"
                       android:orientation="vertical">
 
-            <ViewStub android:id="@+id/new_tablet_tab_strip"
-                      android:inflatedId="@id/new_tablet_tab_strip"
+            <ViewStub android:id="@+id/tablet_tab_strip"
+                      android:inflatedId="@id/tablet_tab_strip"
                       android:layout="@layout/tab_strip"
                       android:layout_width="match_parent"
-                      android:layout_height="@dimen/new_tablet_tab_strip_height"
+                      android:layout_height="@dimen/tablet_tab_strip_height"
                       android:visibility="gone"/>
 
             <org.mozilla.gecko.widget.GeckoViewFlipper
                 android:id="@+id/browser_actionbar"
                 android:layout_width="match_parent"
                 android:layout_height="@dimen/browser_toolbar_height_flipper"
                 android:clickable="true"
                 android:focusable="true">
--- a/mobile/android/base/resources/layout/tabs_panel_default.xml
+++ b/mobile/android/base/resources/layout/tabs_panel_default.xml
@@ -23,17 +23,17 @@
                       android:layout_width="wrap_content"
                       android:layout_height="match_parent"/>
 
             <org.mozilla.gecko.widget.IconTabWidget android:id="@+id/tab_widget"
                                                     android:layout_width="wrap_content"
                                                     android:layout_height="match_parent"
                                                     android:tabStripEnabled="false"
                                                     android:divider="@drawable/tab_indicator_divider"
-                                                    android:dividerPadding="@dimen/new_tablet_tab_panel_divider_vertical_padding"
+                                                    android:dividerPadding="@dimen/tablet_tab_panel_divider_vertical_padding"
                                                     android:layout="@layout/tabs_panel_indicator"/>
 
             <View android:layout_width="0dip"
                   android:layout_height="match_parent"
                   android:layout_weight="1.0"/>
 
             <ImageButton android:id="@+id/add_tab"
                          style="@style/UrlBar.ImageButton"
--- a/mobile/android/base/resources/values-land/dimens.xml
+++ b/mobile/android/base/resources/values-land/dimens.xml
@@ -4,10 +4,10 @@
    - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
 
 <resources>
 
     <!-- Remote Tabs static view top padding. Less in landscape on phones. -->
     <dimen name="home_remote_tabs_top_padding">16dp</dimen>
     <dimen name="page_group_height">64dp</dimen>
 
-    <dimen name="new_tablet_tab_panel_grid_padding">48dp</dimen>
+    <dimen name="tablet_tab_panel_grid_padding">48dp</dimen>
 </resources>
--- a/mobile/android/base/resources/values-large-v11/styles.xml
+++ b/mobile/android/base/resources/values-large-v11/styles.xml
@@ -1,17 +1,17 @@
 <?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/. -->
 
 <resources>
 
     <style name="UrlBar.ImageButton" parent="UrlBar.ImageButtonBase">
-        <item name="android:layout_width">@dimen/new_tablet_browser_toolbar_menu_item_width</item>
+        <item name="android:layout_width">@dimen/tablet_browser_toolbar_menu_item_width</item>
     </style>
 
     <style name="UrlBar.ImageButton.TabCount">
         <item name="android:background">@drawable/tabs_count</item>
     </style>
 
     <style name="UrlBar.Button.Container">
         <item name="android:layout_marginTop">6dp</item>
@@ -49,18 +49,18 @@
         <!-- layout_width/height doesn't work here, likely because it's
              an ImageButton, so we use padding instead.
 
              Notes:
                  * The bookmarks star is larger than the reload button
                  * The reload button contains whitespace at the top of the image to lower it -->
         <item name="android:paddingTop">19dp</item>
         <item name="android:paddingBottom">21dp</item>
-        <item name="android:paddingLeft">@dimen/new_tablet_browser_toolbar_menu_item_padding_horizontal</item>
-        <item name="android:paddingRight">@dimen/new_tablet_browser_toolbar_menu_item_padding_horizontal</item>
+        <item name="android:paddingLeft">@dimen/tablet_browser_toolbar_menu_item_padding_horizontal</item>
+        <item name="android:paddingRight">@dimen/tablet_browser_toolbar_menu_item_padding_horizontal</item>
     </style>
 
     <style name="Widget.MenuItemSecondaryActionBar" parent="Widget.MenuItemSecondaryActionBarBase">
         <item name="drawableTintList">@color/action_bar_secondary_menu_item_colors</item>
     </style>
 
     <style name="Widget.BookmarksListView" parent="Widget.HomeListView">
         <item name="android:scrollbarStyle">outsideOverlay</item>
--- a/mobile/android/base/resources/values-xlarge-land-v11/dimens.xml
+++ b/mobile/android/base/resources/values-xlarge-land-v11/dimens.xml
@@ -1,10 +1,10 @@
 <?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/. -->
 
 <resources>
 
-    <dimen name="new_tablet_tab_panel_grid_padding">64dp</dimen>
+    <dimen name="tablet_tab_panel_grid_padding">64dp</dimen>
 
 </resources>
--- a/mobile/android/base/resources/values-xlarge-v11/dimens.xml
+++ b/mobile/android/base/resources/values-xlarge-v11/dimens.xml
@@ -1,11 +1,11 @@
 <?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/. -->
 
 <resources>
 
     <dimen name="panel_grid_view_column_width">250dp</dimen>
-    <dimen name="new_tablet_tab_panel_grid_padding">48dp</dimen>
+    <dimen name="tablet_tab_panel_grid_padding">48dp</dimen>
 
 </resources>
--- a/mobile/android/base/resources/values/colors.xml
+++ b/mobile/android/base/resources/values/colors.xml
@@ -42,23 +42,23 @@
 
   <color name="background_tabs">#FF363B40</color>
   <color name="highlight">#33000000</color>
   <color name="highlight_focused">#1A000000</color>
   <color name="highlight_dark">#33FFFFFF</color>
   <color name="highlight_dark_focused">#1AFFFFFF</color>
 
   <!-- Synced w/ toolbar_grey_pressed -->
-  <color name="new_tablet_highlight_lwt">#AAD7D7DC</color>
+  <color name="tablet_highlight_lwt">#AAD7D7DC</color>
 
   <!-- Synced w/ tabs_tray_grey_pressed -->
-  <color name="new_tablet_highlight_dark_lwt">#AA45494E</color>
+  <color name="tablet_highlight_dark_lwt">#AA45494E</color>
 
   <!-- (bug 1077195) Focused state values are temporary. -->
-  <color name="new_tablet_highlight_focused">#C0C9D0</color>
+  <color name="tablet_highlight_focused">#C0C9D0</color>
 
   <!-- highlight on shaped button: 20% white over text_and_tabs_tray_grey -->
   <color name="highlight_shaped">#FF696D71</color>
 
   <!-- highlight-focused on shaped button: 10% white over text_and_tabs_tray_grey -->
   <color name="highlight_shaped_focused">#FF565B60</color>
 
   <!-- highlight on private nav button: 20% white over private_toolbar_grey -->
--- a/mobile/android/base/resources/values/dimens.xml
+++ b/mobile/android/base/resources/values/dimens.xml
@@ -23,35 +23,35 @@
     <dimen name="browser_toolbar_icon_width">48dp</dimen>
 
     <!-- favicon_size includes 4dp of right padding. We can't use margin (which would allow us to
          specify the actual size) because that would decrease the size of our hit target. -->
     <dimen name="browser_toolbar_favicon_size">21.33dip</dimen>
     <dimen name="browser_toolbar_shadow_size">2dp</dimen>
 
     <!-- If you update one of these values, update the others. -->
-    <dimen name="new_tablet_nav_button_width">42dp</dimen>
-    <dimen name="new_tablet_nav_button_width_half">21dp</dimen>
-    <dimen name="new_tablet_nav_button_width_plus_half">63dp</dimen>
+    <dimen name="tablet_nav_button_width">42dp</dimen>
+    <dimen name="tablet_nav_button_width_half">21dp</dimen>
+    <dimen name="tablet_nav_button_width_plus_half">63dp</dimen>
 
     <!-- This is the system default for the vertical padding for the divider of the TabWidget.
          Used to mimic the divider padding on the tablet tabs panel back button. -->
-    <dimen name="new_tablet_tab_panel_divider_vertical_padding">12dp</dimen>
+    <dimen name="tablet_tab_panel_divider_vertical_padding">12dp</dimen>
 
-    <dimen name="new_tablet_tab_strip_height">48dp</dimen>
-    <dimen name="new_tablet_tab_strip_item_width">208dp</dimen>
-    <dimen name="new_tablet_tab_strip_item_margin">-28dp</dimen>
-    <dimen name="new_tablet_tab_strip_fading_edge_size">15dp</dimen>
-    <dimen name="new_tablet_browser_toolbar_menu_item_width">56dp</dimen>
+    <dimen name="tablet_tab_strip_height">48dp</dimen>
+    <dimen name="tablet_tab_strip_item_width">208dp</dimen>
+    <dimen name="tablet_tab_strip_item_margin">-28dp</dimen>
+    <dimen name="tablet_tab_strip_fading_edge_size">15dp</dimen>
+    <dimen name="tablet_browser_toolbar_menu_item_width">56dp</dimen>
     <!-- Padding combines with an 18dp image to form the menu item width and height. -->
-    <dimen name="new_tablet_browser_toolbar_menu_item_padding_horizontal">19dp</dimen>
-    <dimen name="new_tablet_browser_toolbar_menu_item_inset_vertical">5dp</dimen>
-    <dimen name="new_tablet_browser_toolbar_menu_item_inset_horizontal">3dp</dimen>
-    <dimen name="new_tablet_browser_toolbar_menu_item_corner_radius">5dp</dimen>
-    <dimen name="new_tablet_tab_strip_button_inset">5dp</dimen>
+    <dimen name="tablet_browser_toolbar_menu_item_padding_horizontal">19dp</dimen>
+    <dimen name="tablet_browser_toolbar_menu_item_inset_vertical">5dp</dimen>
+    <dimen name="tablet_browser_toolbar_menu_item_inset_horizontal">3dp</dimen>
+    <dimen name="tablet_browser_toolbar_menu_item_corner_radius">5dp</dimen>
+    <dimen name="tablet_tab_strip_button_inset">5dp</dimen>
 
     <!-- Dimensions used by Favicons and FaviconView -->
     <dimen name="favicon_bg">32dp</dimen>
     <!-- Set the upper limit on the size of favicon that will be processed. Favicons larger than
          this will be downscaled to this value. If you need to use larger Favicons (Due to a UI
          redesign sometime after this is written) you should increase this value to the largest
          commonly-used size of favicon and, performance permitting, fetch the remainder from the
          database. The largest available size is always stored in the database, regardless of this
@@ -140,24 +140,24 @@
     <dimen name="tabs_strip_shadow_size">1dp</dimen>
     <dimen name="tabs_layout_horizontal_height">156dp</dimen>
     <dimen name="text_selection_handle_width">47dp</dimen>
     <dimen name="text_selection_handle_height">58dp</dimen>
     <dimen name="text_selection_handle_shadow">11dp</dimen>
     <dimen name="validation_message_height">50dp</dimen>
     <dimen name="validation_message_margin_top">6dp</dimen>
 
-    <dimen name="new_tablet_tab_thumbnail_width">168dp</dimen>
-    <dimen name="new_tablet_tab_thumbnail_height">140dp</dimen>
-    <dimen name="new_tablet_tab_panel_column_width">178dp</dimen>
-    <dimen name="new_tablet_tab_panel_grid_padding">19dp</dimen>
-    <dimen name="new_tablet_tab_panel_grid_vspacing">21dp</dimen>
-    <dimen name="new_tablet_tab_panel_grid_padding_top">24dp</dimen>
+    <dimen name="tablet_tab_thumbnail_width">168dp</dimen>
+    <dimen name="tablet_tab_thumbnail_height">140dp</dimen>
+    <dimen name="tablet_tab_panel_column_width">178dp</dimen>
+    <dimen name="tablet_tab_panel_grid_padding">19dp</dimen>
+    <dimen name="tablet_tab_panel_grid_vspacing">21dp</dimen>
+    <dimen name="tablet_tab_panel_grid_padding_top">24dp</dimen>
 
-    <dimen name="new_tablet_tab_highlight_stroke_width">5dp</dimen>
+    <dimen name="tablet_tab_highlight_stroke_width">5dp</dimen>
 
     <!-- PageActionButtons dimensions -->
     <dimen name="page_action_button_width">32dp</dimen>
 
     <!-- Banner -->
     <dimen name="home_banner_height">72dp</dimen>
     <dimen name="home_banner_close_width">42dp</dimen>
     <dimen name="home_banner_icon_height">48dip</dimen>
--- a/mobile/android/base/resources/values/styles.xml
+++ b/mobile/android/base/resources/values/styles.xml
@@ -203,19 +203,19 @@
     <style name="Widget.TabsGridLayout" parent="Widget.GridView">
         <item name="android:layout_width">match_parent</item>
         <item name="android:layout_height">match_parent</item>
         <item name="android:paddingTop">0dp</item>
         <item name="android:stretchMode">spacingWidth</item>
         <item name="android:scrollbarStyle">outsideOverlay</item>
         <item name="android:gravity">center</item>
         <item name="android:numColumns">auto_fit</item>
-        <item name="android:columnWidth">@dimen/new_tablet_tab_panel_column_width</item>
+        <item name="android:columnWidth">@dimen/tablet_tab_panel_column_width</item>
         <item name="android:horizontalSpacing">2dp</item>
-        <item name="android:verticalSpacing">@dimen/new_tablet_tab_panel_grid_vspacing</item>
+        <item name="android:verticalSpacing">@dimen/tablet_tab_panel_grid_vspacing</item>
         <item name="android:drawSelectorOnTop">true</item>
         <item name="android:clipToPadding">false</item>
     </style>
 
     <style name="Widget.BookmarkItemView" parent="Widget.TwoLinePageRow"/>
 
     <style name="Widget.BookmarksListView" parent="Widget.HomeListView"/>
 
@@ -683,18 +683,18 @@
     </style>
 
     <style name="ToastMessageBase" parent="ToastElementBase">
         <item name="android:textColor">@color/toast_button_text</item>
         <item name="android:layout_width">0dp</item>
         <item name="android:layout_weight">1</item>
         <item name="android:layout_height">wrap_content</item>
         <item name="android:layout_gravity">center_vertical</item>
-        <item name="android:ellipsize">none</item>
-        <item name="android:maxLines">3</item>
+        <item name="android:ellipsize">end</item>
+        <item name="android:maxLines">1</item>
         <item name="android:clickable">false</item>
         <item name="android:focusable">false</item>
     </style>
 
     <style name="ToastButtonBase" parent="ToastElementBase">
         <item name="android:background">@drawable/toast_button_background</item>
         <item name="android:textColor">@color/toast_button_text</item>
         <item name="android:minHeight">0dp</item>
--- a/mobile/android/base/resources/xml/preferences_display.xml
+++ b/mobile/android/base/resources/xml/preferences_display.xml
@@ -28,19 +28,19 @@
     <CheckBoxPreference android:key="media.autoplay.enabled"
                         android:title="@string/pref_media_autoplay_enabled"
                         android:summary="@string/pref_media_autoplay_enabled_summary" />
 
     <CheckBoxPreference android:key="browser.ui.zoom.force-user-scalable"
                         android:title="@string/pref_zoom_force_enabled"
                         android:summary="@string/pref_zoom_force_enabled_summary" />
 
-    <CheckBoxPreference android:key="ui.zoomedview.disabled"
-                        android:title="@string/pref_prevent_magnifying_glass"
-                        android:summary="@string/pref_prevent_magnifying_glass_summary" />
+    <CheckBoxPreference android:key="ui.zoomedview.enabled"
+                        android:title="@string/pref_magnifying_glass_enabled"
+                        android:summary="@string/pref_magnifying_glass_enabled_summary" />
 
     <PreferenceCategory android:title="@string/pref_category_input_options"
                         android:key="@string/pref_category_input_options">
 
         <CheckBoxPreference android:key="android.not_a_preference.voice_input_enabled"
                             android:title="@string/pref_voice_input"
                             android:summary="@string/pref_voice_input_summary"
                             android:defaultValue="true"/>
--- a/mobile/android/base/restrictions/DefaultConfiguration.java
+++ b/mobile/android/base/restrictions/DefaultConfiguration.java
@@ -19,9 +19,12 @@ public class DefaultConfiguration implem
     public boolean canLoadUrl(String url) {
         return true;
     }
 
     @Override
     public boolean isRestricted() {
         return false;
     }
+
+    @Override
+    public void update() {}
 }
--- a/mobile/android/base/restrictions/GuestProfileConfiguration.java
+++ b/mobile/android/base/restrictions/GuestProfileConfiguration.java
@@ -67,9 +67,12 @@ public class GuestProfileConfiguration i
 
         return true;
     }
 
     @Override
     public boolean isRestricted() {
         return true;
     }
+
+    @Override
+    public void update() {}
 }
--- a/mobile/android/base/restrictions/RestrictedProfileConfiguration.java
+++ b/mobile/android/base/restrictions/RestrictedProfileConfiguration.java
@@ -1,57 +1,74 @@
 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
  * 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/. */
 
 package org.mozilla.gecko.restrictions;
 
 import org.mozilla.gecko.AboutPages;
+import org.mozilla.gecko.util.ThreadUtils;
 
 import android.annotation.TargetApi;
 import android.content.Context;
 import android.os.Build;
 import android.os.Bundle;
+import android.os.StrictMode;
 import android.os.UserManager;
 
 import java.util.Arrays;
 import java.util.List;
 
+@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
 public class RestrictedProfileConfiguration implements RestrictionConfiguration {
     static List<Restriction> DEFAULT_RESTRICTIONS = Arrays.asList(
             Restriction.DISALLOW_INSTALL_EXTENSION,
             Restriction.DISALLOW_IMPORT_SETTINGS,
             Restriction.DISALLOW_DEVELOPER_TOOLS,
             Restriction.DISALLOW_CUSTOMIZE_HOME,
             Restriction.DISALLOW_PRIVATE_BROWSING,
             Restriction.DISALLOW_LOCATION_SERVICE,
             Restriction.DISALLOW_DISPLAY_SETTINGS,
             Restriction.DISALLOW_CLEAR_HISTORY,
             Restriction.DISALLOW_MASTER_PASSWORD,
             Restriction.DISALLOW_GUEST_BROWSING
     );
 
     private Context context;
+    private Bundle cachedRestrictions;
+    private boolean isCacheInvalid = true;
 
     public RestrictedProfileConfiguration(Context context) {
         this.context = context.getApplicationContext();
     }
 
     @Override
-    public boolean isAllowed(Restriction restriction) {
-        boolean isAllowed = !getAppRestrictions(context).getBoolean(restriction.name, DEFAULT_RESTRICTIONS.contains(restriction));
-
-        if (isAllowed) {
-            // If this restriction is not enforced by the app setup then check wether this is a restriction that is
-            // enforced by the system.
-            isAllowed = !getUserRestrictions(context).getBoolean(restriction.name, false);
+    public synchronized boolean isAllowed(Restriction restriction) {
+        if (isCacheInvalid || !ThreadUtils.isOnUiThread()) {
+            cachedRestrictions = readRestrictions();
+            isCacheInvalid = false;
         }
 
-        return isAllowed;
+        return !cachedRestrictions.getBoolean(restriction.name, DEFAULT_RESTRICTIONS.contains(restriction));
+    }
+
+    private Bundle readRestrictions() {
+        final UserManager mgr = (UserManager) context.getSystemService(Context.USER_SERVICE);
+
+        StrictMode.ThreadPolicy policy = StrictMode.allowThreadDiskReads();
+
+        try {
+            Bundle restrictions = new Bundle();
+            restrictions.putAll(mgr.getApplicationRestrictions(context.getPackageName()));
+            restrictions.putAll(mgr.getUserRestrictions());
+            return restrictions;
+        } finally {
+            StrictMode.setThreadPolicy(policy);
+        }
     }
 
     @Override
     public boolean canLoadUrl(String url) {
         if (!isAllowed(Restriction.DISALLOW_INSTALL_EXTENSION) && AboutPages.isAboutAddons(url)) {
             return false;
         }
 
@@ -67,20 +84,13 @@ public class RestrictedProfileConfigurat
         return true;
     }
 
     @Override
     public boolean isRestricted() {
         return true;
     }
 
-    @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
-    private Bundle getAppRestrictions(final Context context) {
-        final UserManager mgr = (UserManager) context.getSystemService(Context.USER_SERVICE);
-        return mgr.getApplicationRestrictions(context.getPackageName());
-    }
-
-    @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
-    private Bundle getUserRestrictions(final Context context) {
-        final UserManager mgr = (UserManager) context.getSystemService(Context.USER_SERVICE);
-        return mgr.getUserRestrictions();
+    @Override
+    public synchronized void update() {
+        isCacheInvalid = true;
     }
 }
--- a/mobile/android/base/restrictions/RestrictionConfiguration.java
+++ b/mobile/android/base/restrictions/RestrictionConfiguration.java
@@ -18,9 +18,14 @@ public interface RestrictionConfiguratio
      * Is the user allowed to load the given URL?
      */
     boolean canLoadUrl(String url);
 
     /**
      * Is this user restricted in any way?
      */
     boolean isRestricted();
+
+    /**
+     * Update restrictions if needed.
+     */
+    void update();
 }
--- a/mobile/android/base/strings.xml.in
+++ b/mobile/android/base/strings.xml.in
@@ -374,18 +374,18 @@
   <string name="doorhanger_login_select_toast_copy_error">&doorhanger_login_select_toast_copy_error;</string>
   <string name="doorhanger_login_select_action_text">&doorhanger_login_select_action_text;</string>
   <string name="doorhanger_login_select_title">&doorhanger_login_select_title;</string>
 
   <string name="pref_titlebar_mode">&pref_titlebar_mode;</string>
   <string name="pref_titlebar_mode_title">&pref_titlebar_mode_title;</string>
   <string name="pref_titlebar_mode_url">&pref_titlebar_mode_url;</string>
 
-  <string name="pref_prevent_magnifying_glass">&pref_prevent_magnifying_glass;</string>
-  <string name="pref_prevent_magnifying_glass_summary">&pref_prevent_magnifying_glass_summary;</string>
+  <string name="pref_magnifying_glass_enabled">&pref_magnifying_glass_enabled;</string>
+  <string name="pref_magnifying_glass_enabled_summary">&pref_magnifying_glass_enabled_summary;</string>
 
   <string name="pref_scroll_title_bar2">&pref_scroll_title_bar2;</string>
   <string name="pref_scroll_title_bar_summary">&pref_scroll_title_bar_summary;</string>
 
   <string name="page_removed">&page_removed;</string>
 
   <string name="bookmark_edit_title">&bookmark_edit_title;</string>
   <string name="bookmark_edit_name">&bookmark_edit_name;</string>
--- a/mobile/android/base/sync/CommandProcessor.java
+++ b/mobile/android/base/sync/CommandProcessor.java
@@ -15,22 +15,22 @@ import org.json.simple.JSONObject;
 import org.mozilla.gecko.BrowserLocaleManager;
 import org.mozilla.gecko.R;
 import org.mozilla.gecko.background.common.log.Logger;
 import org.mozilla.gecko.sync.repositories.NullCursorException;
 import org.mozilla.gecko.sync.repositories.android.ClientsDatabaseAccessor;
 import org.mozilla.gecko.sync.repositories.domain.ClientRecord;
 import org.mozilla.gecko.tabqueue.TabQueueDispatcher;
 
-import android.app.Notification;
-import android.app.NotificationManager;
 import android.app.PendingIntent;
 import android.content.Context;
 import android.content.Intent;
 import android.net.Uri;
+import android.support.v4.app.NotificationCompat;
+import android.support.v4.app.NotificationManagerCompat;
 
 /**
  * Process commands received from Sync clients.
  * <p>
  * We need a command processor at two different times:
  * <ol>
  * <li>We execute commands during the "clients" engine stage of a Sync. Each
  * command takes a <code>GlobalSession</code> instance as a parameter.</li>
@@ -259,31 +259,32 @@ public class CommandProcessor {
     // We don't care too much about races, but let's try to avoid
     // unnecessary work.
     if (!didUpdateLocale) {
       BrowserLocaleManager.getInstance().getAndApplyPersistedLocale(context);
       didUpdateLocale = true;
     }
 
     final String ns = Context.NOTIFICATION_SERVICE;
-    final NotificationManager notificationManager = (NotificationManager) context.getSystemService(ns);
 
-    // Create a Notification.
-    final int icon = R.drawable.flat_icon;
     String notificationTitle = context.getString(R.string.sync_new_tab);
     if (title != null) {
       notificationTitle = notificationTitle.concat(": " + title);
     }
 
-    final long when = System.currentTimeMillis();
-    Notification notification = new Notification(icon, notificationTitle, when);
-    notification.flags = Notification.FLAG_AUTO_CANCEL;
-
-    // Set pending intent associated with the notification.
     Intent notificationIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(uri));
     notificationIntent.putExtra(TabQueueDispatcher.SKIP_TAB_QUEUE_FLAG, true);
     PendingIntent contentIntent = PendingIntent.getActivity(context, 0, notificationIntent, 0);
-    notification.setLatestEventInfo(context, notificationTitle, uri, contentIntent);
+
+    NotificationCompat.Builder builder = new NotificationCompat.Builder(context);
+    builder.setSmallIcon(R.drawable.flat_icon);
+    builder.setContentTitle(notificationTitle);
+    builder.setWhen(System.currentTimeMillis());
+    builder.setAutoCancel(true);
+    builder.setContentIntent(contentIntent);
+    builder.setContentText(uri);
+    builder.setContentIntent(contentIntent);
 
     // Send notification.
-    notificationManager.notify(currentId.getAndIncrement(), notification);
+    final NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context);
+    notificationManager.notify(currentId.getAndIncrement(), builder.build());
   }
 }
--- a/mobile/android/base/tabs/TabStripView.java
+++ b/mobile/android/base/tabs/TabStripView.java
@@ -65,24 +65,24 @@ public class TabStripView extends TwoWay
         setWillNotDraw(false);
 
         final Resources resources = getResources();
 
         divider = resources.getDrawable(R.drawable.tab_strip_divider);
         divider.getPadding(dividerPadding);
 
         final int itemMargin =
-                resources.getDimensionPixelSize(R.dimen.new_tablet_tab_strip_item_margin);
+                resources.getDimensionPixelSize(R.dimen.tablet_tab_strip_item_margin);
         setItemMargin(itemMargin);
 
         animatorListener = new TabAnimatorListener();
 
         fadingEdgePaint = new Paint();
         fadingEdgeSize =
-                resources.getDimensionPixelOffset(R.dimen.new_tablet_tab_strip_fading_edge_size);
+                resources.getDimensionPixelOffset(R.dimen.tablet_tab_strip_fading_edge_size);
 
         adapter = new TabStripAdapter(context);
         setAdapter(adapter);
     }
 
     private View getViewForTab(Tab tab) {
         final int position = adapter.getPositionForTab(tab);
         return getChildAt(position - getFirstVisiblePosition());
--- a/mobile/android/base/tabs/TabsGridLayout.java
+++ b/mobile/android/base/tabs/TabsGridLayout.java
@@ -83,20 +83,20 @@ class TabsGridLayout extends GridView
             }
         });
 
         // The clipToPadding setting in the styles.xml doesn't seem to be working (bug 1101784)
         // so lets set it manually in code for the moment as it's needed for the padding animation
         setClipToPadding(false);
 
         final Resources resources = getResources();
-        mColumnWidth = resources.getDimensionPixelSize(R.dimen.new_tablet_tab_panel_column_width);
+        mColumnWidth = resources.getDimensionPixelSize(R.dimen.tablet_tab_panel_column_width);
 
-        final int padding = resources.getDimensionPixelSize(R.dimen.new_tablet_tab_panel_grid_padding);
-        final int paddingTop = resources.getDimensionPixelSize(R.dimen.new_tablet_tab_panel_grid_padding_top);
+        final int padding = resources.getDimensionPixelSize(R.dimen.tablet_tab_panel_grid_padding);
+        final int paddingTop = resources.getDimensionPixelSize(R.dimen.tablet_tab_panel_grid_padding_top);
 
         // Lets set double the top padding on the bottom so that the last row shows up properly!
         // Your demise, GridView, cannot come fast enough.
         final int paddingBottom = paddingTop * 2;
 
         setPadding(padding, paddingTop, padding, paddingBottom);
 
         setOnItemClickListener(new OnItemClickListener() {
@@ -137,17 +137,17 @@ class TabsGridLayout extends GridView
         final boolean oneItemOnLastRow = (lastPosition % numberOfColumns == 0);
         if (firstChildOffScreen && lastChildVisible && oneItemOnLastRow) {
             // We need to set the view's bottom padding to prevent a sudden jump as the
             // last item in the row is being removed. We then need to remove the padding
             // via a sweet animation
 
             final int removedHeight = getChildAt(0).getMeasuredHeight();
             final int verticalSpacing =
-                    getResources().getDimensionPixelOffset(R.dimen.new_tablet_tab_panel_grid_vspacing);
+                    getResources().getDimensionPixelOffset(R.dimen.tablet_tab_panel_grid_vspacing);
 
             ValueAnimator paddingAnimator = ValueAnimator.ofInt(getPaddingBottom() + removedHeight + verticalSpacing, getPaddingBottom());
             paddingAnimator.setDuration(ANIM_TIME_MS * 2);
 
             paddingAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
 
                 @Override
                 public void onAnimationUpdate(ValueAnimator animation) {
--- a/mobile/android/base/toolbar/BrowserToolbarTablet.java
+++ b/mobile/android/base/toolbar/BrowserToolbarTablet.java
@@ -31,17 +31,17 @@ class BrowserToolbarTablet extends Brows
     private ForwardButtonState forwardButtonState;
 
     private boolean backButtonWasEnabledOnStartEditing;
 
     public BrowserToolbarTablet(final Context context, final AttributeSet attrs) {
         super(context, attrs);
 
         forwardButtonTranslationWidth =
-                getResources().getDimensionPixelOffset(R.dimen.new_tablet_nav_button_width);
+                getResources().getDimensionPixelOffset(R.dimen.tablet_nav_button_width);
 
         // The forward button is initially expanded (in the layout file)
         // so translate it for start of the expansion animation; future
         // iterations translate it to this position when hiding and will already be set up.
         ViewHelper.setTranslationX(forwardButton, -forwardButtonTranslationWidth);
 
         // TODO: Move this to *TabletBase when old tablet is removed.
         // We don't want users clicking the forward button in transitions, but we don't want it to
--- a/mobile/android/base/toolbar/NavButton.java
+++ b/mobile/android/base/toolbar/NavButton.java
@@ -66,17 +66,17 @@ abstract class NavButton extends ShapedB
         if (drawable == null) {
             return;
         }
 
         final StateListDrawable stateList = new StateListDrawable();
         stateList.addState(PRIVATE_PRESSED_STATE_SET, getColorDrawable(R.color.placeholder_active_grey));
         stateList.addState(PRESSED_ENABLED_STATE_SET, getColorDrawable(R.color.toolbar_grey_pressed));
         stateList.addState(PRIVATE_FOCUSED_STATE_SET, getColorDrawable(R.color.text_and_tabs_tray_grey));
-        stateList.addState(FOCUSED_STATE_SET, getColorDrawable(R.color.new_tablet_highlight_focused));
+        stateList.addState(FOCUSED_STATE_SET, getColorDrawable(R.color.tablet_highlight_focused));
         stateList.addState(PRIVATE_STATE_SET, getColorDrawable(R.color.tabs_tray_grey_pressed));
         stateList.addState(EMPTY_STATE_SET, drawable);
 
         setBackgroundDrawable(stateList);
     }
 
     @Override
     public void onLightweightThemeReset() {
--- a/mobile/android/base/updater/UpdateService.java
+++ b/mobile/android/base/updater/UpdateService.java
@@ -25,16 +25,17 @@ import android.content.Context;
 import android.content.Intent;
 import android.content.SharedPreferences;
 import android.net.ConnectivityManager;
 import android.net.NetworkInfo;
 import android.net.Uri;
 import android.net.wifi.WifiManager;
 import android.net.wifi.WifiManager.WifiLock;
 import android.os.Environment;
+import android.support.v4.app.NotificationManagerCompat;
 import android.support.v4.net.ConnectivityManagerCompat;
 import android.support.v4.app.NotificationCompat;
 import android.support.v4.app.NotificationCompat.Builder;
 import android.util.Log;
 
 import java.io.BufferedInputStream;
 import java.io.BufferedOutputStream;
 import java.io.File;
@@ -72,17 +73,17 @@ public class UpdateService extends Inten
     private static final String KEY_LAST_HASH_VALUE = "UpdateService.lastHashValue";
     private static final String KEY_LAST_FILE_NAME = "UpdateService.lastFileName";
     private static final String KEY_LAST_ATTEMPT_DATE = "UpdateService.lastAttemptDate";
     private static final String KEY_AUTODOWNLOAD_POLICY = "UpdateService.autoDownloadPolicy";
     private static final String KEY_UPDATE_URL = "UpdateService.updateUrl";
 
     private SharedPreferences mPrefs;
 
-    private NotificationManager mNotificationManager;
+    private NotificationManagerCompat mNotificationManager;
     private ConnectivityManager mConnectivityManager;
     private Builder mBuilder;
 
     private volatile WifiLock mWifiLock;
 
     private boolean mDownloading;
     private boolean mCancelDownload;
     private boolean mApplyImmediately;
@@ -137,17 +138,17 @@ public class UpdateService extends Inten
 
     @Override
     public void onCreate () {
         mCrashHandler = CrashHandler.createDefaultCrashHandler(getApplicationContext());
 
         super.onCreate();
 
         mPrefs = getSharedPreferences(PREFS_NAME, 0);
-        mNotificationManager = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
+        mNotificationManager = NotificationManagerCompat.from(this);
         mConnectivityManager = (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
         mWifiLock = ((WifiManager)getSystemService(Context.WIFI_SERVICE))
                     .createWifiLock(WifiManager.WIFI_MODE_FULL_HIGH_PERF, PREFS_NAME);
         mCancelDownload = false;
     }
 
     @Override
     public void onDestroy() {
@@ -295,29 +296,29 @@ public class UpdateService extends Inten
             policy == AutoDownloadPolicy.ENABLED ||
             (policy == AutoDownloadPolicy.WIFI && !ConnectivityManagerCompat.isActiveNetworkMetered(mConnectivityManager));
 
         if (!shouldStartDownload) {
             Log.i(LOGTAG, "not initiating automatic update download due to policy " + policy.toString());
             sendCheckUpdateResult(CheckUpdateResult.AVAILABLE);
 
             // We aren't autodownloading here, so prompt to start the update
-            Notification notification = new Notification(R.drawable.ic_status_logo, null, System.currentTimeMillis());
-
             Intent notificationIntent = new Intent(UpdateServiceHelper.ACTION_DOWNLOAD_UPDATE);
             notificationIntent.setClass(this, UpdateService.class);
-
             PendingIntent contentIntent = PendingIntent.getService(this, 0, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
-            notification.flags = Notification.FLAG_AUTO_CANCEL;
 
-            notification.setLatestEventInfo(this, getResources().getString(R.string.updater_start_title),
-                                            getResources().getString(R.string.updater_start_select),
-                                            contentIntent);
+            NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
+            builder.setSmallIcon(R.drawable.ic_status_logo);
+            builder.setWhen(System.currentTimeMillis());
+            builder.setAutoCancel(true);
+            builder.setContentTitle(getString(R.string.updater_start_title));
+            builder.setContentText(getString(R.string.updater_start_select));
+            builder.setContentIntent(contentIntent);
 
-            mNotificationManager.notify(NOTIFICATION_ID, notification);
+            mNotificationManager.notify(NOTIFICATION_ID, builder.build());
 
             return;
         }
 
         File pkg = downloadUpdatePackage(info, hasFlag(flags, UpdateServiceHelper.FLAG_OVERWRITE_EXISTING));
         if (pkg == null) {
             sendCheckUpdateResult(CheckUpdateResult.NOT_AVAILABLE);
             return;
@@ -327,30 +328,32 @@ public class UpdateService extends Inten
 
         saveUpdateInfo(info, pkg);
         sendCheckUpdateResult(CheckUpdateResult.DOWNLOADED);
 
         if (mApplyImmediately) {
             applyUpdate(pkg);
         } else {
             // Prompt to apply the update
-            Notification notification = new Notification(R.drawable.ic_status_logo, null, System.currentTimeMillis());
 
             Intent notificationIntent = new Intent(UpdateServiceHelper.ACTION_APPLY_UPDATE);
             notificationIntent.setClass(this, UpdateService.class);
             notificationIntent.putExtra(UpdateServiceHelper.EXTRA_PACKAGE_PATH_NAME, pkg.getAbsolutePath());
+            PendingIntent contentIntent = PendingIntent.getService(this, 0, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
 
-            PendingIntent contentIntent = PendingIntent.getService(this, 0, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
-            notification.flags = Notification.FLAG_AUTO_CANCEL;
 
-            notification.setLatestEventInfo(this, getResources().getString(R.string.updater_apply_title),
-                                            getResources().getString(R.string.updater_apply_select),
-                                            contentIntent);
+            NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
+            builder.setSmallIcon(R.drawable.ic_status_logo);
+            builder.setWhen(System.currentTimeMillis());
+            builder.setAutoCancel(true);
+            builder.setContentTitle(getString(R.string.updater_apply_title));
+            builder.setContentText(getString(R.string.updater_apply_select));
+            builder.setContentIntent(contentIntent);
 
-            mNotificationManager.notify(NOTIFICATION_ID, notification);
+            mNotificationManager.notify(NOTIFICATION_ID, builder.build());
         }
     }
 
     private URLConnection openConnectionWithProxy(URI uri) throws java.net.MalformedURLException, java.io.IOException {
         Log.i(LOGTAG, "opening connection with URI: " + uri);
 
         ProxySelector ps = ProxySelector.getDefault();
         Proxy proxy = Proxy.NO_PROXY;
@@ -470,28 +473,28 @@ public class UpdateService extends Inten
                 .setContentIntent(contentIntent)
                 .setDeleteIntent(deleteIntent);
 
         mBuilder.setProgress(100, 0, true);
         mNotificationManager.notify(NOTIFICATION_ID, mBuilder.build());
     }
 
     private void showDownloadFailure() {
-        Notification notification = new Notification(R.drawable.ic_status_logo, null, System.currentTimeMillis());
-
         Intent notificationIntent = new Intent(UpdateServiceHelper.ACTION_CHECK_FOR_UPDATE);
         notificationIntent.setClass(this, UpdateService.class);
-
         PendingIntent contentIntent = PendingIntent.getService(this, 0, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
 
-        notification.setLatestEventInfo(this, getResources().getString(R.string.updater_downloading_title_failed),
-                                        getResources().getString(R.string.updater_downloading_retry),
-                                        contentIntent);
+        NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
+        builder.setSmallIcon(R.drawable.ic_status_logo);
+        builder.setWhen(System.currentTimeMillis());
+        builder.setContentTitle(getString(R.string.updater_downloading_title_failed));
+        builder.setContentText(getString(R.string.updater_downloading_retry));
+        builder.setContentIntent(contentIntent);
 
-        mNotificationManager.notify(NOTIFICATION_ID, notification);
+        mNotificationManager.notify(NOTIFICATION_ID, builder.build());
     }
 
     private boolean deleteUpdatePackage(String path) {
         if (path == null) {
             return false;
         }
 
         File pkg = new File(path);
--- a/mobile/android/gradle/app/build.gradle
+++ b/mobile/android/gradle/app/build.gradle
@@ -45,16 +45,47 @@ android {
 
 dependencies {
     compile project(':base')
     compile project(':omnijar')
     // Including the Robotium JAR directly can cause issues with dexing.
     androidTestCompile 'com.jayway.android.robotium:robotium-solo:4.3.1'
 }
 
+task syncOmnijarFromDistDir(type: Sync) {
+    into("${project.buildDir}/generated/omnijar")
+    from("${topobjdir}/dist/fennec/assets") {
+        include 'omni.ja'
+    }
+}
+
+task checkLibsExistInDistDir<< {
+    if (syncLibsFromDistDir.source.empty) {
+        throw new GradleException("Required JNI libraries not found in ${topobjdir}/dist/fennec/lib.  Have you built and packaged?")
+    }
+}
+
+task syncLibsFromDistDir(type: Sync, dependsOn: checkLibsExistInDistDir) {
+    into("${project.buildDir}/generated/jniLibs")
+    from("${topobjdir}/dist/fennec/lib")
+}
+
+task checkAssetsExistInDistDir<< {
+    if (syncAssetsFromDistDir.source.empty) {
+        throw new GradleException("Required assets not found in ${topobjdir}/dist/fennec/assets.  Have you built and packaged?")
+    }
+}
+
+task syncAssetsFromDistDir(type: Sync, dependsOn: checkAssetsExistInDistDir) {
+    into("${project.buildDir}/generated/assets")
+    from("${topobjdir}/dist/fennec/assets") {
+        exclude 'omni.ja'
+    }
+}
+
 /**
  * We want to expose the JSM files and chrome content to IDEs; the omnijar
  * project does this.  In addition, the :omnijar:buildOmnijar task builds a new
  * omni.ja (directly into the object directory).
  *
  * The task dependency is: :generateDebugAssets -> :omnijar:buildOmnijar.
  *
  * One might expect that we could do this all in the omnijar project, but there
@@ -66,18 +97,25 @@ dependencies {
 android.applicationVariants.all { variant ->
     // We only insert omni.ja and the .so libraries into debug builds.
     def name = variant.buildType.name
     if (!name.contains(com.android.builder.core.BuilderConstants.DEBUG)) {
         return
     }
 
     def buildOmnijarTask = project(':omnijar').tasks.getByName('buildOmnijar')
+    syncOmnijarFromDistDir.dependsOn buildOmnijarTask
     def generateAssetsTask = tasks.findByName("generate${name.capitalize()}Assets")
-    generateAssetsTask.dependsOn buildOmnijarTask
+    generateAssetsTask.dependsOn syncOmnijarFromDistDir
+    generateAssetsTask.dependsOn syncLibsFromDistDir
+    generateAssetsTask.dependsOn syncAssetsFromDistDir
+
+    android.sourceSets.debug.assets.srcDir syncOmnijarFromDistDir.destinationDir
+    android.sourceSets.debug.assets.srcDir syncAssetsFromDistDir.destinationDir
+    android.sourceSets.debug.jniLibs.srcDir syncLibsFromDistDir.destinationDir
 }
 
 apply plugin: 'spoon'
 
 spoon {
     // For now, let's be verbose.
     debug = true
     // It's not helpful to pass when we don't have a device connected.
--- a/mobile/android/gradle/base/build.gradle
+++ b/mobile/android/gradle/base/build.gradle
@@ -72,20 +72,16 @@ dependencies {
     }
 
     compile project(':branding')
     compile project(':preprocessed_code')
     compile project(':preprocessed_resources')
     compile project(':thirdparty')
 }
 
-android.libraryVariants.all { variant ->
-    variant.checkManifest.dependsOn rootProject.generateCodeAndResources
-}
-
 apply plugin: 'idea'
 
 idea {
     module {
         excludeDirs += file('org/mozilla/gecko/resources')
         excludeDirs += file('org/mozilla/gecko/tests')
     }
 }
--- a/mobile/android/gradle/build.gradle
+++ b/mobile/android/gradle/build.gradle
@@ -54,8 +54,22 @@ task generateCodeAndResources(type:Exec)
     standardOutput = new ByteArrayOutputStream()
     errorOutput = standardOutput
     doLast {
         if (execResult.exitValue != 0) {
             throw new GradleException("Process '${commandLine}' finished with non-zero exit value ${execResult.exitValue}:\n\n${standardOutput.toString()}")
         }
     }
 }
+
+afterEvaluate {
+    subprojects {
+        if (!hasProperty('android')) {
+            return
+        }
+        android.applicationVariants.all {
+            checkManifest.dependsOn rootProject.generateCodeAndResources
+        }
+        android.libraryVariants.all {
+            checkManifest.dependsOn rootProject.generateCodeAndResources
+        }
+    }
+}
--- a/mobile/android/gradle/preprocessed_code/build.gradle
+++ b/mobile/android/gradle/preprocessed_code/build.gradle
@@ -16,29 +16,38 @@ android {
 
     lintOptions {
         abortOnError false
     }
 
     sourceSets {
         main {
             java {
-                srcDir "${topsrcdir}/mobile/android/base/adjust"
+                srcDir "${project.buildDir}/generated/source/java"
+
+                srcDir 'src/adjust/java'
                 if (mozconfig.substs.MOZ_INSTALL_TRACKING) {
-                    exclude 'StubAdjustHelper.java'
+                    exclude 'org/mozilla/gecko/adjust/StubAdjustHelper.java'
                 } else {
-                    exclude 'AdjustHelper.java'
+                    exclude 'org/mozilla/gecko/adjust/AdjustHelper.java'
                 }
             }
         }
     }
 }
 
-android.libraryVariants.all { variant ->
-    variant.checkManifest.dependsOn rootProject.generateCodeAndResources
+task syncGeneratedSources(type: Sync) {
+    into("${project.buildDir}/generated/source/java")
+    from("${topobjdir}/mobile/android/base/generated/preprocessed")
 }
 
+android.libraryVariants.all { variant ->
+    // variant does not expose its generate sources task.
+    def name = variant.buildType.name
+    def generateSourcesTask = tasks.findByName("generate${name.capitalize()}Sources")
+    generateSourcesTask.dependsOn syncGeneratedSources
+}
 
 dependencies {
     if (mozconfig.substs.MOZ_INSTALL_TRACKING) {
         compile project(':thirdparty_adjust_sdk')
     }
 }
--- a/mobile/android/gradle/preprocessed_resources/build.gradle
+++ b/mobile/android/gradle/preprocessed_resources/build.gradle
@@ -12,17 +12,33 @@ android {
     compileOptions {
         sourceCompatibility JavaVersion.VERSION_1_7
         targetCompatibility JavaVersion.VERSION_1_7
     }
 
     lintOptions {
         abortOnError false
     }
+
+    sourceSets {
+        main {
+            res {
+                srcDir "${project.buildDir}/generated/source/preprocessed_resources"
+            }
+        }
+    }
+}
+
+task syncPreprocessedResources(type: Sync) {
+    into("${project.buildDir}/generated/source/preprocessed_resources")
+    from("${topobjdir}/mobile/android/base/res")
 }
 
 android.libraryVariants.all { variant ->
-    variant.checkManifest.dependsOn rootProject.generateCodeAndResources
+    // variant does not expose its generate debug res values task.
+    def name = variant.buildType.name
+    def generateResValuesTask = tasks.findByName("generate${name.capitalize()}ResValues")
+    generateResValuesTask.dependsOn syncPreprocessedResources
 }
 
 dependencies {
     compile project(':branding')
 }
--- a/mobile/android/mach_commands.py
+++ b/mobile/android/mach_commands.py
@@ -90,21 +90,20 @@ class MachCommands(MachCommandBase):
             deps=os.path.join(self.topobjdir, 'mobile/android/gradle/.deps/local.properties.pp'))
 
         srcdir('branding/build.gradle', 'mobile/android/gradle/branding/build.gradle')
         srcdir('branding/src/main/AndroidManifest.xml', 'mobile/android/gradle/branding/AndroidManifest.xml')
         srcdir('branding/src/main/res', os.path.join(self.substs['MOZ_BRANDING_DIRECTORY'], 'res'))
 
         srcdir('preprocessed_code/build.gradle', 'mobile/android/gradle/preprocessed_code/build.gradle')
         srcdir('preprocessed_code/src/main/AndroidManifest.xml', 'mobile/android/gradle/preprocessed_code/AndroidManifest.xml')
-        objdir('preprocessed_code/src/main/java', 'mobile/android/base/generated/preprocessed')
+        srcdir('preprocessed_code/src/adjust/java/org/mozilla/gecko/adjust', 'mobile/android/base/adjust')
 
         srcdir('preprocessed_resources/build.gradle', 'mobile/android/gradle/preprocessed_resources/build.gradle')
         srcdir('preprocessed_resources/src/main/AndroidManifest.xml', 'mobile/android/gradle/preprocessed_resources/AndroidManifest.xml')
-        objdir('preprocessed_resources/src/main/res', 'mobile/android/base/res')
 
         srcdir('thirdparty/build.gradle', 'mobile/android/gradle/thirdparty/build.gradle')
         srcdir('thirdparty/src/main/AndroidManifest.xml', 'mobile/android/gradle/thirdparty/AndroidManifest.xml')
         srcdir('thirdparty/src/main/java', 'mobile/android/thirdparty')
 
         srcdir('thirdparty_adjust_sdk/build.gradle', 'mobile/android/gradle/thirdparty_adjust_sdk/build.gradle')
         srcdir('thirdparty_adjust_sdk/src/main/AndroidManifest.xml', 'mobile/android/gradle/thirdparty_adjust_sdk/AndroidManifest.xml')
 
@@ -115,18 +114,16 @@ class MachCommands(MachCommandBase):
         srcdir('omnijar/src/main/java/modules', 'mobile/android/modules')
         srcdir('omnijar/src/main/java/themes', 'mobile/android/themes')
 
         srcdir('app/build.gradle', 'mobile/android/gradle/app/build.gradle')
         objdir('app/src/main/AndroidManifest.xml', 'mobile/android/base/AndroidManifest.xml')
         objdir('app/src/androidTest/AndroidManifest.xml', 'build/mobile/robocop/AndroidManifest.xml')
         srcdir('app/src/androidTest/res', 'build/mobile/robocop/res')
         srcdir('app/src/androidTest/assets', 'mobile/android/tests/browser/robocop/assets')
-        objdir('app/src/debug/assets', 'dist/fennec/assets')
-        objdir('app/src/debug/jniLibs', 'dist/fennec/lib')
         # Test code.
         srcdir('app/src/robocop_harness/org/mozilla/gecko', 'build/mobile/robocop')
         srcdir('app/src/robocop/org/mozilla/gecko/tests', 'mobile/android/tests/browser/robocop')
         srcdir('app/src/background/org/mozilla/gecko', 'mobile/android/tests/background/junit3/src')
         srcdir('app/src/browser', 'mobile/android/tests/browser/junit3/src')
         srcdir('app/src/javaaddons', 'mobile/android/tests/javaaddons/src')
         # Test libraries.
         srcdir('app/libs', 'build/mobile/robocop')
deleted file mode 100644
index 3cecbfabab0a75d3786b0956b4c0c92bbf5c82c4..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index cd67e9db1dfc9a48759869324144bd83eb57fb50..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 21df3027b2a5476824790974247bf489e93cba61..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 40f3fd81ed2e8caa490c58de393c817a6e82f952..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 7b1e124e956ba75c1595f620056d5d218380d8df..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index d0071a06be3ab2cd2124f90a8e0bdac9b02dcb3f..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
--- a/mobile/android/themes/core/jar.mn
+++ b/mobile/android/themes/core/jar.mn
@@ -46,40 +46,34 @@ chrome.jar:
 * skin/aboutLogins.css                      (aboutLogins.css)
   skin/images/spinning_throbber.svg         (images/spinning_throbber.svg)
 
   skin/images/search.png                    (images/search.png)
   skin/images/lock.png                      (images/lock.png)
   skin/images/textfield.png                 (images/textfield.png)
 
   skin/images/5stars.png                    (images/5stars.png)
-  skin/images/addons-32.png                 (images/addons-32.png)
   skin/images/amo-logo.png                  (images/amo-logo.png)
-  skin/images/arrowleft-16.png              (images/arrowleft-16.png)
-  skin/images/arrowright-16.png             (images/arrowright-16.png)
   skin/images/arrowdown-16.png              (images/arrowdown-16.png)
   skin/images/arrowup-16.png                (images/arrowup-16.png)
   skin/images/blocked-warning.png           (images/blocked-warning.png)
   skin/images/checkbox_checked.png          (images/checkbox_checked.png)
   skin/images/checkbox_checked_disabled.png (images/checkbox_checked_disabled.png)
   skin/images/checkbox_checked_pressed.png  (images/checkbox_checked_pressed.png)
   skin/images/checkbox_unchecked.png          (images/checkbox_unchecked.png)
   skin/images/checkbox_unchecked_disabled.png (images/checkbox_unchecked_disabled.png)
   skin/images/checkbox_unchecked_pressed.png  (images/checkbox_unchecked_pressed.png)
   skin/images/chevron.png                   (images/chevron.png)
-  skin/images/default-app-icon.png          (images/default-app-icon.png)
   skin/images/dropmarker.svg                (images/dropmarker.svg)
   skin/images/dropmarker-right.svg          (images/dropmarker-right.svg)
   skin/images/errorpage-warning.png         (images/errorpage-warning.png)
   skin/images/exitfullscreen-hdpi.png       (images/exitfullscreen-hdpi.png)
   skin/images/fullscreen-hdpi.png           (images/fullscreen-hdpi.png)
   skin/images/grey-caution.svg              (images/grey-caution.svg)
   skin/images/certerror-warning.png         (images/certerror-warning.png)
-  skin/images/errorpage-larry-white.png     (images/errorpage-larry-white.png)
-  skin/images/errorpage-larry-black.png     (images/errorpage-larry-black.png)
   skin/images/throbber.png                  (images/throbber.png)
   skin/images/search-clear-30.png           (images/search-clear-30.png)
   skin/images/play-hdpi.png                 (images/play-hdpi.png)
   skin/images/pause-hdpi.png                (images/pause-hdpi.png)
   skin/images/cast-ready-hdpi.png           (images/cast-ready-hdpi.png)
   skin/images/cast-active-hdpi.png          (images/cast-active-hdpi.png)
   skin/images/mute-hdpi.png                 (images/mute-hdpi.png)
   skin/images/unmute-hdpi.png               (images/unmute-hdpi.png)
--- a/toolkit/devtools/client/main.js
+++ b/toolkit/devtools/client/main.js
@@ -2415,16 +2415,46 @@ ObjectClient.prototype = {
   }, {
     before: function(aPacket) {
       if (this._grip.class !== "Promise") {
         throw new Error("getAllocationStack is only valid for promise grips.");
       }
       return aPacket;
     }
   }),
+
+  /**
+   * Request the stack to the promise's fulfillment point.
+   */
+  getPromiseFulfillmentStack: DebuggerClient.requester({
+    type: "fulfillmentStack"
+  }, {
+    before: function(packet) {
+      if (this._grip.class !== "Promise") {
+        throw new Error("getPromiseFulfillmentStack is only valid for " +
+          "promise grips.");
+      }
+      return packet;
+    }
+  }),
+
+  /**
+   * Request the stack to the promise's rejection point.
+   */
+  getPromiseRejectionStack: DebuggerClient.requester({
+    type: "rejectionStack"
+  }, {
+    before: function(packet) {
+      if (this._grip.class !== "Promise") {
+        throw new Error("getPromiseRejectionStack is only valid for " +
+          "promise grips.");
+      }
+      return packet;
+    }
+  })
 };
 
 /**
  * A PropertyIteratorClient provides a way to access to property names and
  * values of an object efficiently, slice by slice.
  * Note that the properties can be sorted in the backend,
  * this is controled while creating the PropertyIteratorClient
  * from ObjectClient.enumProperties.
--- a/toolkit/devtools/server/actors/inspector.js
+++ b/toolkit/devtools/server/actors/inspector.js
@@ -114,16 +114,18 @@ const PSEUDO_SELECTORS = [
 ];
 
 let HELPER_SHEET = ".__fx-devtools-hide-shortcut__ { visibility: hidden !important } ";
 HELPER_SHEET += ":-moz-devtools-highlighted { outline: 2px dashed #F06!important; outline-offset: -2px!important } ";
 
 loader.lazyRequireGetter(this, "DevToolsUtils",
                          "devtools/toolkit/DevToolsUtils");
 
+loader.lazyRequireGetter(this, "AsyncUtils", "devtools/toolkit/async-utils");
+
 loader.lazyGetter(this, "DOMParser", function() {
   return Cc["@mozilla.org/xmlextras/domparser;1"].createInstance(Ci.nsIDOMParser);
 });
 
 loader.lazyGetter(this, "eventListenerService", function() {
   return Cc["@mozilla.org/eventlistenerservice;1"]
            .getService(Ci.nsIEventListenerService);
 });
@@ -619,26 +621,22 @@ var NodeActor = exports.NodeActor = prot
    * The image data is transmitted as a base64 encoded png data-uri.
    * The method rejects if the node isn't an image or if the image is missing
    *
    * Accepts a maxDim request parameter to resize images that are larger. This
    * is important as the resizing occurs server-side so that image-data being
    * transfered in the longstring back to the client will be that much smaller
    */
   getImageData: method(function(maxDim) {
-    // imageToImageData may fail if the node isn't an image
-    try {
-      let imageData = imageToImageData(this.rawNode, maxDim);
-      return promise.resolve({
+    return imageToImageData(this.rawNode, maxDim).then(imageData => {
+      return {
         data: LongStringActor(this.conn, imageData.data),
         size: imageData.size
-      });
-    } catch(e) {
-      return promise.reject(new Error("Image not available"));
-    }
+      };
+    });
   }, {
     request: {maxDim: Arg(0, "nullable:number")},
     response: RetVal("imageData")
   }),
 
   /**
    * Get all event listeners that are listening on this node.
    */
@@ -3640,49 +3638,26 @@ var InspectorActor = exports.InspectorAc
    * The image data is transmitted as a base64 encoded png data-uri.
    * The method rejects if the node isn't an image or if the image is missing
    *
    * Accepts a maxDim request parameter to resize images that are larger. This
    * is important as the resizing occurs server-side so that image-data being
    * transfered in the longstring back to the client will be that much smaller
    */
   getImageDataFromURL: method(function(url, maxDim) {
-    let deferred = promise.defer();
     let img = new this.window.Image();
-
-    // On load, get the image data and send the response
-    img.onload = () => {
-      // imageToImageData throws an error if the image is missing
-      try {
-        let imageData = imageToImageData(img, maxDim);
-        deferred.resolve({
-          data: LongStringActor(this.conn, imageData.data),
-          size: imageData.size
-        });
-      } catch (e) {
-        deferred.reject(new Error("Image " + url+ " not available"));
-      }
-    }
-
-    // If the URL doesn't point to a resource, reject
-    img.onerror = () => {
-      deferred.reject(new Error("Image " + url+ " not available"));
-    }
-
-    // If the request hangs for too long, kill it to avoid queuing up other requests
-    // to the same actor, except if we're running tests
-    if (!DevToolsUtils.testing) {
-      this.window.setTimeout(() => {
-        deferred.reject(new Error("Image " + url + " could not be retrieved in time"));
-      }, IMAGE_FETCHING_TIMEOUT);
-    }
-
     img.src = url;
 
-    return deferred.promise;
+    // imageToImageData waits for the image to load.
+    return imageToImageData(img, maxDim).then(imageData => {
+      return {
+        data: LongStringActor(this.conn, imageData.data),
+        size: imageData.size
+      };
+    });
   }, {
     request: {url: Arg(0), maxDim: Arg(1, "nullable:number")},
     response: RetVal("imageData")
   }),
 
   /**
    * Resolve a URL to its absolute form, in the scope of a given content window.
    * @param {String} url.
@@ -3917,30 +3892,94 @@ function allAnonymousContentTreeWalkerFi
   if (aNode.nodeType == Ci.nsIDOMNode.TEXT_NODE &&
       !/[^\s]/.exec(aNode.nodeValue)) {
     return Ci.nsIDOMNodeFilter.FILTER_SKIP;
   }
   return Ci.nsIDOMNodeFilter.FILTER_ACCEPT
 }
 
 /**
- * Given an image DOMNode, return the image data-uri.
- * @param {DOMNode} node The image node
- * @param {Number} maxDim Optionally pass a maximum size you want the longest
+ * Returns a promise that is settled once the given HTMLImageElement has
+ * finished loading.
+ *
+ * @param {HTMLImageElement} image - The image element.
+ * @param {Number} timeout - Maximum amount of time the image is allowed to load
+ * before the waiting is aborted. Ignored if DevToolsUtils.testing is set.
+ *
+ * @return {Promise} that is fulfilled once the image has loaded. If the image
+ * fails to load or the load takes too long, the promise is rejected.
+ */
+function ensureImageLoaded(image, timeout) {
+  let { HTMLImageElement } = image.ownerDocument.defaultView;
+  if (!(image instanceof HTMLImageElement)) {
+    return promise.reject("image must be an HTMLImageELement");
+  }
+
+  if (image.complete) {
+    // The image has already finished loading.
+    return promise.resolve();
+  }
+
+  // This image is still loading.
+  let onLoad = AsyncUtils.listenOnce(image, "load");
+
+  // Reject if loading fails.
+  let onError = AsyncUtils.listenOnce(image, "error").then(() => {
+    return promise.reject("Image '" + image.src + "' failed to load.");
+  });
+
+  // Don't timeout when testing. This is never settled.
+  let onAbort = new promise(() => {});
+
+  if (!DevToolsUtils.testing) {
+    // Tests are not running. Reject the promise after given timeout.
+    onAbort = DevToolsUtils.waitForTime(timeout).then(() => {
+      return promise.reject("Image '" + image.src + "' took too long to load.");
+    });
+  }
+
+  // See which happens first.
+  return promise.race([onLoad, onError, onAbort]);
+}
+
+/**
+ * Given an <img> or <canvas> element, return the image data-uri. If @param node
+ * is an <img> element, the method waits a while for the image to load before
+ * the data is generated. If the image does not finish loading in a reasonable
+ * time (IMAGE_FETCHING_TIMEOUT milliseconds) the process aborts.
+ *
+ * @param {HTMLImageElement|HTMLCanvasElement} node - The <img> or <canvas>
+ * element, or Image() object. Other types cause the method to reject.
+ * @param {Number} maxDim - Optionally pass a maximum size you want the longest
  * side of the image to be resized to before getting the image data.
- * @return {Object} An object containing the data-uri and size-related information
- * {data: "...", size: {naturalWidth: 400, naturalHeight: 300, resized: true}}
- * @throws an error if the node isn't an image or if the image is missing
+
+ * @return {Promise} A promise that is fulfilled with an object containing the
+ * data-uri and size-related information:
+ * { data: "...",
+ *   size: {
+ *     naturalWidth: 400,
+ *     naturalHeight: 300,
+ *     resized: true }
+ *  }.
+ *
+ * If something goes wrong, the promise is rejected.
  */
-function imageToImageData(node, maxDim) {
-  let isImg = node.tagName.toLowerCase() === "img";
-  let isCanvas = node.tagName.toLowerCase() === "canvas";
+let imageToImageData = Task.async(function* (node, maxDim) {
+  let { HTMLCanvasElement, HTMLImageElement } = node.ownerDocument.defaultView;
+
+  let isImg = node instanceof HTMLImageElement;
+  let isCanvas = node instanceof HTMLCanvasElement;
 
   if (!isImg && !isCanvas) {
-    return null;
+    throw "node is not a <canvas> or <img> element.";
+  }
+
+  if (isImg) {
+    // Ensure that the image is ready.
+    yield ensureImageLoaded(node, IMAGE_FETCHING_TIMEOUT);
   }
 
   // Get the image resize ratio if a maxDim was provided
   let resizeRatio = 1;
   let imgWidth = node.naturalWidth || node.width;
   let imgHeight = node.naturalHeight || node.height;
   let imgMax = Math.max(imgWidth, imgHeight);
   if (maxDim && imgMax > maxDim) {
@@ -3968,13 +4007,13 @@ function imageToImageData(node, maxDim) 
   return {
     data: imageData,
     size: {
       naturalWidth: imgWidth,
       naturalHeight: imgHeight,
       resized: resizeRatio !== 1
     }
   }
-}
+});
 
 loader.lazyGetter(this, "DOMUtils", function () {
   return Cc["@mozilla.org/inspector/dom-utils;1"].getService(Ci.inIDOMUtils);
 });
--- a/toolkit/devtools/server/actors/object.js
+++ b/toolkit/devtools/server/actors/object.js
@@ -572,18 +572,78 @@ ObjectActor.prototype = {
     }
 
     return Promise.all(allocationStacks).then(stacks => {
       return { allocationStack: stacks };
     });
   },
 
   /**
-   * Helper function for onAllocationStack which fetches the source location
-   * for a SavedFrame stack.
+   * Handle a protocol request to get the fulfillment stack of a promise.
+   */
+  onFulfillmentStack: function() {
+    if (this.obj.class != "Promise") {
+      return { error: "objectNotPromise",
+               message: "'fulfillmentStack' request is only valid for " +
+                        "object grips with a 'Promise' class." };
+    }
+
+    let rawPromise = this.obj.unsafeDereference();
+    let stack = PromiseDebugging.getFullfillmentStack(rawPromise);
+    let fulfillmentStacks = [];
+
+    while (stack) {
+      if (stack.source) {
+        let source = this._getSourceOriginalLocation(stack);
+
+        if (source) {
+          fulfillmentStacks.push(source);
+        }
+      }
+      stack = stack.parent;
+    }
+
+    return Promise.all(fulfillmentStacks).then(stacks => {
+      return { fulfillmentStack: stacks };
+    });
+  },
+
+  /**
+   * Handle a protocol request to get the rejection stack of a promise.
+   */
+  onRejectionStack: function() {
+    if (this.obj.class != "Promise") {
+      return { error: "objectNotPromise",
+               message: "'rejectionStack' request is only valid for " +
+                        "object grips with a 'Promise' class." };
+    }
+
+    let rawPromise = this.obj.unsafeDereference();
+    let stack = PromiseDebugging.getRejectionStack(rawPromise);
+    let rejectionStacks = [];
+
+    while (stack) {
+      if (stack.source) {
+        let source = this._getSourceOriginalLocation(stack);
+
+        if (source) {
+          rejectionStacks.push(source);
+        }
+      }
+      stack = stack.parent;
+    }
+
+    return Promise.all(rejectionStacks).then(stacks => {
+      return { rejectionStack: stacks };
+    });
+  },
+
+  /**
+   * Helper function for fetching the source location of a SavedFrame stack.
+   *
    * @param SavedFrame stack
    *        The promise allocation stack frame
    * @return object
    *         Returns an object containing the source location of the SavedFrame
    *         stack.
    */
   _getSourceOriginalLocation: function(stack) {
     let source;
@@ -620,17 +680,19 @@ ObjectActor.prototype.requestTypes = {
   "prototype": ObjectActor.prototype.onPrototype,
   "property": ObjectActor.prototype.onProperty,
   "displayString": ObjectActor.prototype.onDisplayString,
   "ownPropertyNames": ObjectActor.prototype.onOwnPropertyNames,
   "decompile": ObjectActor.prototype.onDecompile,
   "release": ObjectActor.prototype.onRelease,
   "scope": ObjectActor.prototype.onScope,
   "dependentPromises": ObjectActor.prototype.onDependentPromises,
-  "allocationStack": ObjectActor.prototype.onAllocationStack
+  "allocationStack": ObjectActor.prototype.onAllocationStack,
+  "fulfillmentStack": ObjectActor.prototype.onFulfillmentStack,
+  "rejectionStack": ObjectActor.prototype.onRejectionStack
 };
 
 /**
  * Creates an actor to iterate over an object's property names and values.
  *
  * @param objectActor ObjectActor
  *        The object actor.
  * @param options Object
--- a/toolkit/devtools/server/tests/mochitest/chrome.ini
+++ b/toolkit/devtools/server/tests/mochitest/chrome.ini
@@ -3,16 +3,17 @@ tags = devtools
 skip-if = buildapp == 'b2g' || os == 'android'
 support-files =
   Debugger.Source.prototype.element.js
   Debugger.Source.prototype.element-2.js
   Debugger.Source.prototype.element.html
   director-helpers.js
   hello-actor.js
   inspector_getImageData.html
+  inspector-delay-image-response.sjs
   inspector-helpers.js
   inspector-styles-data.css
   inspector-styles-data.html
   inspector-traversal-data.html
   large-image.jpg
   memory-helpers.js
   nonchrome_unsafeDereference.html
   small-image.gif
@@ -52,16 +53,20 @@ skip-if = buildapp == 'mulet'
 [test_getProcess.html]
 skip-if = buildapp == 'mulet'
 [test_inspector-anonymous.html]
 [test_inspector-changeattrs.html]
 [test_inspector-changevalue.html]
 [test_inspector-dead-nodes.html]
 [test_inspector_getImageData.html]
 skip-if = buildapp == 'mulet'
+[test_inspector_getImageDataFromURL.html]
+skip-if = buildapp == 'mulet'
+[test_inspector_getImageData-wait-for-load.html]
+skip-if = buildapp == 'mulet'
 [test_inspector_getNodeFromActor.html]
 [test_inspector-hide.html]
 [test_inspector-insert.html]
 [test_inspector-mutations-attr.html]
 [test_inspector-mutations-childlist.html]
 [test_inspector-mutations-frameload.html]
 [test_inspector-mutations-value.html]
 [test_inspector-pseudoclass-lock.html]
new file mode 100644
--- /dev/null
+++ b/toolkit/devtools/server/tests/mochitest/inspector-delay-image-response.sjs
@@ -0,0 +1,42 @@
+/**
+ * Adapted from https://dxr.mozilla.org/mozilla-central/rev/
+ * 4e883591bb5dff021c108d3e30198a99547eed1e/layout/reftests/backgrounds/
+ * delay-image-response.sjs
+ */
+"use strict";
+
+// A 1x1 PNG image.
+// Source: https://commons.wikimedia.org/wiki/File:1x1.png (Public Domain)
+const IMAGE = atob("iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAAA1BMVEUAA" +
+  "ACnej3aAAAAAXRSTlMAQObYZgAAAApJREFUCNdjYAAAAAIAAeIhvDMAAAAASUVORK5CYII=");
+
+// To avoid GC.
+let timer = null;
+
+function handleRequest(request, response) {
+  let query = {};
+  request.queryString.split("&").forEach(function(val) {
+    let [name, value] = val.split("=");
+    query[name] = unescape(value);
+  });
+
+  response.setStatusLine(request.httpVersion, 200, "OK");
+  response.setHeader("Content-Type", "image/png", false);
+
+  // If there is no delay, we write the image and leave.
+  if (!("delay" in query)) {
+    response.write(IMAGE);
+    return;
+  }
+
+  // If there is a delay, we create a timer which, when it fires, will write
+  // image and leave.
+  response.processAsync();
+  const nsITimer = Components.interfaces.nsITimer;
+
+  timer = Components.classes["@mozilla.org/timer;1"].createInstance(nsITimer);
+  timer.initWithCallback(function() {
+    response.write(IMAGE);
+    response.finish();
+  }, query.delay, nsITimer.TYPE_ONE_SHOT);
+}
--- a/toolkit/devtools/server/tests/mochitest/inspector_getImageData.html
+++ b/toolkit/devtools/server/tests/mochitest/inspector_getImageData.html
@@ -1,11 +1,12 @@
 <html>
 <head>
 <body>
+  <img class="custom">
   <img class="big-horizontal" src="large-image.jpg" style="width:500px;" />
   <canvas class="big-vertical" style="width:500px;"></canvas>
   <img class="small" src="small-image.gif"></img>
   <script>
     window.onload = () => {
       var canvas = document.querySelector("canvas"), ctx = canvas.getContext("2d");
       canvas.width = 1000;
       canvas.height = 2000;
new file mode 100644
--- /dev/null
+++ b/toolkit/devtools/server/tests/mochitest/test_inspector_getImageData-wait-for-load.html
@@ -0,0 +1,136 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+Tests for InspectorActor.getImageData() in following cases:
+ * Image takes too long to load (the method rejects after a timeout).
+ * Image is loading when the method is called and the load finishes before
+   timeout.
+ * Image fails to load.
+
+https://bugzilla.mozilla.org/show_bug.cgi?id=1192536
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 1192536</title>
+
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
+  <script type="application/javascript;version=1.8" src="inspector-helpers.js"></script>
+  <script type="application/javascript;version=1.8">
+
+const DevToolsUtils = require("devtools/toolkit/DevToolsUtils");
+const wasTesting = DevToolsUtils.testing;
+SimpleTest.registerCleanupFunction(() => DevToolsUtils.testing = wasTesting);
+
+const PATH = "http://mochi.test:8888/chrome/toolkit/devtools/server/tests/mochitest/";
+const BASE_IMAGE = PATH + "inspector-delay-image-response.sjs";
+const DELAYED_IMAGE = BASE_IMAGE + "?delay=300";
+const TIMEOUT_IMAGE = BASE_IMAGE + "?delay=50000";
+const NONEXISTENT_IMAGE = PATH + "this-does-not-exist.png";
+
+window.onload = function() {
+  SimpleTest.waitForExplicitFinish();
+  runNextTest();
+}
+
+var gImg = null;
+var gNodeFront = null;
+var gWalker = null;
+
+addTest(function setup() {
+  let url = document.getElementById("inspectorContent").href;
+  attachURL(url, function(err, client, tab, doc) {
+    let {InspectorFront} = require("devtools/server/actors/inspector");
+    let inspector = InspectorFront(client, tab);
+
+    promiseDone(inspector.getWalker().then(walker => {
+      gWalker = walker;
+      return walker.querySelector(gWalker.rootNode, "img.custom").then(img => {
+        gNodeFront = img;
+        gImg = doc.querySelector("img.custom");
+
+        ok(gNodeFront, "Got the image NodeFront.");
+        ok(gImg, "Got the image Node.");
+      });
+    }).then(runNextTest));
+  });
+});
+
+addTest(function testTimeout() {
+  info("Testing that the method aborts if the image takes too long to load.");
+
+  // imageToImageData() only times out when DTU.testing is not set.
+  DevToolsUtils.testing = false;
+
+  gImg.src = TIMEOUT_IMAGE;
+
+  info("Calling getImageData().");
+  ensureRejects(gNodeFront.getImageData(), "Timeout image").then(runNextTest);
+});
+
+addTest(function testNonExistentImage() {
+  info("Testing that non-existent image causes a rejection.");
+
+  // This test shouldn't hit the timeout.
+  DevToolsUtils.testing = true;
+
+  gImg.src = NONEXISTENT_IMAGE;
+
+  info("Calling getImageData().");
+  ensureRejects(gNodeFront.getImageData(), "Non-existent image").then(runNextTest);
+});
+
+addTest(function testDelayedImage() {
+  info("Testing that the method waits for an image to load.");
+
+  // This test shouldn't hit the timeout.
+  DevToolsUtils.testing = true;
+
+  gImg.src = DELAYED_IMAGE;
+
+  info("Calling getImageData().");
+  checkImageData(gNodeFront.getImageData()).then(runNextTest);
+});
+
+addTest(function cleanup() {
+  delete gImg;
+  delete gNodeFront
+  delete gWalker;
+  runNextTest();
+});
+
+/**
+ * Asserts that the given promise rejects.
+ */
+function ensureRejects(promise, desc) {
+  return promise.then(() => {
+    ok(false, desc + ": promise resolved unexpectedly.");
+  }, () => {
+    ok(true, desc + ": promise rejected as expected.");
+  });
+}
+
+/**
+ * Waits for the call to getImageData() the resolve and checks that the image
+ * size is reported correctly.
+ */
+function checkImageData(promise, { width, height } = { width: 1, height: 1 }) {
+  return promise.then(({ size }) => {
+    is(size.naturalWidth, width, "The width is correct.");
+    is(size.naturalHeight, height, "The height is correct.");
+  });
+}
+
+  </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1192536">Mozilla Bug 1192536</a>
+<a id="inspectorContent" target="_blank" href="inspector_getImageData.html">Test Document</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
--- a/toolkit/devtools/server/tests/mochitest/test_inspector_getImageData.html
+++ b/toolkit/devtools/server/tests/mochitest/test_inspector_getImageData.html
@@ -90,20 +90,37 @@ addTest(function testSmallImage() {
       imageData.data.string().then(str => {
         ok(str, "We have an image data string!");
         runNextTest();
       });
     });
   });
 });
 
+addTest(function testNonImgOrCanvasElements() {
+  gWalker.querySelector(gWalker.rootNode, "body").then(body => {
+    ensureRejects(body.getImageData(), "Invalid element").then(runNextTest);
+  });
+});
+
 addTest(function cleanup() {
   delete gWalker;
   runNextTest();
 });
+
+/**
+ * Asserts that the given promise rejects.
+ */
+function ensureRejects(promise, desc) {
+  return promise.then(() => {
+    ok(false, desc + ": promise resolved unexpectedly.");
+  }, () => {
+    ok(true, desc + ": promise rejected as expected.");
+  });
+}
   </script>
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=932937">Mozilla Bug 932937</a>
 <a id="inspectorContent" target="_blank" href="inspector_getImageData.html">Test Document</a>
 <p id="display"></p>
 <div id="content" style="display: none">
 
new file mode 100644
--- /dev/null
+++ b/toolkit/devtools/server/tests/mochitest/test_inspector_getImageDataFromURL.html
@@ -0,0 +1,114 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+Tests for InspectorActor.getImageDataFromURL() in following cases:
+ * Normal case, image loads after a small delay.
+ * Image takes too long to load (the method rejects after a timeout).
+ * Image fails to load.
+
+https://bugzilla.mozilla.org/show_bug.cgi?id=1192536
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 1192536</title>
+
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
+  <script type="application/javascript;version=1.8" src="inspector-helpers.js"></script>
+  <script type="application/javascript;version=1.8">
+
+const DevToolsUtils = require("devtools/toolkit/DevToolsUtils");
+const wasTesting = DevToolsUtils.testing;
+SimpleTest.registerCleanupFunction(() => DevToolsUtils.testing = wasTesting);
+
+const PATH = "http://mochi.test:8888/chrome/toolkit/devtools/server/tests/mochitest/";
+const BASE_IMAGE = PATH + "inspector-delay-image-response.sjs";
+const DELAYED_IMAGE = BASE_IMAGE + "?delay=300";
+const TIMEOUT_IMAGE = BASE_IMAGE + "?delay=50000";
+const NONEXISTENT_IMAGE = PATH + "this-does-not-exist.png";
+
+window.onload = function() {
+  SimpleTest.waitForExplicitFinish();
+  runNextTest();
+}
+
+var gInspector = null;
+
+addTest(function setup() {
+  let url = document.getElementById("inspectorContent").href;
+  attachURL(url, function(err, client, tab, doc) {
+    let {InspectorFront} = require("devtools/server/actors/inspector");
+    gInspector = InspectorFront(client, tab);
+    runNextTest();
+  });
+});
+
+addTest(function testTimeout() {
+  info("Testing that the method aborts if the image takes too long to load.");
+
+  // imageToImageData() only times out when DTU.testing is not set.
+  DevToolsUtils.testing = false;
+
+  ensureRejects(gInspector.getImageDataFromURL(TIMEOUT_IMAGE),
+    "Image that loads for too long").then(runNextTest);
+});
+
+addTest(function testNonExistentImage() {
+  info("Testing that non-existent image causes a rejection.");
+
+  // This test shouldn't hit the timeout.
+  DevToolsUtils.testing = true;
+
+  ensureRejects(gInspector.getImageDataFromURL(NONEXISTENT_IMAGE),
+    "Non-existent image").then(runNextTest);
+});
+
+addTest(function testNormalImage() {
+  info("Testing that the method waits for an image to load.");
+
+  // This test shouldn't hit the timeout.
+  DevToolsUtils.testing = true;
+
+  checkImageData(gInspector.getImageDataFromURL(DELAYED_IMAGE)).then(runNextTest);
+});
+
+addTest(function cleanup() {
+  delete gInspector;
+  runNextTest();
+});
+
+/**
+ * Asserts that the given promise rejects.
+ */
+function ensureRejects(promise, desc) {
+  return promise.then(() => {
+    ok(false, desc + ": promise resolved unexpectedly.");
+  }, () => {
+    ok(true, desc + ": promise rejected as expected.");
+  });
+}
+
+/**
+ * Waits for the call to getImageData() the resolve and checks that the image
+ * size is reported correctly.
+ */
+function checkImageData(promise, { width, height } = { width: 1, height: 1 }) {
+  return promise.then(({ size }) => {
+    is(size.naturalWidth, width, "The width is correct.");
+    is(size.naturalHeight, height, "The height is correct.");
+  });
+}
+
+  </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1192536">Mozilla Bug 1192536</a>
+<a id="inspectorContent" target="_blank" href="inspector_getImageData.html">Test Document</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
--- a/toolkit/modules/AppConstants.jsm
+++ b/toolkit/modules/AppConstants.jsm
@@ -138,16 +138,23 @@ this.AppConstants = Object.freeze({
 
   MOZ_CRASHREPORTER:
 #ifdef MOZ_CRASHREPORTER
   true,
 #else
   false,
 #endif
 
+  MOZ_VERIFY_MAR_SIGNATURE:
+#ifdef MOZ_VERIFY_MAR_SIGNATURE
+  true,
+#else
+  false,
+#endif
+
   MOZ_MAINTENANCE_SERVICE:
 #ifdef MOZ_MAINTENANCE_SERVICE
   true,
 #else
   false,
 #endif
 
   E10S_TESTING_ONLY:
--- a/toolkit/mozapps/extensions/internal/XPIProvider.jsm
+++ b/toolkit/mozapps/extensions/internal/XPIProvider.jsm
@@ -7658,19 +7658,27 @@ DirectoryInstallLocation.prototype = {
    * safe move operations. Calling this method will delete the existing trash
    * directory and its contents.
    *
    * @return an nsIFile
    */
   getTrashDir: function DirInstallLocation_getTrashDir() {
     let trashDir = this._directory.clone();
     trashDir.append(DIR_TRASH);
-    if (trashDir.exists())
-      recursiveRemove(trashDir);
-    trashDir.create(Ci.nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY);
+    let trashDirExists = trashDir.exists();
+    try {
+      if (trashDirExists)
+        recursiveRemove(trashDir);
+      trashDirExists = false;
+    } catch (e) {
+      logger.warn("Failed to remove trash directory", e);
+    }
+    if (!trashDirExists)
+      trashDir.create(Ci.nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY);
+
     return trashDir;
   },
 
   /**
    * Installs an add-on into the install location.
    *
    * @param  aId
    *         The ID of the add-on to install
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_bug1180901.js
@@ -0,0 +1,35 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+function run_test() {
+  createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+  startupManager();
+  run_next_test();
+}
+
+add_task(function* () {
+  let profileDir = OS.Constants.Path.profileDir;
+  let trashDir = OS.Path.join(profileDir, "extensions", "trash");
+  let testFile = OS.Path.join(trashDir, "test.txt");
+
+  yield OS.File.makeDir(trashDir, {
+    from: profileDir,
+    ignoreExisting: true
+  });
+
+  let trashDirExists = yield OS.File.exists(trashDir);
+  ok(trashDirExists, "trash directory should have been created");
+
+  let file = yield OS.File.open(testFile, {create: true}, {winShare: 0});
+  let fileExists = yield OS.File.exists(testFile);
+  ok(fileExists, "test.txt should have been created in " + trashDir);
+
+  yield promiseInstallAllFiles([do_get_addon("test_install1")]);
+  yield promiseRestartManager();
+  fileExists = yield OS.File.exists(testFile);
+  ok(fileExists, "test.txt still exists");
+  yield file.close();
+  yield OS.File.removeDir(OS.Path.join(OS.Constants.Path.profileDir, "extensions"));
+  yield promiseShutdownManager();
+});
--- a/toolkit/mozapps/extensions/test/xpcshell/xpcshell-shared.ini
+++ b/toolkit/mozapps/extensions/test/xpcshell/xpcshell-shared.ini
@@ -283,8 +283,10 @@ run-sequentially = Uses global XCurProcD
 # Bug 676992: test consistently hangs on Android
 skip-if = os == "android"
 run-sequentially = Uses global XCurProcD dir.
 [test_overrideblocklist.js]
 run-sequentially = Uses global XCurProcD dir.
 [test_sourceURI.js]
 [test_webextension.js]
 [test_bootstrap_globals.js]
+[test_bug1180901.js]
+skip-if = os != "win"
--- a/toolkit/mozapps/extensions/test/xpcshell/xpcshell.ini
+++ b/toolkit/mozapps/extensions/test/xpcshell/xpcshell.ini
@@ -23,9 +23,10 @@ skip-if = appname != "firefox"
 [test_provider_shutdown.js]
 [test_provider_unsafe_access_shutdown.js]
 [test_provider_unsafe_access_startup.js]
 [test_shutdown.js]
 [test_XPIcancel.js]
 [test_XPIStates.js]
 
 
+
 [include:xpcshell-shared.ini]
--- a/toolkit/mozapps/update/nsUpdateService.js
+++ b/toolkit/mozapps/update/nsUpdateService.js
@@ -3867,16 +3867,23 @@ Downloader.prototype = {
     if (destination.fileSize != this._patch.size) {
       LOG("Downloader:_verifyDownload downloaded size != expected size.");
       AUSTLMY.pingDownloadCode(this.isCompleteUpdate,
                                AUSTLMY.DWNLD_ERR_VERIFY_PATCH_SIZE_NOT_EQUAL);
       return false;
     }
 
     LOG("Downloader:_verifyDownload downloaded size == expected size.");
+
+    // The hash check is not necessary when mar signatures are used to verify
+    // the downloaded mar file.
+    if (AppConstants.MOZ_VERIFY_MAR_SIGNATURE) {
+      return true;
+    }
+
     let fileStream = Cc["@mozilla.org/network/file-input-stream;1"].
                      createInstance(Ci.nsIFileInputStream);
     fileStream.init(destination, FileUtils.MODE_RDONLY, FileUtils.PERMS_FILE, 0);
 
     let digest;
     try {
       let hash = Cc["@mozilla.org/security/hash;1"].
                  createInstance(Ci.nsICryptoHash);
--- a/toolkit/mozapps/update/tests/chrome/test_0061_check_verifyFailPartial_noComplete.xul
+++ b/toolkit/mozapps/update/tests/chrome/test_0061_check_verifyFailPartial_noComplete.xul
@@ -3,17 +3,17 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 -->
 
 <?xml-stylesheet href="chrome://global/skin" type="text/css"?>
 <?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?>
 
-<window title="Update Wizard pages: update check, basic, download, and errors (partial patch with an invalid hash)"
+<window title="Update Wizard pages: update check, basic, download, and errors (partial patch with an invalid size)"
         xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
         onload="runTestDefault();">
 <script type="application/javascript"
         src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
 <script type="application/javascript"
         src="utils.js"/>
 
 <script type="application/javascript">
@@ -30,17 +30,17 @@ const TESTS = [ {
   pageid: PAGEID_ERRORS,
   buttonClick: "finish"
 } ];
 
 function runTest() {
   debugDump("entering");
 
   let url = URL_HTTP_UPDATE_XML + "?showDetails=1&partialPatchOnly=1" +
-            "&invalidPartialHash=1" + getVersionParams();
+            "&invalidPartialSize=1" + getVersionParams();
   setUpdateURLOverride(url);
 
   gUP.checkForUpdates();
 }
 
 ]]>
 </script>
 
--- a/toolkit/mozapps/update/tests/chrome/test_0062_check_verifyFailComplete_noPartial.xul
+++ b/toolkit/mozapps/update/tests/chrome/test_0062_check_verifyFailComplete_noPartial.xul
@@ -3,17 +3,17 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 -->
 
 <?xml-stylesheet href="chrome://global/skin" type="text/css"?>
 <?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?>
 
-<window title="Update Wizard pages: update check, basic, download, and errors (complete patch with an invalid hash)"
+<window title="Update Wizard pages: update check, basic, download, and errors (complete patch with an invalid size)"
         xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
         onload="runTestDefault();">
 <script type="application/javascript"
         src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
 <script type="application/javascript"
         src="utils.js"/>
 
 <script type="application/javascript">
@@ -30,17 +30,17 @@ const TESTS = [ {
   pageid: PAGEID_ERRORS,
   buttonClick: "finish"
 } ];
 
 function runTest() {
   debugDump("entering");
 
   let url = URL_HTTP_UPDATE_XML + "?showDetails=1&completePatchOnly=1" +
-            "&invalidCompleteHash=1" + getVersionParams();
+            "&invalidCompleteSize=1" + getVersionParams();
   setUpdateURLOverride(url);
 
   gUP.checkForUpdates();
 }
 
 ]]>
 </script>
 
--- a/toolkit/mozapps/update/tests/chrome/test_0063_check_verifyFailPartialComplete.xul
+++ b/toolkit/mozapps/update/tests/chrome/test_0063_check_verifyFailPartialComplete.xul
@@ -3,17 +3,17 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 -->
 
 <?xml-stylesheet href="chrome://global/skin" type="text/css"?>
 <?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?>
 
-<window title="Update Wizard pages: update check, basic, download, and errors (partial and complete patches with invalid hashes)"
+<window title="Update Wizard pages: update check, basic, download, and errors (partial and complete patches with invalid sizes)"
         xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
         onload="runTestDefault();">
 <script type="application/javascript"
         src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
 <script type="application/javascript"
         src="utils.js"/>
 
 <script type="application/javascript">
@@ -29,18 +29,18 @@ const TESTS = [ {
 }, {
   pageid: PAGEID_ERRORS,
   buttonClick: "finish"
 } ];
 
 function runTest() {
   debugDump("entering");
 
-  let url = URL_HTTP_UPDATE_XML + "?showDetails=1&invalidPartialHash=1" +
-            "&invalidCompleteHash=1" + getVersionParams();
+  let url = URL_HTTP_UPDATE_XML + "?showDetails=1&invalidPartialSize=1" +
+            "&invalidCompleteSize=1" + getVersionParams();
   setUpdateURLOverride(url);
 
   gUP.checkForUpdates();
 }
 
 ]]>
 </script>
 
--- a/toolkit/mozapps/update/tests/chrome/test_0064_check_verifyFailPartial_successComplete.xul
+++ b/toolkit/mozapps/update/tests/chrome/test_0064_check_verifyFailPartial_successComplete.xul
@@ -3,17 +3,17 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 -->
 
 <?xml-stylesheet href="chrome://global/skin" type="text/css"?>
 <?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?>
 
-<window title="Update Wizard pages: update check, basic, download, and finished (partial patch with an invalid hash and successful complete patch)"
+<window title="Update Wizard pages: update check, basic, download, and finished (partial patch with an invalid size and successful complete patch)"
         xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
         onload="runTestDefault();">
 <script type="application/javascript"
         src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
 <script type="application/javascript"
         src="utils.js"/>
 
 <script type="application/javascript">
@@ -29,17 +29,17 @@ const TESTS = [ {
 }, {
   pageid: PAGEID_FINISHED,
   buttonClick: "extra1"
 } ];
 
 function runTest() {
   debugDump("entering");
 
-  let url = URL_HTTP_UPDATE_XML + "?showDetails=1&invalidPartialHash=1" +
+  let url = URL_HTTP_UPDATE_XML + "?showDetails=1&invalidPartialSize=1" +
             getVersionParams();
   setUpdateURLOverride(url);
 
   gUP.checkForUpdates();
 }
 
 ]]>
 </script>
--- a/toolkit/mozapps/update/tests/chrome/test_0071_notify_verifyFailPartial_noComplete.xul
+++ b/toolkit/mozapps/update/tests/chrome/test_0071_notify_verifyFailPartial_noComplete.xul
@@ -3,17 +3,17 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 -->
 
 <?xml-stylesheet href="chrome://global/skin" type="text/css"?>
 <?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?>
 
-<window title="Update Wizard pages: errors (partial patch with an invalid hash)"
+<window title="Update Wizard pages: errors (partial patch with an invalid size)"
         xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
         onload="runTestDefault();">
 <script type="application/javascript"
         src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
 <script type="application/javascript"
         src="utils.js"/>
 
 <script type="application/javascript">
@@ -22,17 +22,17 @@
 const TESTS = [ {
   pageid: PAGEID_ERRORS,
   buttonClick: "finish"
 } ];
 
 function runTest() {
   debugDump("entering");
 
-  let patches = getLocalPatchString("partial", null, null, "1234", null, null,
+  let patches = getLocalPatchString("partial", null, null, null, "1234", null,
                                     STATE_DOWNLOADING);
   let updates = getLocalUpdateString(patches, null, null, null,
                                      Services.appinfo.version,
                                      Services.appinfo.platformVersion);
 
   writeUpdatesToXMLFile(getLocalUpdatesXMLString(updates), true);
 
   writeUpdatesToXMLFile(getLocalUpdatesXMLString(""), false);
--- a/toolkit/mozapps/update/tests/chrome/test_0072_notify_verifyFailComplete_noPartial.xul
+++ b/toolkit/mozapps/update/tests/chrome/test_0072_notify_verifyFailComplete_noPartial.xul
@@ -3,17 +3,17 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 -->
 
 <?xml-stylesheet href="chrome://global/skin" type="text/css"?>
 <?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?>
 
-<window title="Update Wizard pages: errors (complete patch with an invalid hash)"
+<window title="Update Wizard pages: errors (complete patch with an invalid size)"
         xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
         onload="runTestDefault();">
 <script type="application/javascript"
         src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
 <script type="application/javascript"
         src="utils.js"/>
 
 <script type="application/javascript">
@@ -22,17 +22,17 @@
 const TESTS = [ {
   pageid: PAGEID_ERRORS,
   buttonClick: "finish"
 } ];
 
 function runTest() {
   debugDump("entering");
 
-  let patches = getLocalPatchString("complete", null, null, "1234", null, null,
+  let patches = getLocalPatchString("complete", null, null, null, "1234", null,
                                     STATE_DOWNLOADING);
   let updates = getLocalUpdateString(patches, null, null, null,
                                      Services.appinfo.version,
                                      Services.appinfo.platformVersion);
 
   writeUpdatesToXMLFile(getLocalUpdatesXMLString(updates), true);
 
   writeUpdatesToXMLFile(getLocalUpdatesXMLString(""), false);
--- a/toolkit/mozapps/update/tests/chrome/test_0073_notify_verifyFailPartialComplete.xul
+++ b/toolkit/mozapps/update/tests/chrome/test_0073_notify_verifyFailPartialComplete.xul
@@ -3,17 +3,17 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 -->
 
 <?xml-stylesheet href="chrome://global/skin" type="text/css"?>
 <?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?>
 
-<window title="Update Wizard pages: errors (partial and complete patches with invalid hashes)"
+<window title="Update Wizard pages: errors (partial and complete patches with invalid sizes)"
         xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
         onload="runTestDefault();">
 <script type="application/javascript"
         src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
 <script type="application/javascript"
         src="utils.js"/>
 
 <script type="application/javascript">
@@ -22,19 +22,19 @@
 const TESTS = [ {
   pageid: PAGEID_ERRORS,
   buttonClick: "finish"
 } ];
 
 function runTest() {
   debugDump("entering");
 
-  let patches = getLocalPatchString("partial", null, null, "1234", null, null,
+  let patches = getLocalPatchString("partial", null, null, null, "1234", null,
                                     STATE_DOWNLOADING) +
-                getLocalPatchString("complete", null, null, "1234", null,
+                getLocalPatchString("complete", null, null, null, "1234",
                                     "false");
   let updates = getLocalUpdateString(patches, null, null, null,
                                      Services.appinfo.version,
                                      Services.appinfo.platformVersion, null,
                                      null, null, null, null, null, null,
                                      "false");
 
   writeUpdatesToXMLFile(getLocalUpdatesXMLString(updates), true);
--- a/toolkit/mozapps/update/tests/chrome/test_0074_notify_verifyFailPartial_successComplete.xul
+++ b/toolkit/mozapps/update/tests/chrome/test_0074_notify_verifyFailPartial_successComplete.xul
@@ -3,17 +3,17 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 -->
 
 <?xml-stylesheet href="chrome://global/skin" type="text/css"?>
 <?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?>
 
-<window title="Update Wizard pages: finishedBackground (partial patch with an invalid hash and successful complete patch)"
+<window title="Update Wizard pages: finishedBackground (partial patch with an invalid size and successful complete patch)"
         xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
         onload="runTestDefault();">
 <script type="application/javascript"
         src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
 <script type="application/javascript"
         src="utils.js"/>
 
 <script type="application/javascript">
@@ -22,17 +22,17 @@
 const TESTS = [ {
   pageid: PAGEID_FINISHED_BKGRD,
   buttonClick: "extra1"
 } ];
 
 function runTest() {
   debugDump("entering");
 
-  let patches = getLocalPatchString("partial", null, null, "1234", null, null,
+  let patches = getLocalPatchString("partial", null, null, null, "1234", null,
                                     STATE_DOWNLOADING) +
                 getLocalPatchString("complete", null, null, null, null,
                                     "false");
   let updates = getLocalUpdateString(patches, null, null, null,
                                      Services.appinfo.version,
                                      Services.appinfo.platformVersion, null,
                                      null, null, null, null, null, null,
                                      "false");
--- a/toolkit/mozapps/update/tests/chrome/test_0084_error_patchApplyFailure_verify_failed.xul
+++ b/toolkit/mozapps/update/tests/chrome/test_0084_error_patchApplyFailure_verify_failed.xul
@@ -37,17 +37,17 @@ function runTest() {
   removeContinueFile();
 
   // Specify the url to update.sjs with a slowDownloadMar param so the ui can
   // load before the download completes.
   let slowDownloadURL = URL_HTTP_UPDATE_XML + "?slowDownloadMar=1";
   let patches = getLocalPatchString("partial", null, null, null, null, null,
                                     STATE_PENDING) +
                 getLocalPatchString("complete", slowDownloadURL, "MD5",
-                                    "1234cd43a1c77e30191c53a329a3f99d", null,
+                                    null, "1234",
                                     "false");
   let updates = getLocalUpdateString(patches, null, null, null,
                                      Services.appinfo.version,
                                      Services.appinfo.platformVersion, null,
                                      null, null, null, null, null, null,
                                      "false");
 
   writeUpdatesToXMLFile(getLocalUpdatesXMLString(updates), true);
--- a/toolkit/mozapps/update/tests/chrome/update.sjs
+++ b/toolkit/mozapps/update/tests/chrome/update.sjs
@@ -107,28 +107,28 @@ function handleRequest(aRequest, aRespon
   if (params.unsupported) {
     aResponse.write(getRemoteUpdatesXMLString("  <update type=\"major\" " +
                                               "unsupported=\"true\" " +
                                               "detailsURL=\"" + URL_HOST +
                                               "\"></update>\n"));
     return;
   }
 
-  let hash;
+  let size;
   let patches = "";
   if (!params.partialPatchOnly) {
-    hash = SHA512_HASH_SIMPLE_MAR + (params.invalidCompleteHash ? "e" : "");
+    size = SIZE_SIMPLE_MAR + (params.invalidCompleteSize ? "1" : "");
     patches += getRemotePatchString("complete", SERVICE_URL, "SHA512",
-                                    hash, SIZE_SIMPLE_MAR);
+                                    SHA512_HASH_SIMPLE_MAR, size);
   }
 
   if (!params.completePatchOnly) {
-    hash = SHA512_HASH_SIMPLE_MAR + (params.invalidPartialHash ? "e" : "");
+    size = SIZE_SIMPLE_MAR + (params.invalidPartialSize ? "1" : "");
     patches += getRemotePatchString("partial", SERVICE_URL, "SHA512",
-                                    hash, SIZE_SIMPLE_MAR);
+                                    SHA512_HASH_SIMPLE_MAR, size);
   }
 
   let type = params.type ? params.type : "major";
   let name = params.name ? params.name : "App Update Test";
   let appVersion = params.appVersion ? params.appVersion : "99.9";
   let displayVersion = params.displayVersion ? params.displayVersion
                                              : "version " + appVersion;
   let platformVersion = params.platformVersion ? params.platformVersion : "99.8";
--- a/toolkit/mozapps/update/tests/unit_aus_update/downloadAndHashCheckMar.js
+++ b/toolkit/mozapps/update/tests/unit_aus_update/downloadAndHashCheckMar.js
@@ -18,17 +18,23 @@ function run_test() {
 
   Services.prefs.setBoolPref(PREF_APP_UPDATE_STAGING_ENABLED, false);
   // The HTTP server is only used for the mar file downloads since it is slow
   start_httpserver();
   setUpdateURLOverride(gURLData + "update.xml");
   // The mock XMLHttpRequest is MUCH faster
   overrideXHR(callHandleEvent);
   standardInit();
-  do_execute_soon(run_test_pt1);
+  // Only perform the non hash check tests when mar signing is enabled since the
+  // update service doesn't perform hash checks when mar signing is enabled.
+  if (IS_MAR_CHECKS_ENABLED) {
+    do_execute_soon(run_test_pt11);
+  } else {
+    do_execute_soon(run_test_pt1);
+  }
 }
 
 // The HttpServer must be stopped before calling do_test_finished
 function finish_test() {
   stop_httpserver(doTestFinish);
 }
 
 // Callback function used by the custom XMLHttpRequest implementation to