Merge m-c to mozilla-inbound on a CLOSED TREE
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Wed, 21 Oct 2015 15:34:18 +0200
changeset 303930 ab8d2508c6ea2e1a0869f62c668eb0dee6709e42
parent 303929 cce5dbdf4c7a424b64ea2b1ee936d153c946fba7 (current diff)
parent 303840 473aefe5bd85842eeb142e0cde8e2cd21edbf40b (diff)
child 303931 cbeed0536844eb5b6ae86240dd5e3adc84a93d37
push id1001
push userraliiev@mozilla.com
push dateMon, 18 Jan 2016 19:06:03 +0000
treeherdermozilla-release@8b89261f3ac4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone44.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 mozilla-inbound on a CLOSED TREE
devtools/client/inspector/test/browser_inspector_menu-05-other.js
--- a/addon-sdk/source/CONTRIBUTING.md
+++ b/addon-sdk/source/CONTRIBUTING.md
@@ -32,17 +32,16 @@ There is a list of [good first bugs here
 
 ## Reviewers
 
 All changes need a review by someone on the Jetpack review crew:
 
 - [@mossop]
 - [@gozala]
 - [@ZER0]
-- [@erikvold]
 - [@jsantell]
 - [@zombie]
 
 For review of Mozilla platform usage and best practices, ask [@autonome],
 [@0c0w3], or [@mossop] to find the domain expert.
 
 For API and developer ergonomics review, ask [@gozala].
 
@@ -56,11 +55,10 @@ For API and developer ergonomics review,
 [coding style guide]:https://github.com/mozilla/addon-sdk/wiki/Coding-style-guide
 [Add-on SDK repo]:https://github.com/mozilla/addon-sdk
 [GitHub]:https://github.com/
 [good first bugs]:https://bugzilla.mozilla.org/buglist.cgi?list_id=7345714&columnlist=bug_severity%2Cpriority%2Cassigned_to%2Cbug_status%2Ctarget_milestone%2Cresolution%2Cshort_desc%2Cchangeddate&query_based_on=jetpack-good-1st-bugs&status_whiteboard_type=allwordssubstr&query_format=advanced&status_whiteboard=[good%20first%20bug]&bug_status=UNCONFIRMED&bug_status=NEW&bug_status=ASSIGNED&bug_status=REOPENED&bug_status=VERIFIED&product=Add-on%20SDK&known_name=jetpack-good-1st-bugs
 
 [@mossop]:https://github.com/mossop/
 [@gozala]:https://github.com/Gozala/
 [@ZER0]:https://github.com/ZER0/
-[@erikvold]:https://github.com/erikvold/
 [@jsantell]:https://github.com/jsantell
 [@zombie]:https://github.com/zombie
--- a/addon-sdk/source/bin/node-scripts/words.txt
+++ b/addon-sdk/source/bin/node-scripts/words.txt
@@ -1,12 +1,11 @@
 addon-sdk
 github
 stackoverflow
 bugzilla
 irc
-erikvold
 jsantell
 mossop
 gozala
 zer0
 autonome
 0c0w3
--- a/b2g/config/aries/sources.xml
+++ b/b2g/config/aries/sources.xml
@@ -10,22 +10,22 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="8d83715f08b7849f16a0dfc88f78d5c3a89c0a54">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="11eb5d4cb2675d359d277ae17772bc75f7ccedbc"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="32d827a70af90a05918f234e5b16b35d5d2a07e8"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="fake-qemu-kernel" path="prebuilts/qemu-kernel" remote="b2g" revision="939b377d55a2f081d94029a30a75d05e5a20daf3"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="c1bbb66f52f9e2d76ce97e7b3aa0cb29957cd7d8"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
-  <project name="moztt" path="external/moztt" remote="b2g" revision="31a7849fe9a8b743d6f5e5facc212f0ef9d57499"/>
+  <project name="moztt" path="external/moztt" remote="b2g" revision="3b9a47b517d345b8d98bc7f787b9a6c2f51ca75d"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="5f931350fbc87c3df9db8b0ceb37734b8b471593"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="48d8c7c950745f1b166b42125e6f0d3293d71636"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="0c28789b9957913be975eb002a22323f93585d4c"/>
   <project name="platform_hardware_libhardware_moz" path="hardware/libhardware_moz" remote="b2g" revision="fdf3a143dc777e5f9d33a88373af7ea161d3b440"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="95bb5b66b3ec5769c3de8d3f25d681787418e7d2"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="ebdad82e61c16772f6cd47e9f11936bf6ebe9aa0"/>
--- a/b2g/config/dolphin/sources.xml
+++ b/b2g/config/dolphin/sources.xml
@@ -10,22 +10,22 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="8d83715f08b7849f16a0dfc88f78d5c3a89c0a54">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="11eb5d4cb2675d359d277ae17772bc75f7ccedbc"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="32d827a70af90a05918f234e5b16b35d5d2a07e8"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="fake-qemu-kernel" path="prebuilts/qemu-kernel" remote="b2g" revision="939b377d55a2f081d94029a30a75d05e5a20daf3"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="c1bbb66f52f9e2d76ce97e7b3aa0cb29957cd7d8"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
-  <project name="moztt" path="external/moztt" remote="b2g" revision="31a7849fe9a8b743d6f5e5facc212f0ef9d57499"/>
+  <project name="moztt" path="external/moztt" remote="b2g" revision="3b9a47b517d345b8d98bc7f787b9a6c2f51ca75d"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="5f931350fbc87c3df9db8b0ceb37734b8b471593"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="48d8c7c950745f1b166b42125e6f0d3293d71636"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="0c28789b9957913be975eb002a22323f93585d4c"/>
   <project name="platform_hardware_libhardware_moz" path="hardware/libhardware_moz" remote="b2g" revision="fdf3a143dc777e5f9d33a88373af7ea161d3b440"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="95bb5b66b3ec5769c3de8d3f25d681787418e7d2"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="ebdad82e61c16772f6cd47e9f11936bf6ebe9aa0"/>
--- a/b2g/config/emulator-ics/sources.xml
+++ b/b2g/config/emulator-ics/sources.xml
@@ -14,22 +14,22 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="refs/tags/android-4.0.4_r2.1" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="1b0db93fb6b870b03467aff50d6419771ba0d88c">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="11eb5d4cb2675d359d277ae17772bc75f7ccedbc"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="32d827a70af90a05918f234e5b16b35d5d2a07e8"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="c1bbb66f52f9e2d76ce97e7b3aa0cb29957cd7d8"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="4ace9aaee0e048dfda11bb787646c59982a3dc80"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="c72c9278ddc2f442d193474993d36e7f2cfb08c4"/>
-  <project name="moztt" path="external/moztt" remote="b2g" revision="31a7849fe9a8b743d6f5e5facc212f0ef9d57499"/>
+  <project name="moztt" path="external/moztt" remote="b2g" revision="3b9a47b517d345b8d98bc7f787b9a6c2f51ca75d"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="34ea6163f9f0e0122fb0bb03607eccdca31ced7a"/>
   <project name="platform_hardware_libhardware_moz" path="hardware/libhardware_moz" remote="b2g" revision="fdf3a143dc777e5f9d33a88373af7ea161d3b440"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
   <project name="platform_bionic" path="bionic" remote="b2g" revision="e2b3733ba3fa5e3f404e983d2e4142b1f6b1b846"/>
   <project name="platform/bootable/recovery" path="bootable/recovery" revision="425f8b5fadf5889834c5acd27d23c9e0b2129c28"/>
   <project name="device/common" path="device/common" revision="42b808b7e93d0619286ae8e59110b176b7732389"/>
   <project name="device/sample" path="device/sample" revision="237bd668d0f114d801a8d6455ef5e02cc3577587"/>
--- a/b2g/config/emulator-jb/sources.xml
+++ b/b2g/config/emulator-jb/sources.xml
@@ -12,19 +12,19 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="660169a3d7e034a892359e39135e8c2785a6ad6f">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="11eb5d4cb2675d359d277ae17772bc75f7ccedbc"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="32d827a70af90a05918f234e5b16b35d5d2a07e8"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="c1bbb66f52f9e2d76ce97e7b3aa0cb29957cd7d8"/>
-  <project name="moztt" path="external/moztt" remote="b2g" revision="31a7849fe9a8b743d6f5e5facc212f0ef9d57499"/>
+  <project name="moztt" path="external/moztt" remote="b2g" revision="3b9a47b517d345b8d98bc7f787b9a6c2f51ca75d"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="0c28789b9957913be975eb002a22323f93585d4c"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="5f931350fbc87c3df9db8b0ceb37734b8b471593"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="48d8c7c950745f1b166b42125e6f0d3293d71636"/>
   <project name="platform_hardware_libhardware_moz" path="hardware/libhardware_moz" remote="b2g" revision="fdf3a143dc777e5f9d33a88373af7ea161d3b440"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="5c45f43419d5582949284eee9cef0c43d866e03b"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="3748b4168e7bd8d46457d4b6786003bc6a5223ce"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="9025e50b9d29b3cabbbb21e1dd94d0d13121a17e"/>
--- a/b2g/config/emulator-kk/sources.xml
+++ b/b2g/config/emulator-kk/sources.xml
@@ -10,21 +10,21 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="8d83715f08b7849f16a0dfc88f78d5c3a89c0a54">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="11eb5d4cb2675d359d277ae17772bc75f7ccedbc"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="32d827a70af90a05918f234e5b16b35d5d2a07e8"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="c1bbb66f52f9e2d76ce97e7b3aa0cb29957cd7d8"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
-  <project name="moztt" path="external/moztt" remote="b2g" revision="31a7849fe9a8b743d6f5e5facc212f0ef9d57499"/>
+  <project name="moztt" path="external/moztt" remote="b2g" revision="3b9a47b517d345b8d98bc7f787b9a6c2f51ca75d"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="5f931350fbc87c3df9db8b0ceb37734b8b471593"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="48d8c7c950745f1b166b42125e6f0d3293d71636"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="0c28789b9957913be975eb002a22323f93585d4c"/>
   <project name="platform_hardware_libhardware_moz" path="hardware/libhardware_moz" remote="b2g" revision="fdf3a143dc777e5f9d33a88373af7ea161d3b440"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="f92a936f2aa97526d4593386754bdbf02db07a12"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="6e47ff2790f5656b5b074407829ceecf3e6188c4"/>
--- a/b2g/config/emulator-l/sources.xml
+++ b/b2g/config/emulator-l/sources.xml
@@ -10,21 +10,21 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="c9d4fe680662ee44a4bdea42ae00366f5df399cf">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="11eb5d4cb2675d359d277ae17772bc75f7ccedbc"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="32d827a70af90a05918f234e5b16b35d5d2a07e8"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="c1bbb66f52f9e2d76ce97e7b3aa0cb29957cd7d8"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
-  <project name="moztt" path="external/moztt" remote="b2g" revision="31a7849fe9a8b743d6f5e5facc212f0ef9d57499"/>
+  <project name="moztt" path="external/moztt" remote="b2g" revision="3b9a47b517d345b8d98bc7f787b9a6c2f51ca75d"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="5f931350fbc87c3df9db8b0ceb37734b8b471593"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="48d8c7c950745f1b166b42125e6f0d3293d71636"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="0c28789b9957913be975eb002a22323f93585d4c"/>
   <project name="platform_hardware_libhardware_moz" path="hardware/libhardware_moz" remote="b2g" revision="fdf3a143dc777e5f9d33a88373af7ea161d3b440"/>
   <!-- Stock Android things -->
   <project groups="pdk,linux" name="platform/prebuilts/clang/linux-x86/host/3.5" path="prebuilts/clang/linux-x86/host/3.5" revision="ffc05a232799fe8fcb3e47b7440b52b1fb4244c0"/>
   <project groups="pdk,linux,arm" name="platform/prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.8" path="prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.8" revision="337e0ef5e40f02a1ae59b90db0548976c70a7226"/>
--- a/b2g/config/emulator/sources.xml
+++ b/b2g/config/emulator/sources.xml
@@ -14,22 +14,22 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="refs/tags/android-4.0.4_r2.1" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="1b0db93fb6b870b03467aff50d6419771ba0d88c">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="11eb5d4cb2675d359d277ae17772bc75f7ccedbc"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="32d827a70af90a05918f234e5b16b35d5d2a07e8"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="c1bbb66f52f9e2d76ce97e7b3aa0cb29957cd7d8"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="4ace9aaee0e048dfda11bb787646c59982a3dc80"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="c72c9278ddc2f442d193474993d36e7f2cfb08c4"/>
-  <project name="moztt" path="external/moztt" remote="b2g" revision="31a7849fe9a8b743d6f5e5facc212f0ef9d57499"/>
+  <project name="moztt" path="external/moztt" remote="b2g" revision="3b9a47b517d345b8d98bc7f787b9a6c2f51ca75d"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="34ea6163f9f0e0122fb0bb03607eccdca31ced7a"/>
   <project name="platform_hardware_libhardware_moz" path="hardware/libhardware_moz" remote="b2g" revision="fdf3a143dc777e5f9d33a88373af7ea161d3b440"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
   <project name="platform_bionic" path="bionic" remote="b2g" revision="e2b3733ba3fa5e3f404e983d2e4142b1f6b1b846"/>
   <project name="platform/bootable/recovery" path="bootable/recovery" revision="425f8b5fadf5889834c5acd27d23c9e0b2129c28"/>
   <project name="device/common" path="device/common" revision="42b808b7e93d0619286ae8e59110b176b7732389"/>
   <project name="device/sample" path="device/sample" revision="237bd668d0f114d801a8d6455ef5e02cc3577587"/>
--- a/b2g/config/flame-kk/sources.xml
+++ b/b2g/config/flame-kk/sources.xml
@@ -10,22 +10,22 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="8d83715f08b7849f16a0dfc88f78d5c3a89c0a54">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="11eb5d4cb2675d359d277ae17772bc75f7ccedbc"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="32d827a70af90a05918f234e5b16b35d5d2a07e8"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="fake-qemu-kernel" path="prebuilts/qemu-kernel" remote="b2g" revision="939b377d55a2f081d94029a30a75d05e5a20daf3"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="c1bbb66f52f9e2d76ce97e7b3aa0cb29957cd7d8"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
-  <project name="moztt" path="external/moztt" remote="b2g" revision="31a7849fe9a8b743d6f5e5facc212f0ef9d57499"/>
+  <project name="moztt" path="external/moztt" remote="b2g" revision="3b9a47b517d345b8d98bc7f787b9a6c2f51ca75d"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="5f931350fbc87c3df9db8b0ceb37734b8b471593"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="48d8c7c950745f1b166b42125e6f0d3293d71636"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="0c28789b9957913be975eb002a22323f93585d4c"/>
   <project name="platform_hardware_libhardware_moz" path="hardware/libhardware_moz" remote="b2g" revision="fdf3a143dc777e5f9d33a88373af7ea161d3b440"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="95bb5b66b3ec5769c3de8d3f25d681787418e7d2"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="ebdad82e61c16772f6cd47e9f11936bf6ebe9aa0"/>
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,9 +1,9 @@
 {
     "git": {
-        "git_revision": "11eb5d4cb2675d359d277ae17772bc75f7ccedbc", 
+        "git_revision": "32d827a70af90a05918f234e5b16b35d5d2a07e8", 
         "remote": "https://git.mozilla.org/releases/gaia.git", 
         "branch": ""
     }, 
-    "revision": "6575311e4b9daab959d80f17cf77bea4c91660a6", 
+    "revision": "c63d4e7ff786d83b828d58fced85f9b6406eab32", 
     "repo_path": "integration/gaia-central"
 }
--- a/b2g/config/nexus-4-kk/sources.xml
+++ b/b2g/config/nexus-4-kk/sources.xml
@@ -10,22 +10,22 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="8d83715f08b7849f16a0dfc88f78d5c3a89c0a54">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="11eb5d4cb2675d359d277ae17772bc75f7ccedbc"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="32d827a70af90a05918f234e5b16b35d5d2a07e8"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="fake-qemu-kernel" path="prebuilts/qemu-kernel" remote="b2g" revision="939b377d55a2f081d94029a30a75d05e5a20daf3"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="c1bbb66f52f9e2d76ce97e7b3aa0cb29957cd7d8"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
-  <project name="moztt" path="external/moztt" remote="b2g" revision="31a7849fe9a8b743d6f5e5facc212f0ef9d57499"/>
+  <project name="moztt" path="external/moztt" remote="b2g" revision="3b9a47b517d345b8d98bc7f787b9a6c2f51ca75d"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="5f931350fbc87c3df9db8b0ceb37734b8b471593"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="48d8c7c950745f1b166b42125e6f0d3293d71636"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="0c28789b9957913be975eb002a22323f93585d4c"/>
   <project name="platform_hardware_libhardware_moz" path="hardware/libhardware_moz" remote="b2g" revision="fdf3a143dc777e5f9d33a88373af7ea161d3b440"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="f92a936f2aa97526d4593386754bdbf02db07a12"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="6e47ff2790f5656b5b074407829ceecf3e6188c4"/>
--- a/b2g/config/nexus-4/sources.xml
+++ b/b2g/config/nexus-4/sources.xml
@@ -13,19 +13,19 @@
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="660169a3d7e034a892359e39135e8c2785a6ad6f">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="fake-qemu-kernel" path="prebuilts/qemu-kernel" remote="b2g" revision="939b377d55a2f081d94029a30a75d05e5a20daf3"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="11eb5d4cb2675d359d277ae17772bc75f7ccedbc"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="32d827a70af90a05918f234e5b16b35d5d2a07e8"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="c1bbb66f52f9e2d76ce97e7b3aa0cb29957cd7d8"/>
-  <project name="moztt" path="external/moztt" remote="b2g" revision="31a7849fe9a8b743d6f5e5facc212f0ef9d57499"/>
+  <project name="moztt" path="external/moztt" remote="b2g" revision="3b9a47b517d345b8d98bc7f787b9a6c2f51ca75d"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="0c28789b9957913be975eb002a22323f93585d4c"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="5f931350fbc87c3df9db8b0ceb37734b8b471593"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="48d8c7c950745f1b166b42125e6f0d3293d71636"/>
   <project name="platform_hardware_libhardware_moz" path="hardware/libhardware_moz" remote="b2g" revision="fdf3a143dc777e5f9d33a88373af7ea161d3b440"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="5c45f43419d5582949284eee9cef0c43d866e03b"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="3748b4168e7bd8d46457d4b6786003bc6a5223ce"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="9025e50b9d29b3cabbbb21e1dd94d0d13121a17e"/>
--- a/b2g/config/nexus-5-l/sources.xml
+++ b/b2g/config/nexus-5-l/sources.xml
@@ -10,22 +10,22 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="c9d4fe680662ee44a4bdea42ae00366f5df399cf">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="11eb5d4cb2675d359d277ae17772bc75f7ccedbc"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="32d827a70af90a05918f234e5b16b35d5d2a07e8"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="fake-qemu-kernel" path="prebuilts/qemu-kernel" remote="b2g" revision="939b377d55a2f081d94029a30a75d05e5a20daf3"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="c1bbb66f52f9e2d76ce97e7b3aa0cb29957cd7d8"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
-  <project name="moztt" path="external/moztt" remote="b2g" revision="31a7849fe9a8b743d6f5e5facc212f0ef9d57499"/>
+  <project name="moztt" path="external/moztt" remote="b2g" revision="3b9a47b517d345b8d98bc7f787b9a6c2f51ca75d"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="5f931350fbc87c3df9db8b0ceb37734b8b471593"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="48d8c7c950745f1b166b42125e6f0d3293d71636"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="0c28789b9957913be975eb002a22323f93585d4c"/>
   <project name="platform_hardware_libhardware_moz" path="hardware/libhardware_moz" remote="b2g" revision="fdf3a143dc777e5f9d33a88373af7ea161d3b440"/>
   <!-- Stock Android things -->
   <project groups="pdk,linux" name="platform/prebuilts/clang/linux-x86/host/3.5" path="prebuilts/clang/linux-x86/host/3.5" revision="ffc05a232799fe8fcb3e47b7440b52b1fb4244c0"/>
   <project groups="pdk,linux,arm" name="platform/prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.8" path="prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.8" revision="337e0ef5e40f02a1ae59b90db0548976c70a7226"/>
--- a/browser/base/content/test/alerts/browser.ini
+++ b/browser/base/content/test/alerts/browser.ini
@@ -1,9 +1,10 @@
 [DEFAULT]
 support-files =
   file_dom_notifications.html
 
+[browser_notification_do_not_disturb.js]
 [browser_notification_open_settings.js]
 [browser_notification_remove_permission.js]
 skip-if = e10s
 [browser_notification_tab_switching.js]
 skip-if = buildapp == 'mulet' || e10s # Bug 1100662 - content access causing uncaught exception - Error: cannot ipc non-cpow object at chrome://mochitests/content/browser/browser/base/content/test/general/browser_notification_tab_switching.js:32 (or in RemoteAddonsChild.jsm)
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/alerts/browser_notification_do_not_disturb.js
@@ -0,0 +1,91 @@
+"use strict";
+
+var tab;
+var notification;
+var notification2;
+var notificationURL = "http://example.org/browser/browser/base/content/test/alerts/file_dom_notifications.html";
+
+const ALERT_SERVICE = Cc["@mozilla.org/alerts-service;1"]
+                        .getService(Ci.nsIAlertsService)
+                        .QueryInterface(Ci.nsIAlertsDoNotDisturb);
+
+function test () {
+  waitForExplicitFinish();
+
+  try {
+    // Only run the test if the do-not-disturb
+    // interface has been implemented.
+    ALERT_SERVICE.manualDoNotDisturb;
+    ok(true, "Alert service implements do-not-disturb interface");
+  } catch (e) {
+    ok(true, "Alert service doesn't implement do-not-disturb interface, exiting test");
+    finish();
+    return;
+  }
+
+  let pm = Services.perms;
+  registerCleanupFunction(function() {
+    ALERT_SERVICE.manualDoNotDisturb = false;
+    pm.remove(makeURI(notificationURL), "desktop-notification");
+    gBrowser.removeTab(tab);
+    window.restore();
+  });
+
+  pm.add(makeURI(notificationURL), "desktop-notification", pm.ALLOW_ACTION);
+
+  // Make sure that do-not-disturb is not enabled.
+  ok(!ALERT_SERVICE.manualDoNotDisturb, "Alert service should not be disabled when test starts");
+  ALERT_SERVICE.manualDoNotDisturb = false;
+
+  tab = gBrowser.addTab(notificationURL);
+  gBrowser.selectedTab = tab;
+  tab.linkedBrowser.addEventListener("load", onLoad, true);
+}
+
+function onLoad() {
+  tab.linkedBrowser.removeEventListener("load", onLoad, true);
+  let win = tab.linkedBrowser.contentWindow.wrappedJSObject;
+  notification = win.showNotification2();
+  notification.addEventListener("show", onAlertShowing);
+}
+
+function onAlertShowing() {
+  info("Notification alert showing");
+  notification.removeEventListener("show", onAlertShowing);
+
+  let alertWindow = Services.wm.getMostRecentWindow("alert:alert");
+  if (!alertWindow) {
+    ok(true, "Notifications don't use XUL windows on all platforms.");
+    notification.close();
+    finish();
+    return;
+  }
+  let doNotDisturbMenuItem = alertWindow.document.getElementById("doNotDisturbMenuItem");
+  is(doNotDisturbMenuItem.localName, "menuitem", "menuitem found");
+  alertWindow.addEventListener("beforeunload", onAlertClosing);
+  doNotDisturbMenuItem.click();
+  info("Clicked on do-not-disturb menuitem")
+}
+
+function onAlertClosing(event) {
+  event.target.removeEventListener("beforeunload", onAlertClosing);
+
+  ok(ALERT_SERVICE.manualDoNotDisturb, "Alert service should be disabled after clicking menuitem");
+  let win = tab.linkedBrowser.contentWindow.wrappedJSObject;
+  notification2 = win.showNotification2();
+  notification2.addEventListener("show", onAlert2Showing);
+
+  // The notification should not appear, but there is
+  // no way from the client-side to know that it was
+  // blocked, except for waiting some time and realizing
+  // that the "onshow" event never fired.
+  setTimeout(function() {
+    notification2.removeEventListener("show", onAlert2Showing);
+    finish();
+  }, 2000);
+}
+
+function onAlert2Showing() {
+  ok(false, "the second alert should not have been shown");
+  notification2.close();
+}
--- a/browser/components/preferences/in-content/content.js
+++ b/browser/components/preferences/in-content/content.js
@@ -1,12 +1,25 @@
 /* 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/. */
 
+XPCOMUtils.defineLazyGetter(this, "AlertsServiceDND", function () {
+  try {
+    let alertsService = Cc["@mozilla.org/alerts-service;1"]
+                          .getService(Ci.nsIAlertsService)
+                          .QueryInterface(Ci.nsIAlertsDoNotDisturb);
+    // This will throw if manualDoNotDisturb isn't implemented.
+    alertsService.manualDoNotDisturb;
+    return alertsService;
+  } catch (ex) {
+    return undefined;
+  }
+});
+
 var gContentPane = {
   init: function ()
   {
     function setEventListener(aId, aEventType, aCallback)
     {
       document.getElementById(aId)
               .addEventListener(aEventType, aCallback.bind(gContentPane));
     }
@@ -25,32 +38,46 @@ var gContentPane = {
       row.removeAttribute("hidden");
       // Showing attribution only for Bing Translator.
       Components.utils.import("resource:///modules/translation/Translation.jsm");
       if (Translation.translationEngine == "bing") {
         document.getElementById("bingAttribution").removeAttribute("hidden");
       }
     }
 
+    let doNotDisturbAlertsEnabled = false;
+    if (AlertsServiceDND) {
+      let notificationsDoNotDisturbRow =
+        document.getElementById("notificationsDoNotDisturbRow");
+      notificationsDoNotDisturbRow.removeAttribute("hidden");
+      if (AlertsServiceDND.manualDoNotDisturb) {
+        let notificationsDoNotDisturb =
+          document.getElementById("notificationsDoNotDisturb");
+        notificationsDoNotDisturb.setAttribute("checked", true);
+      }
+    }
+
     setEventListener("font.language.group", "change",
       gContentPane._rebuildFonts);
     setEventListener("notificationsPolicyButton", "command",
       gContentPane.showNotificationExceptions);
     setEventListener("popupPolicyButton", "command",
       gContentPane.showPopupExceptions);
     setEventListener("advancedFonts", "command",
       gContentPane.configureFonts);
     setEventListener("colors", "command",
       gContentPane.configureColors);
     setEventListener("chooseLanguage", "command",
       gContentPane.showLanguages);
     setEventListener("translationAttributionImage", "click",
       gContentPane.openTranslationProviderAttribution);
     setEventListener("translateButton", "command",
       gContentPane.showTranslationExceptions);
+    setEventListener("notificationsDoNotDisturb", "command",
+      gContentPane.toggleDoNotDisturbNotifications);
 
     let drmInfoURL =
       Services.urlFormatter.formatURLPref("app.support.baseURL") + "drm-content";
     document.getElementById("playDRMContentLink").setAttribute("href", drmInfoURL);
     let emeUIEnabled = Services.prefs.getBoolPref("browser.eme.ui.enabled");
     // Force-disable/hide on WinXP:
     if (navigator.platform.toLowerCase().startsWith("win")) {
       emeUIEnabled = emeUIEnabled && parseFloat(Services.sysinfo.get("version")) >= 6;
@@ -248,10 +275,15 @@ var gContentPane = {
   {
     gSubDialog.open("chrome://browser/content/preferences/translation.xul");
   },
 
   openTranslationProviderAttribution: function ()
   {
     Components.utils.import("resource:///modules/translation/Translation.jsm");
     Translation.openProviderAttribution();
-  }
+  },
+
+  toggleDoNotDisturbNotifications: function (event)
+  {
+    AlertsServiceDND.manualDoNotDisturb = event.target.checked;
+  },
 };
--- a/browser/components/preferences/in-content/content.xul
+++ b/browser/components/preferences/in-content/content.xul
@@ -72,16 +72,25 @@
         <vbox align="start">
           <label id="notificationsPolicy">&notificationsPolicyDesc.label;</label>
         </vbox>
         <hbox pack="end">
           <button id="notificationsPolicyButton" label="&notificationsPolicyButton.label;"
                   accesskey="&notificationsPolicyButton.accesskey;"/>
         </hbox>
       </row>
+      <row id="notificationsDoNotDisturbRow" hidden="true">
+        <vbox align="start">
+          <checkbox id="notificationsDoNotDisturb" label="&notificationsDoNotDisturb.label;"
+                    accesskey="&notificationsDoNotDisturb.accesskey;"/>
+          <label id="notificationsDoNotDisturbDetails"
+                 class="indent"
+                 value="&notificationsDoNotDisturbDetails.value;"/>
+        </vbox>
+      </row>
     </rows>
   </grid>
 </groupbox>
 
 <groupbox id="miscGroup" data-category="paneContent" hidden="true">
   <caption><label>&popups.label;</label></caption>
   <grid id="contentGrid">
     <columns>
--- a/browser/components/preferences/in-content/tests/browser.ini
+++ b/browser/components/preferences/in-content/tests/browser.ini
@@ -15,16 +15,17 @@ support-files =
 [browser_change_app_handler.js]
 skip-if = os != "win" # This test tests the windows-specific app selection dialog, so can't run on non-Windows
 [browser_chunk_permissions.js]
 [browser_connection.js]
 [browser_connection_bug388287.js]
 [browser_cookies_exceptions.js]
 [browser_healthreport.js]
 skip-if = !healthreport || (os == 'linux' && debug)
+[browser_notifications_do_not_disturb.js]
 [browser_permissions.js]
 [browser_proxy_backup.js]
 [browser_privacypane_1.js]
 [browser_privacypane_3.js]
 [browser_privacypane_4.js]
 [browser_privacypane_5.js]
 [browser_privacypane_8.js]
 skip-if = e10s # Bug ?????? -  "leaked until shutdown [nsGlobalWindow #99 about:preferences]"
new file mode 100644
--- /dev/null
+++ b/browser/components/preferences/in-content/tests/browser_notifications_do_not_disturb.js
@@ -0,0 +1,44 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+
+registerCleanupFunction(function() {
+  while (gBrowser.tabs[1])
+    gBrowser.removeTab(gBrowser.tabs[1]);
+});
+
+add_task(function() {
+  let prefs = yield openPreferencesViaOpenPreferencesAPI("paneContent", undefined, {leaveOpen: true});
+  is(prefs.selectedPane, "paneContent", "Content pane was selected");
+
+  let doc = gBrowser.contentDocument;
+  let notificationsDoNotDisturbRow = doc.getElementById("notificationsDoNotDisturbRow");
+  if (notificationsDoNotDisturbRow.hidden) {
+    todo(false, "Do not disturb is not available on this platform");
+    return;
+  }
+
+  let alertService;
+  try {
+    alertService = Cc["@mozilla.org/alerts-service;1"]
+                     .getService(Ci.nsIAlertsService)
+                     .QueryInterface(Ci.nsIAlertsDoNotDisturb);
+  } catch (ex) {
+    ok(true, "Do not disturb is not available on this platform: " + ex.message);
+    return;
+  }
+
+  let checkbox = doc.getElementById("notificationsDoNotDisturb");
+  ok(!checkbox.checked, "Checkbox should not be checked by default");
+  ok(!alertService.manualDoNotDisturb, "Do not disturb should be off by default");
+
+  let checkboxChanged = waitForEvent(checkbox, "command")
+  checkbox.click();
+  yield checkboxChanged;
+  ok(alertService.manualDoNotDisturb, "Do not disturb should be enabled when checked");
+
+  checkboxChanged = waitForEvent(checkbox, "command")
+  checkbox.click();
+  yield checkboxChanged;
+  ok(!alertService.manualDoNotDisturb, "Do not disturb should be disabled when unchecked");
+});
--- a/browser/locales/en-US/chrome/browser/devtools/inspector.dtd
+++ b/browser/locales/en-US/chrome/browser/devtools/inspector.dtd
@@ -74,16 +74,42 @@
 <!ENTITY inspectorScrollNodeIntoView.label       "Scroll Into View">
 <!ENTITY inspectorScrollNodeIntoView.accesskey   "S">
 
 <!-- LOCALIZATION NOTE (inspectorHTMLDelete.label): This is the label shown in
      the inspector contextual-menu for the item that lets users delete the
      current node -->
 <!ENTITY inspectorHTMLDelete.label          "Delete Node">
 <!ENTITY inspectorHTMLDelete.accesskey      "D">
+<!-- LOCALIZATION NOTE (inspectorAttributeSubmenu.label): This is the label
+     shown in the inspector contextual-menu for the sub-menu of the other
+     attribute items, which allow to:
+     - add new attribute
+     - edit attribute
+     - remove attribute -->
+<!ENTITY inspectorAttributeSubmenu.label      "Attribute">
+<!ENTITY inspectorAttributeSubmenu.accesskey  "A">
+
+<!-- LOCALIZATION NOTE (inspectorAddAttribute.label): This is the label shown in
+     the inspector contextual-menu for the item that lets users add attribute
+     to current node -->
+<!ENTITY inspectorAddAttribute.label        "Add Attribute">
+<!ENTITY inspectorAddAttribute.accesskey    "A">
+
+<!-- LOCALIZATION NOTE (inspectorEditAttribute.label): This is the label shown in
+     the inspector contextual-menu for the item that lets users edit attribute
+     for current node -->
+<!ENTITY inspectorEditAttribute.label        "Edit Attribute">
+<!ENTITY inspectorEditAttribute.accesskey    "E">
+
+<!-- LOCALIZATION NOTE (inspectorRemoveAttribute.label): This is the label shown in
+     the inspector contextual-menu for the item that lets users delete attribute
+     from current node -->
+<!ENTITY inspectorRemoveAttribute.label        "Remove Attribute">
+<!ENTITY inspectorRemoveAttribute.accesskey    "R">
 
 <!ENTITY inspector.selectButton.tooltip     "Select element with mouse">
 
 <!-- LOCALIZATION NOTE (inspectorSearchHTML.label2): This is the label shown as
      the placeholder in inspector search box -->
 <!ENTITY inspectorSearchHTML.label2          "Search with CSS Selectors">
 <!ENTITY inspectorSearchHTML.key            "F">
 
--- a/browser/locales/en-US/chrome/browser/devtools/inspector.properties
+++ b/browser/locales/en-US/chrome/browser/devtools/inspector.properties
@@ -99,8 +99,20 @@ inspector.menu.openUrlInNewTab.label=Ope
 inspector.menu.copyUrlToClipboard.label=Copy Link Address
 
 # LOCALIZATION NOTE (inspector.menu.selectElement.label): This is the label of a
 # menu item in the inspector contextual-menu that appears when the user right-
 # clicks on the attribute of a node in the inspector that is the ID of another
 # element in the DOM (like with <label for="input-id">), and that allows to
 # select that element in the inspector.
 inspector.menu.selectElement.label=Select Element #%S
+
+# LOCALIZATION NOTE (inspector.menu.editAttribute.label): This is the label of a
+# sub-menu "Attribute" in the inspector contextual-menu that appears
+# when the user right-clicks on the node in the inspector, and that allows
+# to edit an attribute on this node.
+inspector.menu.editAttribute.label=Edit Attribute %S
+
+# LOCALIZATION NOTE (inspector.menu.removeAttribute.label): This is the label of a
+# sub-menu "Attribute" in the inspector contextual-menu that appears
+# when the user right-clicks on the attribute of a node in the inspector,
+# and that allows to remove this attribute.
+inspector.menu.removeAttribute.label=Remove Attribute %S
--- a/browser/locales/en-US/chrome/browser/preferences/content.dtd
+++ b/browser/locales/en-US/chrome/browser/preferences/content.dtd
@@ -6,16 +6,19 @@
 
 <!ENTITY  blockPopups.label           "Block pop-up windows">
 <!ENTITY  blockPopups.accesskey       "B">
 
 <!ENTITY  notificationsPolicy.label            "Notifications">
 <!ENTITY  notificationsPolicyDesc.label        "Choose which sites are allowed to show notifications">
 <!ENTITY  notificationsPolicyButton.accesskey  "h">
 <!ENTITY  notificationsPolicyButton.label      "Choose…">
+<!ENTITY  notificationsDoNotDisturb.label      "Do not disturb me">
+<!ENTITY  notificationsDoNotDisturb.accesskey  "n">
+<!ENTITY  notificationsDoNotDisturbDetails.value "No notification will be shown until you restart &brandShortName;">
 
 <!ENTITY  popupExceptions.label       "Exceptions…">
 <!ENTITY  popupExceptions.accesskey   "E">
 
 <!ENTITY  fontsAndColors.label        "Fonts &amp; Colors">
 
 <!ENTITY  defaultFont.label           "Default font:">
 <!ENTITY  defaultFont.accesskey       "D">
--- a/browser/themes/shared/incontentprefs/preferences.inc.css
+++ b/browser/themes/shared/incontentprefs/preferences.inc.css
@@ -266,17 +266,19 @@ description > html|a {
   /* !important is needed to override the rules defined in common.css */
   -moz-margin-start: 20px !important;
   /* center the links */
   margin-top: 8px;
   margin-bottom: 8px;
 }
 
 .indent {
-  -moz-margin-start: 33px;
+  /* !important needed to override -moz-margin-start:0 !important; rule
+     define in common.css for labels */
+  -moz-margin-start: 33px !important;
 }
 
 .text-link {
   margin-bottom: 0;
 }
 
 #showUpdateHistory {
   -moz-margin-start: 0;
--- a/devtools/client/inspector/inspector-panel.js
+++ b/devtools/client/inspector/inspector-panel.js
@@ -72,16 +72,17 @@ const LAYOUT_CHANGE_TIMER = 250;
  */
 function InspectorPanel(iframeWindow, toolbox) {
   this._toolbox = toolbox;
   this._target = toolbox._target;
   this.panelDoc = iframeWindow.document;
   this.panelWin = iframeWindow;
   this.panelWin.inspector = this;
 
+  this.nodeMenuTriggerInfo = null;
   this._onBeforeNavigate = this._onBeforeNavigate.bind(this);
   this._target.on("will-navigate", this._onBeforeNavigate);
 
   EventEmitter.decorate(this);
 }
 
 exports.InspectorPanel = InspectorPanel;
 
@@ -642,19 +643,24 @@ InspectorPanel.prototype = {
       if (content && content.trim().length > 0) {
         return content;
       }
     }
     return null;
   },
 
   /**
-   * Disable the delete item if needed. Update the pseudo classes.
+   * Update, enable, disable, hide, show any menu item depending on the current
+   * element.
    */
-  _setupNodeMenu: function() {
+  _setupNodeMenu: function(event) {
+    let markupContainer = this.markup.getContainer(this.selection.nodeFront);
+    this.nodeMenuTriggerInfo =
+      markupContainer.editor.getInfoAtNode(event.target.triggerNode);
+
     let isSelectionElement = this.selection.isElementNode() &&
                              !this.selection.isPseudoElementNode();
     let isEditableElement = isSelectionElement &&
                             !this.selection.isAnonymousNode();
     let isDuplicatableElement = isSelectionElement &&
                                 !this.selection.isAnonymousNode() &&
                                 !this.selection.isRoot();
     let isScreenshotable = isSelectionElement &&
@@ -691,19 +697,18 @@ InspectorPanel.prototype = {
     let copyOuterHTML = this.panelDoc.getElementById("node-menu-copyouter");
     let scrollIntoView = this.panelDoc.getElementById("node-menu-scrollnodeintoview");
     let expandAll = this.panelDoc.getElementById("node-menu-expand");
     let collapse = this.panelDoc.getElementById("node-menu-collapse");
 
     expandAll.setAttribute("disabled", "true");
     collapse.setAttribute("disabled", "true");
 
-    let markUpContainer = this.markup.importNode(this.selection.nodeFront, false);
-    if (this.selection.isNode() && markUpContainer.hasChildren) {
-      if (markUpContainer.expanded) {
+    if (this.selection.isNode() && markupContainer.hasChildren) {
+      if (markupContainer.expanded) {
         collapse.removeAttribute("disabled");
       }
       expandAll.removeAttribute("disabled");
     }
 
     this._target.actorHasMethod("domwalker", "duplicateNode").then(value => {
       duplicateNode.hidden = !value;
     });
@@ -779,22 +784,62 @@ InspectorPanel.prototype = {
       pasteAfter.disabled = true;
       pasteFirstChild.disabled = true;
       pasteLastChild.disabled = true;
     }
 
     // Enable the "copy image data-uri" item if the selection is previewable
     // which essentially checks if it's an image or canvas tag
     let copyImageData = this.panelDoc.getElementById("node-menu-copyimagedatauri");
-    let markupContainer = this.markup.getContainer(this.selection.nodeFront);
     if (isSelectionElement && markupContainer && markupContainer.isPreviewable()) {
       copyImageData.removeAttribute("disabled");
     } else {
       copyImageData.setAttribute("disabled", "true");
     }
+
+    // Enable / disable "Add Attribute", "Edit Attribute"
+    // and "Remove Attribute" items
+    this._setupAttributeMenu(isEditableElement);
+  },
+
+  _setupAttributeMenu: function(isEditableElement) {
+    let addAttribute = this.panelDoc.getElementById("node-menu-add-attribute");
+    let editAttribute = this.panelDoc.getElementById("node-menu-edit-attribute");
+    let removeAttribute = this.panelDoc.getElementById("node-menu-remove-attribute");
+    let nodeInfo = this.nodeMenuTriggerInfo;
+
+    // Enable "Add Attribute" for all editable elements
+    if (isEditableElement) {
+      addAttribute.removeAttribute("disabled");
+    } else {
+      addAttribute.setAttribute("disabled", "true");
+    }
+
+    // Enable "Edit Attribute" and "Remove Attribute" only on attribute click
+    if (isEditableElement && nodeInfo && nodeInfo.type === "attribute") {
+      editAttribute.removeAttribute("disabled");
+      editAttribute.setAttribute("label",
+        strings.formatStringFromName(
+          "inspector.menu.editAttribute.label", [`"${nodeInfo.name}"`], 1));
+
+      removeAttribute.removeAttribute("disabled");
+      removeAttribute.setAttribute("label",
+        strings.formatStringFromName(
+          "inspector.menu.removeAttribute.label", [`"${nodeInfo.name}"`], 1));
+    } else {
+      editAttribute.setAttribute("disabled", "true");
+      editAttribute.setAttribute("label",
+        strings.formatStringFromName(
+          "inspector.menu.editAttribute.label", [''], 1));
+
+      removeAttribute.setAttribute("disabled", "true");
+      removeAttribute.setAttribute("label",
+        strings.formatStringFromName(
+          "inspector.menu.removeAttribute.label", [''], 1));
+    }
   },
 
   _resetNodeMenu: function() {
     // Remove any extra items
     while (this.lastNodemenuItem.nextSibling) {
       let toDelete = this.lastNodemenuItem.nextSibling;
       toDelete.parentNode.removeChild(toDelete);
     }
@@ -1219,16 +1264,43 @@ InspectorPanel.prototype = {
     if (this.markup) {
       this.markup.deleteNode(this.selection.nodeFront);
     } else {
       // remove the node from content
       this.walker.removeNode(this.selection.nodeFront);
     }
   },
 
+  /**
+   * Add attribute to node.
+   * Used for node context menu and shouldn't be called directly.
+   */
+  onAddAttribute: function() {
+    let container = this.markup.getContainer(this.selection.nodeFront);
+    container.addAttribute();
+  },
+
+  /**
+   * Edit attribute for node.
+   * Used for node context menu and shouldn't be called directly.
+   */
+  onEditAttribute: function() {
+    let container = this.markup.getContainer(this.selection.nodeFront);
+    container.editAttribute(this.nodeMenuTriggerInfo.name);
+  },
+
+  /**
+   * Remove attribute from node.
+   * Used for node context menu and shouldn't be called directly.
+   */
+  onRemoveAttribute: function() {
+    let container = this.markup.getContainer(this.selection.nodeFront);
+    container.removeAttribute(this.nodeMenuTriggerInfo.name);
+  },
+
   expandNode: function() {
     this.markup.expandAll(this.selection.nodeFront);
   },
 
   collapseNode: function() {
     this.markup.collapseNode(this.selection.nodeFront);
   },
 
--- a/devtools/client/inspector/inspector.xul
+++ b/devtools/client/inspector/inspector.xul
@@ -105,16 +105,33 @@
         oncommand="inspector.screenshotNode()" />
       <menuitem id="node-menu-duplicatenode"
         label="&inspectorDuplicateNode.label;"
         oncommand="inspector.duplicateNode()"/>
       <menuitem id="node-menu-delete"
         label="&inspectorHTMLDelete.label;"
         accesskey="&inspectorHTMLDelete.accesskey;"
         oncommand="inspector.deleteNode()"/>
+      <menu label="&inspectorAttributeSubmenu.label;"
+        accesskey="&inspectorAttributeSubmenu.accesskey;">
+        <menupopup>
+          <menuitem id="node-menu-add-attribute"
+            label="&inspectorAddAttribute.label;"
+            accesskey="&inspectorAddAttribute.accesskey;"
+            oncommand="inspector.onAddAttribute()"/>
+          <menuitem id="node-menu-edit-attribute"
+            label="&inspectorEditAttribute.label;"
+            accesskey="&inspectorEditAttribute.accesskey;"
+            oncommand="inspector.onEditAttribute()"/>
+          <menuitem id="node-menu-remove-attribute"
+            label="&inspectorRemoveAttribute.label;"
+            accesskey="&inspectorRemoveAttribute.accesskey;"
+            oncommand="inspector.onRemoveAttribute()"/>
+        </menupopup>
+      </menu>
       <menuseparator id="node-menu-link-separator"/>
       <menuitem id="node-menu-link-follow"
         oncommand="inspector.onFollowLink()"/>
       <menuitem id="node-menu-link-copy"
         oncommand="inspector.onCopyLink()"/>
       <menuseparator/>
       <menuitem id="node-menu-pseudo-hover"
         label=":hover" type="checkbox"
--- a/devtools/client/inspector/test/browser.ini
+++ b/devtools/client/inspector/test/browser.ini
@@ -81,17 +81,18 @@ skip-if = e10s # GCLI isn't e10s compati
 [browser_inspector_inspect-object-element.js]
 [browser_inspector_invalidate.js]
 [browser_inspector_keyboard-shortcuts-copy-outerhtml.js]
 [browser_inspector_keyboard-shortcuts.js]
 [browser_inspector_menu-01-sensitivity.js]
 [browser_inspector_menu-02-copy-items.js]
 [browser_inspector_menu-03-paste-items.js]
 [browser_inspector_menu-04-use-in-console.js]
-[browser_inspector_menu-05-other.js]
+[browser_inspector_menu-05-attribute-items.js]
+[browser_inspector_menu-06-other.js]
 [browser_inspector_navigation.js]
 [browser_inspector_pane-toggle-01.js]
 [browser_inspector_pane-toggle-02.js]
 [browser_inspector_pane-toggle-03.js]
 [browser_inspector_picker-stop-on-destroy.js]
 [browser_inspector_picker-stop-on-tool-change.js]
 [browser_inspector_pseudoclass-lock.js]
 [browser_inspector_pseudoclass-menu.js]
--- a/devtools/client/inspector/test/browser_inspector_menu-01-sensitivity.js
+++ b/devtools/client/inspector/test/browser_inspector_menu-01-sensitivity.js
@@ -27,22 +27,34 @@ const ALL_MENU_ITEMS = [
   "node-menu-copyouter",
   "node-menu-copyuniqueselector",
   "node-menu-copyimagedatauri",
   "node-menu-delete",
   "node-menu-pseudo-hover",
   "node-menu-pseudo-active",
   "node-menu-pseudo-focus",
   "node-menu-scrollnodeintoview",
-  "node-menu-screenshotnode"
+  "node-menu-screenshotnode",
+  "node-menu-add-attribute",
+  "node-menu-edit-attribute",
+  "node-menu-remove-attribute"
 ].concat(PASTE_MENU_ITEMS, ACTIVE_ON_DOCTYPE_ITEMS);
 
 const INACTIVE_ON_DOCTYPE_ITEMS =
   ALL_MENU_ITEMS.filter(item => ACTIVE_ON_DOCTYPE_ITEMS.indexOf(item) === -1);
 
+/**
+ * Test cases, each item of this array may define the following properties:
+ *   desc: string that will be logged
+ *   selector: selector of the node to be selected
+ *   disabled: items that should have disabled state
+ *   clipboardData: clipboard content
+ *   clipboardDataType: clipboard content type
+ *   attributeTrigger: attribute that will be used as context menu trigger
+ */
 const TEST_CASES = [
   {
     desc: "doctype node with empty clipboard",
     selector: null,
     disabled: INACTIVE_ON_DOCTYPE_ITEMS,
   },
   {
     desc: "doctype node with html on clipboard",
@@ -50,138 +62,183 @@ const TEST_CASES = [
     clipboardDataType: "html",
     selector: null,
     disabled: INACTIVE_ON_DOCTYPE_ITEMS,
   },
   {
     desc: "element node HTML on the clipboard",
     clipboardData: "<p>some text</p>",
     clipboardDataType: "html",
-    disabled: ["node-menu-copyimagedatauri"],
+    disabled: [
+      "node-menu-copyimagedatauri",
+      "node-menu-edit-attribute",
+      "node-menu-remove-attribute"
+    ],
     selector: "#sensitivity",
   },
   {
     desc: "<html> element",
     clipboardData: "<p>some text</p>",
     clipboardDataType: "html",
     selector: "html",
     disabled: [
       "node-menu-copyimagedatauri",
       "node-menu-pastebefore",
       "node-menu-pasteafter",
       "node-menu-pastefirstchild",
       "node-menu-pastelastchild",
+      "node-menu-edit-attribute",
+      "node-menu-remove-attribute"
     ],
   },
   {
     desc: "<body> with HTML on clipboard",
     clipboardData: "<p>some text</p>",
     clipboardDataType: "html",
     selector: "body",
     disabled: [
       "node-menu-copyimagedatauri",
       "node-menu-pastebefore",
       "node-menu-pasteafter",
+      "node-menu-edit-attribute",
+      "node-menu-remove-attribute"
     ]
   },
   {
     desc: "<img> with HTML on clipboard",
     clipboardData: "<p>some text</p>",
     clipboardDataType: "html",
     selector: "img",
-    disabled: []
+    disabled: [
+      "node-menu-edit-attribute",
+      "node-menu-remove-attribute"
+    ]
   },
   {
     desc: "<head> with HTML on clipboard",
     clipboardData: "<p>some text</p>",
     clipboardDataType: "html",
     selector: "head",
     disabled: [
       "node-menu-copyimagedatauri",
       "node-menu-pastebefore",
       "node-menu-pasteafter",
       "node-menu-screenshotnode",
+      "node-menu-edit-attribute",
+      "node-menu-remove-attribute"
     ],
   },
   {
     desc: "<head> with no html on clipboard",
     selector: "head",
     disabled: PASTE_MENU_ITEMS.concat([
       "node-menu-copyimagedatauri",
       "node-menu-screenshotnode",
+      "node-menu-edit-attribute",
+      "node-menu-remove-attribute"
     ]),
   },
   {
     desc: "<element> with text on clipboard",
     clipboardData: "some text",
     clipboardDataType: undefined,
     selector: "#paste-area",
-    disabled: ["node-menu-copyimagedatauri"],
+    disabled: [
+      "node-menu-copyimagedatauri",
+      "node-menu-edit-attribute",
+      "node-menu-remove-attribute"
+    ]
   },
   {
     desc: "<element> with base64 encoded image data uri on clipboard",
     clipboardData:
       "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABC" +
       "AAAAAA6fptVAAAACklEQVQYV2P4DwABAQEAWk1v8QAAAABJRU5ErkJggg==",
     clipboardDataType: undefined,
     selector: "#paste-area",
-    disabled: PASTE_MENU_ITEMS.concat(["node-menu-copyimagedatauri"]),
+    disabled: PASTE_MENU_ITEMS.concat([
+      "node-menu-copyimagedatauri",
+      "node-menu-edit-attribute",
+      "node-menu-remove-attribute"
+    ]),
   },
   {
     desc: "<element> with empty string on clipboard",
     clipboardData: "",
     clipboardDataType: undefined,
     selector: "#paste-area",
-    disabled: PASTE_MENU_ITEMS.concat(["node-menu-copyimagedatauri"]),
+    disabled: PASTE_MENU_ITEMS.concat([
+      "node-menu-copyimagedatauri",
+      "node-menu-edit-attribute",
+      "node-menu-remove-attribute"
+    ]),
   },
   {
     desc: "<element> with whitespace only on clipboard",
     clipboardData: " \n\n\t\n\n  \n",
     clipboardDataType: undefined,
     selector: "#paste-area",
-    disabled: PASTE_MENU_ITEMS.concat(["node-menu-copyimagedatauri"]),
+    disabled: PASTE_MENU_ITEMS.concat([
+      "node-menu-copyimagedatauri",
+      "node-menu-edit-attribute",
+      "node-menu-remove-attribute"
+    ]),
   },
   {
     desc: "<element> that isn't visible on the page, empty clipboard",
     selector: "#hiddenElement",
     disabled: PASTE_MENU_ITEMS.concat([
       "node-menu-copyimagedatauri",
       "node-menu-screenshotnode",
+      "node-menu-edit-attribute",
+      "node-menu-remove-attribute"
     ]),
   },
   {
     desc: "<element> nested in another hidden element, empty clipboard",
     selector: "#nestedHiddenElement",
     disabled: PASTE_MENU_ITEMS.concat([
       "node-menu-copyimagedatauri",
       "node-menu-screenshotnode",
+      "node-menu-edit-attribute",
+      "node-menu-remove-attribute"
     ]),
+  },
+  {
+    desc: "<element> with context menu triggered on attribute, empty clipboard",
+    selector: "#attributes",
+    disabled: PASTE_MENU_ITEMS.concat(["node-menu-copyimagedatauri"]),
+    attributeTrigger: "data-edit"
   }
 ];
 
 var clipboard = require("sdk/clipboard");
 registerCleanupFunction(() => {
   clipboard = null;
 });
 
 add_task(function *() {
   let { inspector } = yield openInspectorForURL(TEST_URL);
   for (let test of TEST_CASES) {
-    let { desc, disabled, selector } = test;
+    let { desc, disabled, selector, attributeTrigger } = test;
 
     info(`Test ${desc}`);
     setupClipboard(test.clipboardData, test.clipboardDataType);
 
     let front = yield getNodeFrontForSelector(selector, inspector);
 
     info("Selecting the specified node.");
     yield selectNode(front, inspector);
 
     info("Simulating context menu click on the selected node container.");
-    contextMenuClick(getContainerForNodeFront(front, inspector).tagLine);
+    let nodeFrontContainer = getContainerForNodeFront(front, inspector);
+    let contextMenuTrigger = attributeTrigger
+      ? nodeFrontContainer.tagLine.querySelector(`[data-attr="${attributeTrigger}"]`)
+      : nodeFrontContainer.tagLine;
+    contextMenuClick(contextMenuTrigger);
 
     for (let menuitem of ALL_MENU_ITEMS) {
       let elt = inspector.panelDoc.getElementById(menuitem);
       let shouldBeDisabled = disabled.indexOf(menuitem) !== -1;
       let isDisabled = elt.hasAttribute("disabled");
 
       is(isDisabled, shouldBeDisabled,
         `#${menuitem} should be ${shouldBeDisabled ? "disabled" : "enabled"} `);
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/test/browser_inspector_menu-05-attribute-items.js
@@ -0,0 +1,75 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
+
+// Test that attribute items work in the context menu
+
+const TEST_URL = TEST_URL_ROOT + "doc_inspector_menu.html";
+
+add_task(function* () {
+  let { inspector, toolbox, testActor } = yield openInspectorForURL(TEST_URL);
+  yield selectNode("#attributes", inspector);
+
+  yield testAddAttribute();
+  yield testEditAttribute();
+  yield testRemoveAttribute();
+
+  function* testAddAttribute() {
+    info("Testing 'Add Attribute' menu item");
+    let addAttribute = getMenuItem("node-menu-add-attribute");
+
+    info("Triggering 'Add Attribute' and waiting for mutation to occur");
+    dispatchCommandEvent(addAttribute);
+    EventUtils.synthesizeKey('class="u-hidden"', {});
+    let onMutation = inspector.once("markupmutation");
+    EventUtils.synthesizeKey('VK_RETURN', {});
+    yield onMutation;
+
+    let hasAttribute = testActor.hasNode("#attributes.u-hidden");
+    ok(hasAttribute, "attribute was successfully added");
+  }
+
+  function* testEditAttribute() {
+    info("Testing 'Edit Attribute' menu item");
+    let editAttribute = getMenuItem("node-menu-edit-attribute");
+
+    info("Triggering 'Edit Attribute' and waiting for mutation to occur");
+    inspector.nodeMenuTriggerInfo = {
+      type: "attribute",
+      name: "data-edit"
+    };
+    dispatchCommandEvent(editAttribute);
+    EventUtils.synthesizeKey("data-edit='edited'", {});
+    let onMutation = inspector.once("markupmutation");
+    EventUtils.synthesizeKey('VK_RETURN', {});
+    yield onMutation;
+
+    let isAttributeChanged =
+      yield testActor.hasNode("#attributes[data-edit='edited']");
+    ok(isAttributeChanged, "attribute was successfully edited");
+  }
+
+  function* testRemoveAttribute() {
+    info("Testing 'Remove Attribute' menu item");
+    let removeAttribute = getMenuItem("node-menu-remove-attribute");
+
+    info("Triggering 'Remove Attribute' and waiting for mutation to occur");
+    inspector.nodeMenuTriggerInfo = {
+      type: "attribute",
+      name: "data-remove"
+    };
+    let onMutation = inspector.once("markupmutation");
+    dispatchCommandEvent(removeAttribute);
+    yield onMutation;
+
+    let hasAttribute = yield testActor.hasNode("#attributes[data-remove]")
+    ok(!hasAttribute, "attribute was successfully removed");
+  }
+
+  function getMenuItem(id) {
+    let attribute = inspector.panelDoc.getElementById(id);
+    ok(attribute, "Menu item '" + id + "' found");
+    return attribute;
+  }
+});
rename from devtools/client/inspector/test/browser_inspector_menu-05-other.js
rename to devtools/client/inspector/test/browser_inspector_menu-06-other.js
--- a/devtools/client/inspector/test/doc_inspector_menu.html
+++ b/devtools/client/inspector/test/doc_inspector_menu.html
@@ -18,11 +18,12 @@
       <p class="duplicate">This will be duplicated</p>
       <p id="delete">This has to be deleted</p>
       <img id="copyimage" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAAAAAA6fptVAAAACklEQVQYV2P4DwABAQEAWk1v8QAAAABJRU5ErkJggg==" />
       <div id="hiddenElement" style="display: none;">
         <p id="nestedHiddenElement">Visible element nested inside a non-visible element</p>
       </div>
       <p id="console-var">Paragraph for testing console variables</p>
       <p id="console-var-multi">Paragraph for testing multiple console variables</p>
+      <p id="attributes" data-edit="original" data-remove="thing">Attributes are going to be changed here</p>
     </div>
   </body>
 </html>
--- a/devtools/client/markupview/markup-view.js
+++ b/devtools/client/markupview/markup-view.js
@@ -2300,16 +2300,46 @@ MarkupElementContainer.prototype = Herit
   setSingleTextChild: function(singleTextChild) {
     this.singleTextChild = singleTextChild;
     this.editor.updateTextEditor();
   },
 
   clearSingleTextChild: function() {
     this.singleTextChild = undefined;
     this.editor.updateTextEditor();
+  },
+
+  /**
+   * Trigger new attribute field for input.
+   */
+  addAttribute: function() {
+    this.editor.newAttr.editMode();
+  },
+
+  /**
+   * Trigger attribute field for editing.
+   */
+  editAttribute: function(attrName) {
+    this.editor.attrElements.get(attrName).editMode();
+  },
+
+  /**
+   * Remove attribute from container.
+   * This is an undoable action.
+   */
+  removeAttribute: function(attrName) {
+    let doMods = this.editor._startModifyingAttributes();
+    let undoMods = this.editor._startModifyingAttributes();
+    this.editor._saveAttribute(attrName, undoMods);
+    doMods.removeAttribute(attrName);
+    this.undo.do(() => {
+      doMods.apply();
+    }, () => {
+      undoMods.apply();
+    });
   }
 });
 
 /**
  * Dummy container node used for the root document element.
  */
 function RootContainer(aMarkupView, aNode) {
   this.doc = aMarkupView.doc;
@@ -2356,16 +2386,23 @@ function GenericEditor(aContainer, aNode
   } else {
     this.tag.textContent = aNode.nodeName;
   }
 }
 
 GenericEditor.prototype = {
   destroy: function() {
     this.elt.remove();
+  },
+
+  /**
+   * Stub method for consistency with ElementEditor.
+   */
+  getInfoAtNode: function() {
+    return null;
   }
 };
 
 /**
  * Creates a simple text editor node, used for TEXT and COMMENT
  * nodes.
  *
  * @param MarkupContainer aContainer The container owning this editor.
@@ -2438,17 +2475,24 @@ TextEditor.prototype = {
         if (this.selected) {
           this.value.textContent = str;
           this.markup.emit("text-expand")
         }
       }).then(null, console.error);
     }
   },
 
-  destroy: function() {}
+  destroy: function() {},
+
+  /**
+   * Stub method for consistency with ElementEditor.
+   */
+  getInfoAtNode: function() {
+    return null;
+  }
 };
 
 /**
  * Creates an editor for an Element node.
  *
  * @param MarkupContainer aContainer The container owning this editor.
  * @param Element aNode The node being edited.
  */
@@ -2492,28 +2536,24 @@ function ElementEditor(aContainer, aNode
     stopOnReturn: true,
     contentType: InplaceEditor.CONTENT_TYPES.CSS_MIXED,
     popup: this.markup.popup,
     done: (aVal, aCommit) => {
       if (!aCommit) {
         return;
       }
 
-      try {
-        let doMods = this._startModifyingAttributes();
-        let undoMods = this._startModifyingAttributes();
-        this._applyAttributes(aVal, null, doMods, undoMods);
-        this.container.undo.do(() => {
-          doMods.apply();
-        }, function() {
-          undoMods.apply();
-        });
-      } catch(x) {
-        console.error(x);
-      }
+      let doMods = this._startModifyingAttributes();
+      let undoMods = this._startModifyingAttributes();
+      this._applyAttributes(aVal, null, doMods, undoMods);
+      this.container.undo.do(() => {
+        doMods.apply();
+      }, function() {
+        undoMods.apply();
+      });
     }
   });
 
   let tagName = this.node.nodeName.toLowerCase();
   this.tag.textContent = tagName;
   this.closeTag.textContent = tagName;
   this.eventNode.style.display = this.node.hasEventListeners ? "inline-block" : "none";
 
@@ -2535,16 +2575,45 @@ ElementEditor.prototype = {
     }
 
     flashElementOn(this.getAttributeElement(attrName));
 
     this.animationTimers[attrName] = setTimeout(() => {
       flashElementOff(this.getAttributeElement(attrName));
     }, this.markup.CONTAINER_FLASHING_DURATION);
   },
+  /**
+   * Returns information about node in the editor.
+   *
+   * @param {DOMNode} node
+   *        The node to get information from.
+   *
+   * @return {Object}
+   *         An object literal with the following information:
+   *         {type: "attribute", name: "rel", value: "index", el: node}
+   */
+  getInfoAtNode: function(node) {
+    if (!node) {
+      return null;
+    }
+
+    let type = null;
+    let name = null;
+    let value = null;
+
+    // Attribute
+    let attribute = node.closest('.attreditor');
+    if (attribute) {
+      type = "attribute";
+      name = attribute.querySelector('.attr-name').textContent;
+      value = attribute.querySelector('.attr-value').textContent;
+    }
+
+    return {type, name, value, el: node};
+  },
 
   /**
    * Update the state of the editor from the node.
    */
   update: function() {
     let nodeAttributes = this.node.attributes || [];
 
     // Keep the data model in sync with attributes on the node.
@@ -2692,29 +2761,25 @@ ElementEditor.prototype = {
         }
 
         let doMods = this._startModifyingAttributes();
         let undoMods = this._startModifyingAttributes();
 
         // Remove the attribute stored in this editor and re-add any attributes
         // parsed out of the input element. Restore original attribute if
         // parsing fails.
-        try {
-          this.refocusOnEdit(aAttr.name, attr, direction);
-          this._saveAttribute(aAttr.name, undoMods);
-          doMods.removeAttribute(aAttr.name);
-          this._applyAttributes(aVal, attr, doMods, undoMods);
-          this.container.undo.do(() => {
-            doMods.apply();
-          }, () => {
-            undoMods.apply();
-          });
-        } catch(ex) {
-          console.error(ex);
-        }
+        this.refocusOnEdit(aAttr.name, attr, direction);
+        this._saveAttribute(aAttr.name, undoMods);
+        doMods.removeAttribute(aAttr.name);
+        this._applyAttributes(aVal, attr, doMods, undoMods);
+        this.container.undo.do(() => {
+          doMods.apply();
+        }, () => {
+          undoMods.apply();
+        });
       }
     });
 
     // Figure out where we should place the attribute.
     let before = aBefore;
     if (aAttr.name == "id") {
       before = this.attrList.firstChild;
     } else if (aAttr.name == "class") {
--- a/mobile/android/base/BrowserApp.java
+++ b/mobile/android/base/BrowserApp.java
@@ -204,17 +204,16 @@ public class BrowserApp extends GeckoApp
 
     private static final int GECKO_TOOLS_MENU = -1;
     private static final int ADDON_MENU_OFFSET = 1000;
     public static final String TAB_HISTORY_FRAGMENT_TAG = "tabHistoryFragment";
 
     private static class MenuItemInfo {
         public int id;
         public String label;
-        public String icon;
         public boolean checkable;
         public boolean checked;
         public boolean enabled = true;
         public boolean visible = true;
         public int parent;
         public boolean added;   // So we can re-add after a locale change.
     }
 
@@ -1758,17 +1757,16 @@ public class BrowserApp extends GeckoApp
             final Intent intent = new Intent(Intent.ACTION_VIEW);
             intent.setData(Uri.parse("market://details?id=" + getPackageName()));
             startActivity(intent);
 
         } else if ("Menu:Add".equals(event)) {
             final MenuItemInfo info = new MenuItemInfo();
             info.label = message.getString("name");
             info.id = message.getInt("id") + ADDON_MENU_OFFSET;
-            info.icon = message.optString("icon", null);
             info.checked = message.optBoolean("checked", false);
             info.enabled = message.optBoolean("enabled", true);
             info.visible = message.optBoolean("visible", true);
             info.checkable = message.optBoolean("checkable", false);
             final int parent = message.optInt("parent", 0);
             info.parent = parent <= 0 ? parent : parent + ADDON_MENU_OFFSET;
             final MenuItemInfo menuItemInfo = info;
             ThreadUtils.postToUiThread(new Runnable() {
--- a/mobile/android/chrome/content/browser.js
+++ b/mobile/android/chrome/content/browser.js
@@ -2312,19 +2312,19 @@ var NativeWindow = {
     _callbacks: [],
     _menuId: 1,
     toolsMenuID: -1,
     add: function() {
       let options;
       if (arguments.length == 1) {
         options = arguments[0];
       } else if (arguments.length == 3) {
+          Log.w("Browser", "This menu addon API has been deprecated. Instead, use the options object API.");
           options = {
             name: arguments[0],
-            icon: arguments[1],
             callback: arguments[2]
           };
       } else {
          throw "Incorrect number of parameters";
       }
 
       options.type = "Menu:Add";
       options.id = this._menuId;
@@ -5837,95 +5837,124 @@ var HealthReportStatusListener = {
           },
         });
       }.bind(this));
   },
 };
 
 var XPInstallObserver = {
   init: function() {
+    Services.obs.addObserver(this, "addon-install-origin-blocked", false);
+    Services.obs.addObserver(this, "addon-install-disabled", false);
     Services.obs.addObserver(this, "addon-install-blocked", false);
     Services.obs.addObserver(this, "addon-install-started", false);
     Services.obs.addObserver(this, "xpi-signature-changed", false);
     Services.obs.addObserver(this, "browser-delayed-startup-finished", false);
 
     AddonManager.addInstallListener(this);
   },
 
   observe: function(aSubject, aTopic, aData) {
+    let installInfo = aSubject.QueryInterface(Ci.amIWebInstallInfo);
+    let tab = BrowserApp.getTabForBrowser(installInfo.browser);
+    let strings = Strings.browser;
+
+    let host = null;
+    if (installInfo.originatingURI) {
+      host = installInfo.originatingURI.host;
+    }
+
+    let brandShortName = Strings.brand.GetStringFromName("brandShortName");
+
     switch (aTopic) {
       case "addon-install-started":
-        NativeWindow.toast.show(Strings.browser.GetStringFromName("alertAddonsDownloading"), "short");
+        NativeWindow.toast.show(strings.GetStringFromName("alertAddonsDownloading"), "short");
         break;
-      case "addon-install-blocked": {
-        let installInfo = aSubject.QueryInterface(Ci.amIWebInstallInfo);
-        let tab = BrowserApp.getTabForBrowser(installInfo.browser);
+      case "addon-install-disabled": {
         if (!tab)
           return;
 
-        let host = null;
-        if (installInfo.originatingURI) {
-          host = installInfo.originatingURI.host;
-        }
-
-        let brandShortName = Strings.brand.GetStringFromName("brandShortName");
-        let notificationName, buttons, message;
-        let strings = Strings.browser;
         let enabled = true;
         try {
           enabled = Services.prefs.getBoolPref("xpinstall.enabled");
-        }
-        catch (e) {}
-
+        } catch (e) {}
+
+        let buttons, message, callback;
         if (!enabled) {
-          notificationName = "xpinstall-disabled";
-          if (Services.prefs.prefIsLocked("xpinstall.enabled")) {
-            message = strings.GetStringFromName("xpinstallDisabledMessageLocked");
-            buttons = [];
-          } else {
-            message = strings.formatStringFromName("xpinstallDisabledMessage2", [brandShortName, host], 2);
-            buttons = [{
-              label: strings.GetStringFromName("xpinstallDisabledButton"),
-              callback: function editPrefs() {
-                Services.prefs.setBoolPref("xpinstall.enabled", true);
-                return false;
-              }
-            }];
+          message = strings.GetStringFromName("xpinstallDisabledMessageLocked");
+          buttons = [strings.GetStringFromName("unsignedAddonsDisabled.dismiss")];
+          callback: (data) => {};
+        } else {
+          message = strings.formatStringFromName("xpinstallDisabledMessage2", [brandShortName, host], 2);
+          buttons = [
+              strings.GetStringFromName("xpinstallDisabledButton"),
+              strings.GetStringFromName("unsignedAddonsDisabled.dismiss")
+          ];
+          callback: (data) => {
+            if (data.button === 1) {
+              Services.prefs.setBoolPref("xpinstall.enabled", true)
+            }
+          };
+        }
+
+        new Prompt({
+          title: Strings.browser.GetStringFromName("addonError.titleError"),
+          message: message,
+          buttons: buttons
+        }).show(callback);
+        break;
+      }
+      case "addon-install-blocked": {
+        if (!tab)
+          return;
+
+        let message;
+        if (host) {
+          // We have a host which asked for the install.
+          message = strings.formatStringFromName("xpinstallPromptWarning2", [brandShortName, host], 2);
+        } else {
+          // Without a host we address the add-on as the initiator of the install.
+          let addon = null;
+          if (installInfo.installs.length > 0) {
+            addon = installInfo.installs[0].name;
           }
-        } else {
-          notificationName = "xpinstall";
-          if (host) {
-            // We have a host which asked for the install.
-            message = strings.formatStringFromName("xpinstallPromptWarning2", [brandShortName, host], 2);
+          if (addon) {
+            // We have an addon name, show the regular message.
+            message = strings.formatStringFromName("xpinstallPromptWarningLocal", [brandShortName, addon], 2);
           } else {
-            // Without a host we address the add-on as the initiator of the install.
-            let addon = null;
-            if (installInfo.installs.length > 0) {
-              addon = installInfo.installs[0].name;
-            }
-            if (addon) {
-              // We have an addon name, show the regular message.
-              message = strings.formatStringFromName("xpinstallPromptWarningLocal", [brandShortName, addon], 2);
-            } else {
-              // We don't have an addon name, show an alternative message.
-              message = strings.formatStringFromName("xpinstallPromptWarningDirect", [brandShortName], 1);
-            }
+            // We don't have an addon name, show an alternative message.
+            message = strings.formatStringFromName("xpinstallPromptWarningDirect", [brandShortName], 1);
           }
-
-          buttons = [{
-            label: strings.GetStringFromName("xpinstallPromptAllowButton"),
-            callback: function() {
-              // Kick off the install
-              installInfo.install();
-              return false;
-            },
-            positive: true
-          }];
         }
-        NativeWindow.doorhanger.show(message, aTopic, buttons, tab.id);
+
+        let buttons = [
+            strings.GetStringFromName("xpinstallPromptAllowButton"),
+            strings.GetStringFromName("unsignedAddonsDisabled.dismiss")
+        ];
+        new Prompt({
+          title: Strings.browser.GetStringFromName("addonError.titleBlocked"),
+          message: message,
+          buttons: buttons
+        }).show((data) => {
+          if (data.button === 0) {
+            // Kick off the install
+            installInfo.install();
+          }
+        });
+        break;
+      }
+      case "addon-install-origin-blocked": {
+        if (!tab)
+          return;
+
+        new Prompt({
+          title: Strings.browser.GetStringFromName("addonError.titleBlocked"),
+          message: strings.formatStringFromName("xpinstallPromptWarningDirect", [brandShortName], 1),
+          buttons: [strings.GetStringFromName("unsignedAddonsDisabled.dismiss")]
+        }).show((data) => {});
         break;
       }
       case "xpi-signature-changed": {
         if (JSON.parse(aData).disabled.length) {
           this._notifyUnsignedAddonsDisabled();
         }
         break;
       }
--- a/toolkit/components/alerts/resources/content/alert.js
+++ b/toolkit/components/alerts/resources/content/alert.js
@@ -39,62 +39,70 @@ function prefillAlertInfo() {
     case 11: {
       if (window.arguments[10]) {
         let alertBox = document.getElementById("alertBox");
         alertBox.setAttribute("hasOrigin", true);
 
         let hostPort = window.arguments[10];
         const ALERT_BUNDLE = Services.strings.createBundle(
           "chrome://alerts/locale/alert.properties");
+        const BRAND_BUNDLE = Services.strings.createBundle(
+          "chrome://branding/locale/brand.properties");
+        const BRAND_NAME = BRAND_BUNDLE.GetStringFromName("brandShortName");
         let label = document.getElementById("alertSourceLabel");
         label.setAttribute("value",
           ALERT_BUNDLE.formatStringFromName("source.label",
                                             [hostPort],
                                             1));
+        let doNotDisturbMenuItem = document.getElementById("doNotDisturbMenuItem");
+        doNotDisturbMenuItem.setAttribute("label",
+          ALERT_BUNDLE.formatStringFromName("doNotDisturb.label",
+                                            [BRAND_NAME],
+                                            1));
         let disableForOrigin = document.getElementById("disableForOriginMenuItem");
         disableForOrigin.setAttribute("label",
           ALERT_BUNDLE.formatStringFromName("webActions.disableForOrigin.label",
                                             [hostPort],
                                             1));
       }
     }
     case 10:
       gAlertListener = window.arguments[9];
     case 9:
       gReplacedWindow = window.arguments[8];
     case 8:
       if (window.arguments[7]) {
-        document.getElementById('alertTitleLabel').setAttribute('lang', window.arguments[7]);
-        document.getElementById('alertTextLabel').setAttribute('lang', window.arguments[7]);
+        document.getElementById("alertTitleLabel").setAttribute("lang", window.arguments[7]);
+        document.getElementById("alertTextLabel").setAttribute("lang", window.arguments[7]);
       }
     case 7:
       if (window.arguments[6]) {
-        document.getElementById('alertNotification').style.direction = window.arguments[6];
+        document.getElementById("alertNotification").style.direction = window.arguments[6];
       }
     case 6:
       gOrigin = window.arguments[5];
     case 5:
       gAlertCookie = window.arguments[4];
     case 4:
       gAlertTextClickable = window.arguments[3];
       if (gAlertTextClickable) {
-        document.getElementById('alertNotification').setAttribute('clickable', true);
-        document.getElementById('alertTextLabel').setAttribute('clickable', true);
+        document.getElementById("alertNotification").setAttribute("clickable", true);
+        document.getElementById("alertTextLabel").setAttribute("clickable", true);
       }
     case 3:
       if (window.arguments[2]) {
-        document.getElementById('alertBox').setAttribute('hasBodyText', true);
-        document.getElementById('alertTextLabel').textContent = window.arguments[2];
+        document.getElementById("alertBox").setAttribute("hasBodyText", true);
+        document.getElementById("alertTextLabel").textContent = window.arguments[2];
       }
     case 2:
-      document.getElementById('alertTitleLabel').textContent = window.arguments[1];
+      document.getElementById("alertTitleLabel").textContent = window.arguments[1];
     case 1:
       if (window.arguments[0]) {
-        document.getElementById('alertBox').setAttribute('hasImage', true);
-        document.getElementById('alertImage').setAttribute('src', window.arguments[0]);
+        document.getElementById("alertBox").setAttribute("hasImage", true);
+        document.getElementById("alertImage").setAttribute("src", window.arguments[0]);
       }
     case 0:
       break;
   }
 }
 
 function onAlertLoad() {
   const ALERT_DURATION_IMMEDIATE = 12000;
@@ -137,17 +145,17 @@ function onAlertLoad() {
   }
 }
 
 function moveWindowToReplace(aReplacedAlert) {
   let heightDelta = window.outerHeight - aReplacedAlert.outerHeight;
 
   // Move windows that come after the replaced alert if the height is different.
   if (heightDelta != 0) {
-    let windows = Services.wm.getEnumerator('alert:alert');
+    let windows = Services.wm.getEnumerator("alert:alert");
     while (windows.hasMoreElements()) {
       let alertWindow = windows.getNext();
       // boolean to determine if the alert window is after the replaced alert.
       let alertIsAfter = gOrigin & NS_ALERT_TOP ?
                          alertWindow.screenY > aReplacedAlert.screenY :
                          aReplacedAlert.screenY > alertWindow.screenY;
       if (alertIsAfter) {
         // The new Y position of the window.
@@ -167,17 +175,17 @@ function moveWindowToReplace(aReplacedAl
 function moveWindowToEnd() {
   // Determine position
   let x = gOrigin & NS_ALERT_LEFT ? screen.availLeft :
           screen.availLeft + screen.availWidth - window.outerWidth;
   let y = gOrigin & NS_ALERT_TOP ? screen.availTop :
           screen.availTop + screen.availHeight - window.outerHeight;
 
   // Position the window at the end of all alerts.
-  let windows = Services.wm.getEnumerator('alert:alert');
+  let windows = Services.wm.getEnumerator("alert:alert");
   while (windows.hasMoreElements()) {
     let alertWindow = windows.getNext();
     if (alertWindow != window) {
       if (gOrigin & NS_ALERT_TOP) {
         y = Math.max(y, alertWindow.screenY + alertWindow.outerHeight);
       } else {
         y = Math.min(y, alertWindow.screenY - window.outerHeight);
       }
@@ -190,17 +198,17 @@ function moveWindowToEnd() {
 
   window.moveTo(x, y);
 }
 
 function onAlertBeforeUnload() {
   if (!gIsReplaced) {
     // Move other alert windows to fill the gap left by closing alert.
     let heightDelta = window.outerHeight + WINDOW_MARGIN;
-    let windows = Services.wm.getEnumerator('alert:alert');
+    let windows = Services.wm.getEnumerator("alert:alert");
     while (windows.hasMoreElements()) {
       let alertWindow = windows.getNext();
       if (alertWindow != window) {
         if (gOrigin & NS_ALERT_TOP) {
           if (alertWindow.screenY > window.screenY) {
             alertWindow.moveTo(alertWindow.screenX, alertWindow.screenY - heightDelta);
           }
         } else {
@@ -226,16 +234,24 @@ function onAlertClick() {
   if (alertBox.getAttribute("animate") == "true") {
     // Closed when the animation ends.
     alertBox.setAttribute("clicked", "true");
   } else {
     window.close();
   }
 }
 
+function doNotDisturb() {
+  const alertService = Cc["@mozilla.org/alerts-service;1"]
+                         .getService(Ci.nsIAlertsService)
+                         .QueryInterface(Ci.nsIAlertsDoNotDisturb);
+  alertService.manualDoNotDisturb = true;
+  onAlertClose();
+}
+
 function disableForOrigin() {
   gAlertListener.observe(null, "alertdisablecallback", gAlertCookie);
   onAlertClose();
 }
 
 function onAlertClose() {
   let alertBox = document.getElementById("alertBox");
   if (alertBox.getAttribute("animate") == "true") {
--- a/toolkit/components/alerts/resources/content/alert.xul
+++ b/toolkit/components/alerts/resources/content/alert.xul
@@ -41,16 +41,19 @@
       <vbox id="alertTextBox" class="alertTextBox">
         <label id="alertTextLabel" class="alertText plain"/>
         <spacer flex="1"/>
         <box id="alertFooter">
           <label id="alertSourceLabel" class="alertSource plain"/>
           <button type="menu" id="alertSettings" tooltiptext="&settings.label;"
                   onclick="event.stopPropagation();">
             <menupopup position="after_end">
+              <menuitem id="doNotDisturbMenuItem"
+                        oncommand="doNotDisturb();"/>
+              <menuseparator/>
               <menuitem id="disableForOriginMenuItem"
                         oncommand="disableForOrigin();"/>
             </menupopup>
           </button>
         </box>
       </vbox>
     </box>
   </vbox>
--- a/toolkit/locales/en-US/chrome/alerts/alert.properties
+++ b/toolkit/locales/en-US/chrome/alerts/alert.properties
@@ -12,8 +12,12 @@ actionButton.label = …
 # with the hostname origin of the notification.
 webActions.disableForOrigin.label = Disable notifications from %S
 
 # LOCALIZATION NOTE(source.label): Used to show the URL of the site that
 # sent the notification (e.g., "via mozilla.org"). "%1$S" is the source host
 # and port.
 source.label=via %1$S
 webActions.settings.label = Notification settings
+
+# LOCALIZATION NOTE(doNotDisturb.label): %S is replaced with the
+# brandShortName of the application.
+doNotDisturb.label = Do not disturb me until I restart %S