bug 725408 - implement WebappRT launcher/shell; r=bsmedberg
authorMyk Melez <myk@mozilla.org>
Tue, 17 Apr 2012 07:11:53 -0700
changeset 95171 ef55c163a23a0fb7dc361791a53b6149d69f3ca2
parent 95170 2d345cf4616b132e576d3e82cbe8b5a6b537d6c0
child 95172 76291134483795ce68ef05ea9a89d03bf0c0eaca
push id886
push userlsblakk@mozilla.com
push dateMon, 04 Jun 2012 19:57:52 +0000
treeherdermozilla-beta@bbd8d5efd6d1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbsmedberg
bugs725408
milestone14.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
bug 725408 - implement WebappRT launcher/shell; r=bsmedberg
allmakefiles.sh
browser/build.mk
browser/components/BrowserComponents.manifest
browser/components/feeds/src/BrowserFeeds.manifest
browser/components/privatebrowsing/src/nsPrivateBrowsingService.manifest
browser/components/sessionstore/src/nsSessionStore.manifest
browser/installer/package-manifest.in
browser/locales/en-US/webapprt/webapp.dtd
browser/locales/en-US/webapprt/webapp.properties
browser/locales/jar.mn
config/autoconf.mk.in
configure.in
dom/base/Webapps.jsm
modules/libpref/src/Preferences.cpp
services/sync/SyncComponents.manifest
toolkit/mozapps/installer/windows/nsis/makensis.mk
toolkit/mozapps/installer/windows/nsis/preprocess-locale.py
toolkit/xre/nsXREDirProvider.cpp
webapprt/Makefile.in
webapprt/WebappRT.jsm
webapprt/WebappRTCommandLineHandler.js
webapprt/WebappRTComponents.manifest
webapprt/WebappRTDirectoryProvider.js
webapprt/content/webapp.js
webapprt/content/webapp.xul
webapprt/jar.mn
webapprt/locales/en-US/webapp-uninstaller/webapp-uninstaller.properties
webapprt/mac/Makefile.in
webapprt/mac/webapprt.mm
webapprt/prefs.js
webapprt/webapprt.ini.in
webapprt/win/Makefile.in
webapprt/win/webapp-uninstaller.nsi.in
webapprt/win/webapprt.cpp
webapprt/win/webapprt.exe.manifest
webapprt/win/webapprt.rc
xpcom/glue/FileUtils.h
xpfe/appshell/src/nsAppShellService.cpp
--- a/allmakefiles.sh
+++ b/allmakefiles.sh
@@ -35,16 +35,22 @@ config/autoconf.mk
 config/nspr/Makefile
 config/doxygen.cfg
 config/expandlibs_config.py
 mfbt/Makefile
 probes/Makefile
 extensions/Makefile
 "
 
+if [ "$MOZ_WEBAPP_RUNTIME" ]; then
+  add_makefiles "
+webapprt/Makefile
+  "
+fi
+
 if [ ! "$LIBXUL_SDK" ]; then
   if [ "$STLPORT_SOURCES" ]; then
     add_makefiles "
       build/stlport/Makefile
       build/stlport/stl/config/_android.h
     "
   fi
   add_makefiles "
--- a/browser/build.mk
+++ b/browser/build.mk
@@ -46,16 +46,20 @@ tier_app_dirs += extensions
 endif
 
 tier_app_dirs += $(MOZ_BRANDING_DIRECTORY)
 
 ifdef MOZ_SERVICES_SYNC
 tier_app_dirs += services
 endif
 
+ifdef MOZ_WEBAPP_RUNTIME
+tier_app_dirs += webapprt
+endif
+
 tier_app_dirs += browser
 # Never add other tier_app_dirs after browser. They won't get packaged
 # properly on mac.
 
 ################################################
 # Parallel build on Windows with GNU make check
 
 default::
--- a/browser/components/BrowserComponents.manifest
+++ b/browser/components/BrowserComponents.manifest
@@ -19,13 +19,27 @@ contract @mozilla.org/uriloader/content-
 contract @mozilla.org/uriloader/content-handler;1?type=image/x-icon {5d0ce354-df01-421a-83fb-7ead0990c24e} application={ec8030f7-c20a-464f-9b0e-13a3a9e97384}
 contract @mozilla.org/uriloader/content-handler;1?type=image/vnd.microsoft.icon {5d0ce354-df01-421a-83fb-7ead0990c24e} application={ec8030f7-c20a-464f-9b0e-13a3a9e97384}
 contract @mozilla.org/uriloader/content-handler;1?type=application/http-index-format {5d0ce354-df01-421a-83fb-7ead0990c24e} application={ec8030f7-c20a-464f-9b0e-13a3a9e97384}
 category command-line-handler m-browser @mozilla.org/browser/clh;1 application={ec8030f7-c20a-464f-9b0e-13a3a9e97384}
 category command-line-handler x-default @mozilla.org/browser/final-clh;1 application={ec8030f7-c20a-464f-9b0e-13a3a9e97384}
 category command-line-validator b-browser @mozilla.org/browser/clh;1 application={ec8030f7-c20a-464f-9b0e-13a3a9e97384}
 
 # nsBrowserGlue.js
+
+# WebappRT doesn't need these instructions, and they don't necessarily work
+# with it, but it does use a GRE directory that the GRE shares with Firefox,
+# so in order to prevent the instructions from being processed for WebappRT,
+# we need to restrict them to the applications that depend on them, i.e.:
+#
+#   b2g:            {3c2e2abc-06d4-11e1-ac3b-374f68613e61}
+#   browser:        {ec8030f7-c20a-464f-9b0e-13a3a9e97384}
+#   mobile/android: {aa3c5121-dab2-40e2-81ca-7ea25febc110}
+#   mobile/xul:     {a23983c0-fd0e-11dc-95ff-0800200c9a66}
+#
+# In theory we should do this for all these instructions, but in practice it is
+# sufficient to do it for the app-startup one, and the file is simpler that way.
+
 component {eab9012e-5f74-4cbc-b2b5-a590235513cc} nsBrowserGlue.js
 contract @mozilla.org/browser/browserglue;1 {eab9012e-5f74-4cbc-b2b5-a590235513cc}
-category app-startup nsBrowserGlue service,@mozilla.org/browser/browserglue;1
+category app-startup nsBrowserGlue service,@mozilla.org/browser/browserglue;1 application={3c2e2abc-06d4-11e1-ac3b-374f68613e61} application={ec8030f7-c20a-464f-9b0e-13a3a9e97384} application={aa3c5121-dab2-40e2-81ca-7ea25febc110} application={a23983c0-fd0e-11dc-95ff-0800200c9a66}
 component {d8903bf6-68d5-4e97-bcd1-e4d3012f721a} nsBrowserGlue.js
 contract @mozilla.org/content-permission/prompt;1 {d8903bf6-68d5-4e97-bcd1-e4d3012f721a}
--- a/browser/components/feeds/src/BrowserFeeds.manifest
+++ b/browser/components/feeds/src/BrowserFeeds.manifest
@@ -1,16 +1,29 @@
+# WebappRT doesn't need these instructions, and they don't necessarily work
+# with it, but it does use a GRE directory that the GRE shares with Firefox,
+# so in order to prevent the instructions from being processed for WebappRT,
+# we need to restrict them to the applications that depend on them, i.e.:
+#
+#   b2g:            {3c2e2abc-06d4-11e1-ac3b-374f68613e61}
+#   browser:        {ec8030f7-c20a-464f-9b0e-13a3a9e97384}
+#   mobile/android: {aa3c5121-dab2-40e2-81ca-7ea25febc110}
+#   mobile/xul:     {a23983c0-fd0e-11dc-95ff-0800200c9a66}
+#
+# In theory we should do this for all these instructions, but in practice it is
+# sufficient to do it for the app-startup one, and the file is simpler that way.
+
 component {229fa115-9412-4d32-baf3-2fc407f76fb1} FeedConverter.js
 contract @mozilla.org/streamconv;1?from=application/vnd.mozilla.maybe.feed&to=*/* {229fa115-9412-4d32-baf3-2fc407f76fb1}
 contract @mozilla.org/streamconv;1?from=application/vnd.mozilla.maybe.video.feed&to=*/* {229fa115-9412-4d32-baf3-2fc407f76fb1}
 contract @mozilla.org/streamconv;1?from=application/vnd.mozilla.maybe.audio.feed&to=*/* {229fa115-9412-4d32-baf3-2fc407f76fb1}
 component {2376201c-bbc6-472f-9b62-7548040a61c6} FeedConverter.js
 contract @mozilla.org/browser/feeds/result-service;1 {2376201c-bbc6-472f-9b62-7548040a61c6}
 component {4f91ef2e-57ba-472e-ab7a-b4999e42d6c0} FeedConverter.js
 contract @mozilla.org/network/protocol;1?name=feed {4f91ef2e-57ba-472e-ab7a-b4999e42d6c0}
 component {1c31ed79-accd-4b94-b517-06e0c81999d5} FeedConverter.js
 contract @mozilla.org/network/protocol;1?name=pcast {1c31ed79-accd-4b94-b517-06e0c81999d5}
 component {49bb6593-3aff-4eb3-a068-2712c28bd58e} FeedWriter.js
 contract @mozilla.org/browser/feeds/result-writer;1 {49bb6593-3aff-4eb3-a068-2712c28bd58e}
 category JavaScript-global-constructor BrowserFeedWriter @mozilla.org/browser/feeds/result-writer;1
 component {792a7e82-06a0-437c-af63-b2d12e808acc} WebContentConverter.js
 contract @mozilla.org/embeddor.implemented/web-content-handler-registrar;1 {792a7e82-06a0-437c-af63-b2d12e808acc}
-category app-startup WebContentConverter service,@mozilla.org/embeddor.implemented/web-content-handler-registrar;1
\ No newline at end of file
+category app-startup WebContentConverter service,@mozilla.org/embeddor.implemented/web-content-handler-registrar;1 application={3c2e2abc-06d4-11e1-ac3b-374f68613e61} application={ec8030f7-c20a-464f-9b0e-13a3a9e97384} application={aa3c5121-dab2-40e2-81ca-7ea25febc110} application={a23983c0-fd0e-11dc-95ff-0800200c9a66}
--- a/browser/components/privatebrowsing/src/nsPrivateBrowsingService.manifest
+++ b/browser/components/privatebrowsing/src/nsPrivateBrowsingService.manifest
@@ -1,4 +1,17 @@
+# WebappRT doesn't need these instructions, and they don't necessarily work
+# with it, but it does use a GRE directory that the GRE shares with Firefox,
+# so in order to prevent the instructions from being processed for WebappRT,
+# we need to restrict them to the applications that depend on them, i.e.:
+#
+#   b2g:            {3c2e2abc-06d4-11e1-ac3b-374f68613e61}
+#   browser:        {ec8030f7-c20a-464f-9b0e-13a3a9e97384}
+#   mobile/android: {aa3c5121-dab2-40e2-81ca-7ea25febc110}
+#   mobile/xul:     {a23983c0-fd0e-11dc-95ff-0800200c9a66}
+#
+# In theory we should do this for all these instructions, but in practice it is
+# sufficient to do it for the app-startup one, and the file is simpler that way.
+
 component {c31f4883-839b-45f6-82ad-a6a9bc5ad599} nsPrivateBrowsingService.js
 contract @mozilla.org/privatebrowsing;1 {c31f4883-839b-45f6-82ad-a6a9bc5ad599}
 category command-line-handler m-privatebrowsing @mozilla.org/privatebrowsing;1
-category app-startup nsPrivateBrowsingService service,@mozilla.org/privatebrowsing;1
+category app-startup nsPrivateBrowsingService service,@mozilla.org/privatebrowsing;1 application={3c2e2abc-06d4-11e1-ac3b-374f68613e61} application={ec8030f7-c20a-464f-9b0e-13a3a9e97384} application={aa3c5121-dab2-40e2-81ca-7ea25febc110} application={a23983c0-fd0e-11dc-95ff-0800200c9a66}
--- a/browser/components/sessionstore/src/nsSessionStore.manifest
+++ b/browser/components/sessionstore/src/nsSessionStore.manifest
@@ -1,5 +1,18 @@
+# WebappRT doesn't need these instructions, and they don't necessarily work
+# with it, but it does use a GRE directory that the GRE shares with Firefox,
+# so in order to prevent the instructions from being processed for WebappRT,
+# we need to restrict them to the applications that depend on them, i.e.:
+#
+#   b2g:            {3c2e2abc-06d4-11e1-ac3b-374f68613e61}
+#   browser:        {ec8030f7-c20a-464f-9b0e-13a3a9e97384}
+#   mobile/android: {aa3c5121-dab2-40e2-81ca-7ea25febc110}
+#   mobile/xul:     {a23983c0-fd0e-11dc-95ff-0800200c9a66}
+#
+# In theory we should do this for all these instructions, but in practice it is
+# sufficient to do it for the app-startup one, and the file is simpler that way.
+
 component {5280606b-2510-4fe0-97ef-9b5a22eafe6b} nsSessionStore.js
 contract @mozilla.org/browser/sessionstore;1 {5280606b-2510-4fe0-97ef-9b5a22eafe6b}
 component {ec7a6c20-e081-11da-8ad9-0800200c9a66} nsSessionStartup.js
 contract @mozilla.org/browser/sessionstartup;1 {ec7a6c20-e081-11da-8ad9-0800200c9a66}
-category app-startup nsSessionStartup service,@mozilla.org/browser/sessionstartup;1
+category app-startup nsSessionStartup service,@mozilla.org/browser/sessionstartup;1 application={3c2e2abc-06d4-11e1-ac3b-374f68613e61} application={ec8030f7-c20a-464f-9b0e-13a3a9e97384} application={aa3c5121-dab2-40e2-81ca-7ea25febc110} application={a23983c0-fd0e-11dc-95ff-0800200c9a66}
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -498,16 +498,19 @@
 ; [Default Preferences]
 ; All the pref files must be part of base to prevent migration bugs
 @BINPATH@/@PREF_DIR@/firefox.js
 @BINPATH@/@PREF_DIR@/firefox-branding.js
 @BINPATH@/@PREF_DIR@/channel-prefs.js
 #ifdef MOZ_SERVICES_SYNC
 @BINPATH@/@PREF_DIR@/services-sync.js
 #endif
+#ifdef MOZ_WEBAPP_RUNTIME
+@BINPATH@/@PREF_DIR@/webapprt@mozilla.org/prefs.js
+#endif
 @BINPATH@/greprefs.js
 @BINPATH@/defaults/autoconfig/platform.js
 @BINPATH@/defaults/autoconfig/prefcalls.js
 @BINPATH@/defaults/profile/prefs.js
 
 ; [Layout Engine Resources]
 ; Style Sheets, Graphics and other Resources used by the layout engine. 
 @BINPATH@/res/EditorOverride.css
@@ -619,8 +622,19 @@ bin/components/@DLL_PREFIX@nkgnomevfs@DL
 #endif
 
 ; [OS/2]
 #ifdef XP_OS2
 @BINPATH@/MozSounds.cmd
 @BINPATH@/*.xqs
 @BINPATH@/components/*.xqs
 #endif
+
+#ifdef MOZ_WEBAPP_RUNTIME
+; [Webapp Runtime]
+@BINPATH@/webapprt-stub@BIN_SUFFIX@
+@BINPATH@/chrome/webapprt@JAREXT@
+@BINPATH@/chrome/webapprt.manifest
+@BINPATH@/components/WebappRTComponents.manifest
+@BINPATH@/components/WebappRTDirectoryProvider.js
+@BINPATH@/components/WebappRTCommandLineHandler.js
+@BINPATH@/webapprt.ini
+#endif
new file mode 100644
--- /dev/null
+++ b/browser/locales/en-US/webapprt/webapp.dtd
@@ -0,0 +1,49 @@
+<!-- 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/.  -->
+
+<!-- LOCALIZATION NOTE: These are localized strings for the webapp runtime,
+   - which loads a webapp in a separate process from Firefox.  Webapps loaded
+   - in this way have very little application chrome, but the runtime does
+   - provide them with some default functionality, like the standard OS
+   - menus/menuitems. -->
+
+<!ENTITY fileMenu.label                     "File">
+<!ENTITY fileMenu.accesskey                 "F">
+
+<!ENTITY quitApplicationCmdWin.label        "Exit">
+<!ENTITY quitApplicationCmdWin.accesskey    "x">
+<!ENTITY quitApplicationCmd.label           "Quit">
+<!ENTITY quitApplicationCmd.accesskey       "Q">
+<!-- On Mac, we create the Quit and Hide command labels dynamically,
+   - using properties in window.properties, in order to include the name
+   - of the webapp in the labels without creating a DTD file for it. -->
+<!ENTITY quitApplicationCmdMac.key          "Q">
+<!ENTITY hideThisAppCmdMac.key              "H">
+<!ENTITY hideOtherAppsCmdMac.label          "Hide Others">
+<!ENTITY hideOtherAppsCmdMac.key            "H">
+<!ENTITY showAllAppsCmdMac.label            "Show All">
+
+<!ENTITY editMenu.label                     "Edit">
+<!ENTITY editMenu.accesskey                 "E">
+<!ENTITY undoCmd.label                      "Undo">
+<!ENTITY undoCmd.key                        "Z">
+<!ENTITY undoCmd.accesskey                  "U">
+<!ENTITY redoCmd.label                      "Redo">
+<!ENTITY redoCmd.key                        "Y">
+<!ENTITY redoCmd.accesskey                  "R">
+<!ENTITY cutCmd.label                       "Cut">
+<!ENTITY cutCmd.key                         "X">
+<!ENTITY cutCmd.accesskey                   "t">
+<!ENTITY copyCmd.label                      "Copy">
+<!ENTITY copyCmd.key                        "C">
+<!ENTITY copyCmd.accesskey                  "C">
+<!ENTITY pasteCmd.label                     "Paste">
+<!ENTITY pasteCmd.key                       "V">
+<!ENTITY pasteCmd.accesskey                 "P">
+<!ENTITY deleteCmd.label                    "Delete">
+<!ENTITY deleteCmd.key                      "D">
+<!ENTITY deleteCmd.accesskey                "D">
+<!ENTITY selectAllCmd.label                 "Select All">
+<!ENTITY selectAllCmd.key                   "A">
+<!ENTITY selectAllCmd.accesskey             "A">
new file mode 100644
--- /dev/null
+++ b/browser/locales/en-US/webapprt/webapp.properties
@@ -0,0 +1,17 @@
+# 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/.
+
+# LOCALIZATION NOTE: These are localized strings for the webapp runtime,
+# which loads a webapp in a separate process from Firefox.  Webapps loaded
+# in this way have very little application chrome, but the runtime does
+# provide them with some default functionality, like the standard OS
+# menus/menuitems.
+
+# LOCALIZATION NOTE (quitApplicationCmdMac.label): %S will be replaced with
+# the name of the webapp.
+quitApplicationCmdMac.label=Quit %S
+
+# LOCALIZATION NOTE (hideApplicationCmdMac.label): %S will be replaced with
+# the name of the webapp.
+hideApplicationCmdMac.label=Hide %S
--- a/browser/locales/jar.mn
+++ b/browser/locales/jar.mn
@@ -105,8 +105,11 @@
 *   locale/browser/appstrings.properties       (%chrome/overrides/appstrings.properties)
 *   locale/browser/downloads/settingsChange.dtd  (%chrome/overrides/settingsChange.dtd)
 % override chrome://global/locale/netError.dtd chrome://browser/locale/netError.dtd
 % override chrome://global/locale/appstrings.properties chrome://browser/locale/appstrings.properties
 % override chrome://mozapps/locale/downloads/settingsChange.dtd chrome://browser/locale/downloads/settingsChange.dtd
 % locale testpilot @AB_CD@ %locale/feedback/
     locale/feedback/main.dtd                       (%feedback/main.dtd)
     locale/feedback/main.properties                (%feedback/main.properties)
+% locale webapprt @AB_CD@ %locale/webapprt/
+    locale/webapprt/webapp.dtd                     (%webapprt/webapp.dtd)
+    locale/webapprt/webapp.properties              (%webapprt/webapp.properties)
--- a/config/autoconf.mk.in
+++ b/config/autoconf.mk.in
@@ -675,16 +675,18 @@ WRAP_SYSTEM_INCLUDES = @WRAP_SYSTEM_INCL
 HAVE_ARM_SIMD = @HAVE_ARM_SIMD@
 HAVE_ARM_NEON = @HAVE_ARM_NEON@
 HAVE_GCC_ALIGN_ARG_POINTER = @HAVE_GCC_ALIGN_ARG_POINTER@
 
 MOZ_THEME_FASTSTRIPE = @MOZ_THEME_FASTSTRIPE@
 
 MOZ_SERVICES_SYNC = @MOZ_SERVICES_SYNC@
 
+MOZ_WEBAPP_RUNTIME = @MOZ_WEBAPP_RUNTIME@
+
 MOZ_OFFICIAL_BRANDING = @MOZ_OFFICIAL_BRANDING@
 
 HAVE_CLOCK_MONOTONIC = @HAVE_CLOCK_MONOTONIC@
 REALTIME_LIBS = @REALTIME_LIBS@
 
 MOZ_APP_COMPONENT_LIBS = @MOZ_APP_COMPONENT_LIBS@
 MOZ_APP_EXTRA_LIBS = @MOZ_APP_EXTRA_LIBS@
 
--- a/configure.in
+++ b/configure.in
@@ -4492,16 +4492,17 @@ dnl ====================================
 MOZ_ARG_HEADER(Application)
 
 ENABLE_TESTS=1
 ENABLE_SYSTEM_EXTENSION_DIRS=1
 MOZ_BRANDING_DIRECTORY=
 MOZ_OFFICIAL_BRANDING=
 MOZ_FEEDS=1
 MOZ_FLEXBOX=
+MOZ_WEBAPP_RUNTIME=
 MOZ_JSDEBUGGER=1
 MOZ_AUTH_EXTENSION=1
 MOZ_OGG=1
 MOZ_RAW=
 MOZ_SYDNEYAUDIO=
 MOZ_CUBEB=
 MOZ_VORBIS=
 MOZ_TREMOR=
@@ -4554,26 +4555,28 @@ BUILD_CTYPES=1
 MOZ_USE_NATIVE_POPUP_WINDOWS=
 MOZ_ANDROID_HISTORY=
 MOZ_WEBSMS_BACKEND=
 MOZ_GRAPHITE=1
 
 case "${target}" in
 *darwin*)
     ACCESSIBILITY=
+    MOZ_WEBAPP_RUNTIME=1
     ;;
 *)
     ACCESSIBILITY=1
     ;;
 esac
 
 case "$target_os" in
     mingw*)
         NS_ENABLE_TSF=1
         AC_DEFINE(NS_ENABLE_TSF)
+        MOZ_WEBAPP_RUNTIME=1
         ;;
 esac
 
 case "${target}" in
     *-android*|*-linuxandroid*)
         if test "$CPU_ARCH" = "arm" ; then
           USE_ARM_KUSER=1
         fi
@@ -6270,16 +6273,54 @@ if test -n "$MOZ_TREE_FREETYPE"; then
    CAIRO_FT_LIBS='$(call EXPAND_LIBNAME_PATH,freetype,$(DEPTH)/modules/freetype2/.libs)'
    AC_DEFINE(HAVE_FT_BITMAP_SIZE_Y_PPEM)
    AC_DEFINE(HAVE_FT_GLYPHSLOT_EMBOLDEN)
    AC_DEFINE(HAVE_FT_LOAD_SFNT_TABLE)
    AC_SUBST(CAIRO_FT_CFLAGS)
 fi
 
 dnl ========================================================
+dnl Web App Runtime
+dnl ========================================================
+MOZ_ARG_DISABLE_BOOL(webapp-runtime,
+[  --disable-webapp-runtime  Disable Web App Runtime],
+    MOZ_WEBAPP_RUNTIME=,
+    MOZ_WEBAPP_RUNTIME=1)
+if test -n "$MOZ_WEBAPP_RUNTIME" -a "$OS_ARCH" = "WINNT"; then
+    # Disable Web App Runtime for Windows builds that use the new toolkit if the
+    # required major version and minimum minor version of Unicode NSIS isn't in
+    # the path.
+    REQ_NSIS_MAJOR_VER=2
+    MIN_NSIS_MINOR_VER=33
+    MOZ_PATH_PROGS(MAKENSISU, $MAKENSISU makensisu-2.46 makensisu makensis)
+    if test -z "$MAKENSISU" -o "$MAKENSISU" = ":"; then
+        AC_MSG_ERROR([To build the Web App Runtime you must have the latest MozillaBuild or Unicode NSIS with a major version of $REQ_NSIS_MAJOR_VER and a minimum minor version of $MIN_NSIS_MINOR_VER in your path. To build without the Web App Runtime reconfigure using --disable-webapp-runtime.])
+    fi
+    changequote(,)
+    MAKENSISU_VER=`"$MAKENSISU" -version 2>/dev/null | sed -e '/-Unicode/!s/.*//g' -e 's/^v\([0-9]\+\.[0-9]\+\)\-Unicode$/\1/g'`
+    changequote([,])
+    if test ! "$MAKENSISU_VER" = ""; then
+        MAKENSISU_MAJOR_VER=`echo $MAKENSISU_VER | $AWK -F\. '{ print $1 }'`
+        MAKENSISU_MINOR_VER=`echo $MAKENSISU_VER | $AWK -F\. '{ print $2 }'`
+    fi
+    AC_MSG_CHECKING([for Unicode NSIS with major version == $REQ_NSIS_MAJOR_VER and minor version >= $MIN_NSIS_MINOR_VER])
+    if test "$MAKENSISU_VER" = "" ||
+       test ! "$MAKENSISU_MAJOR_VER" = "$REQ_NSIS_MAJOR_VER" -o \
+            ! "$MAKENSISU_MINOR_VER" -ge $MIN_NSIS_MINOR_VER; then
+        AC_MSG_RESULT([no])
+        AC_MSG_ERROR([To build the Web App Runtime you must have the latest MozillaBuild or Unicode NSIS with a major version of $REQ_NSIS_MAJOR_VER and a minimum minor version of $MIN_NSIS_MINOR_VER in your path. To build without the Web App Runtime reconfigure using --disable-webapp-runtime.])
+    fi
+    AC_MSG_RESULT([yes])
+fi
+AC_SUBST(MOZ_WEBAPP_RUNTIME)
+if test "$MOZ_WEBAPP_RUNTIME"; then
+    AC_DEFINE(MOZ_WEBAPP_RUNTIME)
+fi
+
+dnl ========================================================
 dnl Installer
 dnl ========================================================
 dnl Abort Windows build if the required major version and
 dnl minimum minor version of Unicode NSIS isn't in the path
 dnl (unless in case of cross compiling, for which Unicode
 dnl is not yet sufficient).
 if test "$OS_ARCH" = "WINNT"; then
     REQ_NSIS_MAJOR_VER=2
--- a/dom/base/Webapps.jsm
+++ b/dom/base/Webapps.jsm
@@ -7,29 +7,34 @@ const Cc = Components.classes;
 const Ci = Components.interfaces;
 
 let EXPORTED_SYMBOLS = ["DOMApplicationRegistry", "DOMApplicationManifest"];
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/FileUtils.jsm");
 
+const WEBAPP_RUNTIME = Services.appinfo.ID == "webapprt@mozilla.org";
+
 XPCOMUtils.defineLazyGetter(this, "NetUtil", function() {
   Cu.import("resource://gre/modules/NetUtil.jsm");
   return NetUtil;
 });
 
 XPCOMUtils.defineLazyGetter(this, "ppmm", function() {
   return Cc["@mozilla.org/parentprocessmessagemanager;1"].getService(Ci.nsIFrameMessageManager);
 });
 
 #ifdef MOZ_WIDGET_GONK
   const DIRECTORY_NAME = "webappsDir";
 #else
-  const DIRECTORY_NAME = "ProfD";
+  // If we're executing in the context of the webapp runtime, the data files
+  // are in a different directory (currently the Firefox profile that installed
+  // the webapp); otherwise, they're in the current profile.
+  const DIRECTORY_NAME = WEBAPP_RUNTIME ? "WebappRegD" : "ProfD";
 #endif
 
 let DOMApplicationRegistry = {
   appsFile: null,
   webapps: { },
 
   init: function() {
     this.messages = ["Webapps:Install", "Webapps:Uninstall",
--- a/modules/libpref/src/Preferences.cpp
+++ b/modules/libpref/src/Preferences.cpp
@@ -38,16 +38,17 @@
  * ***** END LICENSE BLOCK ***** */
 
 #include "mozilla/dom/ContentChild.h"
 
 #include "mozilla/Util.h"
 #include "mozilla/HashFunctions.h"
 
 #include "nsXULAppAPI.h"
+#include "nsIXULAppInfo.h"
 
 #include "mozilla/Preferences.h"
 #include "nsAppDirectoryServiceDefs.h"
 #include "nsDirectoryServiceDefs.h"
 #include "nsICategoryManager.h"
 #include "nsCategoryManagerUtils.h"
 #include "nsNetUtil.h"
 #include "nsIFile.h"
@@ -79,16 +80,18 @@
 #include "nsRefPtrHashtable.h"
 
 namespace mozilla {
 
 // Definitions
 #define INITIAL_PREF_FILES 10
 static NS_DEFINE_CID(kZipReaderCID, NS_ZIPREADER_CID);
 
+#define WEBAPPRT_APPID "webapprt@mozilla.org"
+
 // Prototypes
 static nsresult openPrefFile(nsIFile* aFile);
 static nsresult pref_InitInitialObjects(void);
 static nsresult pref_LoadPrefsInDirList(const char *listId);
 static nsresult ReadExtensionPrefs(nsIFile *aFile);
 
 Preferences* Preferences::sPreferences = nsnull;
 nsIPrefBranch* Preferences::sRootBranch = nsnull;
@@ -1010,16 +1013,24 @@ static nsresult pref_InitInitialObjects(
   // to have the same behaviour as $app != $gre, where this is required as
   // a supported location for GRE preferences.
   //
   // When $app != $gre, we additionally load, in omni.jar case:
   // - jar:$app/omni.jar!/defaults/preferences/*.js
   // - $app/defaults/preferences/*.js
   // and in non omni.jar case:
   // - $app/defaults/preferences/*.js
+  //
+  // When we're running WebappRT (i.e. $app == WEBAPPRT_APPID), in omni.jar
+  // case, we also load:
+  // - jar:$gre/omni.jar!/defaults/pref/$WEBAPPRT_APPID/*.js
+  // This allows WebappRT-specific prefs to override those of another app
+  // with whom it shares an app dir (i.e. Firefox).
+  // (A $WEBAPPRT_APPID dir is similarly hardcoded into the app pref dir list
+  // in nsXREDirProvider for when we're running WebappRT in non omni.jar case.)
 
   nsZipFind *findPtr;
   nsAutoPtr<nsZipFind> find;
   nsTArray<nsCString> prefEntries;
   const char *entryName;
   PRUint16 entryNameLen;
 
   nsRefPtr<nsZipArchive> jarReader = mozilla::Omnijar::GetReader(mozilla::Omnijar::GRE);
@@ -1033,16 +1044,40 @@ static nsresult pref_InitInitialObjects(
     NS_ENSURE_SUCCESS(rv, rv);
 
     find = findPtr;
     while (NS_SUCCEEDED(find->FindNext(&entryName, &entryNameLen))) {
       prefEntries.AppendElement(Substring(entryName, entryNameLen));
     }
 
     prefEntries.Sort();
+
+    // Load jar:$gre/omni.jar!/defaults/pref/$WEBAPPRT_APPID/*.js
+    // if we're running WebappRT.
+    nsCOMPtr<nsIXULAppInfo> appInfo =
+      do_GetService("@mozilla.org/xre/app-info;1", &rv);
+    if (NS_SUCCEEDED(rv)) {
+      nsCAutoString appID;
+      if (NS_SUCCEEDED(appInfo->GetID(appID)) && appID.Equals(WEBAPPRT_APPID)) {
+        nsCAutoString prefsPath("defaults/pref/");
+        prefsPath.Append(appID);
+        prefsPath.AppendLiteral("/*.js$");
+        rv = jarReader->FindInit(prefsPath.get(), &findPtr);
+        NS_ENSURE_SUCCESS(rv, rv);
+
+        // Make sure the files get read last by putting them at the beginning
+        // of the list of pref entries (which is processed backwards), so prefs
+        // in these app-specific files override those in non-app-specific ones.
+        find = findPtr;
+        while (NS_SUCCEEDED(find->FindNext(&entryName, &entryNameLen))) {
+          prefEntries.InsertElementAt(0, Substring(entryName, entryNameLen));
+        }
+      }
+    }
+
     for (PRUint32 i = prefEntries.Length(); i--; ) {
       rv = pref_ReadPrefFromJar(jarReader, prefEntries[i].get());
       if (NS_FAILED(rv))
         NS_WARNING("Error parsing preferences.");
     }
   } else {
     // Load $gre/greprefs.js
     nsCOMPtr<nsIFile> greprefsFile;
--- a/services/sync/SyncComponents.manifest
+++ b/services/sync/SyncComponents.manifest
@@ -1,10 +1,23 @@
+# WebappRT doesn't need these instructions, and they don't necessarily work
+# with it, but it does use a GRE directory that the GRE shares with Firefox,
+# so in order to prevent the instructions from being processed for WebappRT,
+# we need to restrict them to the applications that depend on them, i.e.:
+#
+#   b2g:            {3c2e2abc-06d4-11e1-ac3b-374f68613e61}
+#   browser:        {ec8030f7-c20a-464f-9b0e-13a3a9e97384}
+#   mobile/android: {aa3c5121-dab2-40e2-81ca-7ea25febc110}
+#   mobile/xul:     {a23983c0-fd0e-11dc-95ff-0800200c9a66}
+#
+# In theory we should do this for all these instructions, but in practice it is
+# sufficient to do it for the app-startup one, and the file is simpler that way.
+
 # Weave.js
 component {74b89fb0-f200-4ae8-a3ec-dd164117f6de} Weave.js
 contract @mozilla.org/weave/service;1 {74b89fb0-f200-4ae8-a3ec-dd164117f6de}
-category app-startup WeaveService service,@mozilla.org/weave/service;1
+category app-startup WeaveService service,@mozilla.org/weave/service;1 application={3c2e2abc-06d4-11e1-ac3b-374f68613e61} application={ec8030f7-c20a-464f-9b0e-13a3a9e97384} application={aa3c5121-dab2-40e2-81ca-7ea25febc110} application={a23983c0-fd0e-11dc-95ff-0800200c9a66}
 component {d28f8a0b-95da-48f4-b712-caf37097be41} Weave.js
 contract @mozilla.org/network/protocol/about;1?what=sync-log {d28f8a0b-95da-48f4-b712-caf37097be41}
 # Register resource aliases
 resource services-sync resource:///modules/services-sync/
 resource services-common resource:///modules/services-common/
 resource services-crypto resource:///modules/services-crypto/
--- a/toolkit/mozapps/installer/windows/nsis/makensis.mk
+++ b/toolkit/mozapps/installer/windows/nsis/makensis.mk
@@ -102,8 +102,17 @@ uninstaller::
 	cp $(CONFIG_DIR)/helper.exe $(DIST)/bin/uninstall
 
 ifdef MOZ_MAINTENANCE_SERVICE
 maintenanceservice_installer::
 	cd $(CONFIG_DIR) && $(MAKENSISU) maintenanceservice_installer.nsi
 	$(NSINSTALL) -D $(DIST)/bin/
 	cp $(CONFIG_DIR)/maintenanceservice_installer.exe $(DIST)/bin
 endif
+
+ifdef MOZ_WEBAPP_RUNTIME
+webapp_uninstaller::
+	$(INSTALL) $(addprefix $(MOZILLA_DIR)/toolkit/mozapps/installer/windows/nsis/,$(TOOLKIT_NSIS_FILES)) $(CONFIG_DIR)
+	$(INSTALL) $(addprefix $(MOZILLA_DIR)/other-licenses/nsis/Plugins/,$(CUSTOM_NSIS_PLUGINS)) $(CONFIG_DIR)
+	cd $(CONFIG_DIR) && $(MAKENSISU) webapp-uninstaller.nsi
+	$(NSINSTALL) -D $(DIST)/bin/
+	cp $(CONFIG_DIR)/webapp-uninstaller.exe $(DIST)/bin
+endif
--- a/toolkit/mozapps/installer/windows/nsis/preprocess-locale.py
+++ b/toolkit/mozapps/installer/windows/nsis/preprocess-locale.py
@@ -62,90 +62,127 @@ def get_locale_strings(path, prefix, mid
     return output
 
 def lookup(path, l10ndirs):
     for d in l10ndirs:
         if isfile(join(d, path)):
             return join(d, path)
     return join(l10ndirs[-1], path)
 
-def preprocess_locale_files(moz_dir, ab_cd, config_dir, l10ndirs):
+def preprocess_locale_files(config_dir, l10ndirs):
     """
     Preprocesses the installer localized properties files into the format
     required by NSIS and creates a basic NSIS nlf file.
 
     Parameters:
+    config_dir - the path to the destination directory
+    l10ndirs   - list of paths to search for installer locale files
+    """
+
+    # Create the main NSIS language file
+    fp = open_utf16le_file(join(config_dir, "overrideLocale.nsh"))
+    locale_strings = get_locale_strings(lookup("override.properties",
+                                               l10ndirs),
+                                        "LangString ^",
+                                        " 0 ",
+                                        False)
+    fp.write(unicode(locale_strings, "utf-8").encode("utf-16-le"))
+    fp.close()
+
+    # Create the Modern User Interface language file
+    fp = open_utf16le_file(join(config_dir, "baseLocale.nsh"))
+    fp.write((u""";NSIS Modern User Interface - Language File
+;Compatible with Modern UI 1.68
+;Language: baseLocale (0)
+!insertmacro MOZ_MUI_LANGUAGEFILE_BEGIN \"baseLocale\"
+!define MUI_LANGNAME \"baseLocale\"
+""").encode("utf-16-le"))
+    locale_strings = get_locale_strings(lookup("mui.properties", l10ndirs),
+                                        "!define ", " ", True)
+    fp.write(unicode(locale_strings, "utf-8").encode("utf-16-le"))
+    fp.write(u"!insertmacro MOZ_MUI_LANGUAGEFILE_END\n".encode("utf-16-le"))
+    fp.close()
+
+    # Create the custom language file for our custom strings
+    fp = open_utf16le_file(join(config_dir, "customLocale.nsh"))
+    locale_strings = get_locale_strings(lookup("custom.properties",
+                                               l10ndirs),
+                                        "LangString ",
+                                        " 0 ",
+                                        True)
+    fp.write(unicode(locale_strings, "utf-8").encode("utf-16-le"))
+    fp.close()
+
+def create_nlf_file(moz_dir, ab_cd, config_dir):
+    """
+    Create a basic NSIS nlf file.
+
+    Parameters:
     moz_dir    - the path to top source directory for the toolkit source
     ab_cd      - the locale code
     config_dir - the path to the destination directory
-    l10ndirs  - list of paths to search for installer locale files
     """
-
-    # Set the language ID to 0 to make this locale the default locale. An
-    # actual ID will need to be used to create a multi-language installer
-    # (e.g. for CD distributions, etc.).
-    lang_id = "0"
     rtl = "-"
 
     # Check whether the locale is right to left from locales.nsi.
-    fp = open(join(moz_dir, "toolkit/mozapps/installer/windows/nsis/locales.nsi"), "r")
+    fp = open(join(moz_dir,
+                   "toolkit/mozapps/installer/windows/nsis/locales.nsi"),
+              "r")
     for line in fp:
         line = line.strip()
         if line == "!define " + ab_cd + "_rtl":
             rtl = "RTL"
             break
 
     fp.close()
 
     # Create the main NSIS language file with RTL for right to left locales
     # along with the default codepage, font name, and font size represented
     # by the '-' character.
     fp = open_utf16le_file(join(config_dir, "baseLocale.nlf"))
     fp.write((u"""# Header, don't edit
 NLF v6
 # Start editing here
 # Language ID
-%s
+0
 # Font and size - dash (-) means default
 -
 -
 # Codepage - dash (-) means ANSI code page
 -
 # RTL - anything else than RTL means LTR
 %s
-""" % (lang_id, rtl)).encode("utf-16-le"))
+""" % rtl).encode("utf-16-le"))
     fp.close()
 
-    # Create the main NSIS language file
-    fp = open_utf16le_file(join(config_dir, "overrideLocale.nsh"))
-    locale_strings = get_locale_strings(lookup("override.properties", l10ndirs),
-                                        "LangString ^", " " + lang_id + " ", False)
+def preprocess_locale_file(config_dir,
+                           l10ndirs,
+                           properties_filename,
+                           output_filename):
+    """
+    Preprocesses a single localized properties file into the format
+    required by NSIS and creates a basic NSIS nlf file.
+
+    Parameters:
+    config_dir            - the path to the destination directory
+    l10ndirs              - list of paths to search for installer locale files
+    properties_filename   - the name of the properties file to search for
+    output_filename       - the output filename to write
+    """
+
+    # Create the custom language file for our custom strings
+    fp = open_utf16le_file(join(config_dir, output_filename))
+    locale_strings = get_locale_strings(lookup(properties_filename,
+                                               l10ndirs),
+                                        "LangString ",
+                                        " 0 ",
+                                        True)
     fp.write(unicode(locale_strings, "utf-8").encode("utf-16-le"))
     fp.close()
 
-    # Create the Modern User Interface language file
-    fp = open_utf16le_file(join(config_dir, "baseLocale.nsh"))
-    fp.write((u""";NSIS Modern User Interface - Language File
-;Compatible with Modern UI 1.68
-;Language: baseLocale (%s)
-!insertmacro MOZ_MUI_LANGUAGEFILE_BEGIN \"baseLocale\"
-!define MUI_LANGNAME \"baseLocale\"
-""" % (lang_id)).encode("utf-16-le"))
-    locale_strings = get_locale_strings(lookup("mui.properties", l10ndirs),
-                                        "!define ", " ", True)
-    fp.write(unicode(locale_strings, "utf-8").encode("utf-16-le"))
-    fp.write(u"!insertmacro MOZ_MUI_LANGUAGEFILE_END\n".encode("utf-16-le"))
-    fp.close()
-
-    # Create the custom language file for our custom strings
-    fp = open_utf16le_file(join(config_dir, "customLocale.nsh"))
-    locale_strings = get_locale_strings(lookup("custom.properties", l10ndirs),
-                        "LangString ", " " + lang_id + " ", True)
-    fp.write(unicode(locale_strings, "utf-8").encode("utf-16-le"))
-    fp.close()
 
 def convert_utf8_utf16le(in_file_path, out_file_path):
     """
     Converts a UTF-8 file to a new UTF-16LE file
 
     Arguments:
     in_file_path  - the path to the UTF-8 source file to convert
     out_file_path - the path to the UTF-16LE destination file to create
@@ -155,62 +192,169 @@ def convert_utf8_utf16le(in_file_path, o
     out_fp.write(unicode(in_fp.read(), "utf-8").encode("utf-16-le"))
     in_fp.close()
     out_fp.close()
 
 if __name__ == '__main__':
     usage = """usage: %prog command <args>
 
 Commands:
- --convert-utf8-utf16le - preprocesses installer locale properties files and
-                          creates a basic NSIS nlf file
- --preprocess-locale    - Preprocesses the installer localized properties files
-                          into the format required by NSIS and creates a basic
-                          NSIS nlf file.
+ --convert-utf8-utf16le     - Preprocesses installer locale properties files
+ --preprocess-locale        - Preprocesses the installer localized properties
+                              files into the format required by NSIS and
+                              creates a basic NSIS nlf file.
+ --preprocess-single-file   - Preprocesses a single properties file into the
+                              format required by NSIS
+ --create-nlf-file          - Creates a basic NSIS nlf file
 
 preprocess-locale.py --preprocess-locale <src> <locale> <code> <dest>
 
 Arguments:
  <src>   \tthe path to top source directory for the toolkit source
  <locale>\tthe path to the installer's locale files
  <code>  \tthe locale code
  <dest>  \tthe path to the destination directory
 
 
+preprocess-locale.py --preprocess-single-file <src>
+                                              <locale>
+                                              <dest>
+                                              <infile>
+                                              <outfile>
+
+Arguments:
+ <src>    \tthe path to top source directory for the toolkit source
+ <locale> \tthe path to the installer's locale files
+ <dest>   \tthe path to the destination directory
+ <infile> \tthe properties file to process
+ <outfile>\tthe nsh file to write
+
+
+preprocess-locale.py --create-nlf-file <src>
+                                       <code>
+                                       <dest>
+
+Arguments:
+ <src>    \tthe path to top source directory for the toolkit source
+ <code>   \tthe locale code
+ <dest>   \tthe path to the destination directory
+
+
 preprocess-locale.py --convert-utf8-utf16le <src> <dest>
 
 Arguments:
  <src> \tthe path to the UTF-8 source file to convert
  <dest>\tthe path to the UTF-16LE destination file to create
 """
+
+    preprocess_locale_args_help_string = """\
+Arguments to --preprocess-locale should be:
+   <src> <locale> <code> <dest>
+or
+   <src> <code> <dest> --l10n-dir <dir> [--l10n-dir <dir> ...]"""
+
+    preprocess_single_file_args_help_string = """\
+Arguments to --preprocess-single_file should be:
+   <src> <locale> <code> <dest> <infile> <outfile>
+or
+   <src> <locale> <code> <dest> <infile> <outfile>
+   --l10n-dir <dir> [--l10n-dir <dir>...]"""
+
+    create_nlf_args_help_string = """\
+Arguments to --create-nlf-file should be:
+   <src> <code> <dest>"""
+
     p = OptionParser(usage=usage)
     p.add_option("--preprocess-locale", action="store_true", default=False,
                  dest='preprocess')
+    p.add_option("--preprocess-single-file", action="store_true", default=False,
+                 dest='preprocessSingle')
+    p.add_option("--create-nlf-file", action="store_true", default=False,
+                 dest='createNlf')
     p.add_option("--l10n-dir", action="append", default=[],
                  dest="l10n_dirs",
                  help="Add directory to lookup for locale files")
     p.add_option("--convert-utf8-utf16le", action="store_true", default=False,
                  dest='convert')
 
     options, args = p.parse_args()
 
-    if ((not (options.preprocess or options.convert)) or
-        (options.preprocess and options.convert)):
-        p.error("You need to specify either --preprocess-locale or --convert-utf-utf16le")
+    foundOne = False
+    if (options.preprocess):
+        foundOne = True
+    if (options.convert):
+        if(foundOne):
+            p.error("More than one command specified")
+        else:
+            foundOne = True
+    if (options.preprocessSingle):
+        if(foundOne):
+            p.error("More than one command specified")
+        else:
+            foundOne = True
+    if (options.createNlf):
+        if(foundOne):
+            p.error("More than one command specified")
+        else:
+            foundOne = True
+
+    if (not foundOne):
+      p.error("No command specified")
 
     if options.preprocess:
         if len(args) not in (3,4):
-            p.error("--preprocess-locale needs all of <src> <locale> <code> <dest>")
+            p.error(preprocess_locale_args_help_string)
+
+        # Parse args
         pargs = args[:]
-        if len(args) == 4:
-            l10n_dirs = [args[1]]
+        moz_dir = pargs[0]
+        if len(pargs) == 4:
+            l10n_dirs = [pargs[1]]
             del pargs[1]
         else:
             if not options.l10n_dirs:
-                p.error("--preprocess-locale needs either <locale> or --l10ndir")
+                p.error(preprocess_locale_args_help_string)
             l10n_dirs = options.l10n_dirs
+        ab_cd = pargs[1]
+        config_dir = pargs[2]
+
+        # Create the output files
+        create_nlf_file(moz_dir, ab_cd, config_dir)
+        preprocess_locale_files(config_dir, l10n_dirs)
+    elif options.preprocessSingle:
+        if len(args) not in (4,5):
+            p.error(preprocess_single_file_args_help_string)
 
-        pargs.append(l10n_dirs)
-        preprocess_locale_files(*pargs)
-    else:
+        # Parse args
+        pargs = args[:]
+        moz_dir = pargs[0]
+        if len(pargs) == 5:
+            l10n_dirs = [pargs[1]]
+            del pargs[1]
+        else:
+            if not options.l10n_dirs:
+                p.error(preprocess_single_file_args_help_string)
+            l10n_dirs = options.l10n_dirs
+        config_dir = pargs[1]
+        in_file = pargs[2]
+        out_file = pargs[3]
+
+        # Create the output files
+        preprocess_locale_file(config_dir,
+                               l10n_dirs,
+                               in_file,
+                               out_file)
+    elif options.createNlf:
+        if len(args) != 3:
+            p.error(create_nlf_args_help_string)
+
+        # Parse args
+        pargs = args[:]
+        moz_dir = pargs[0]
+        ab_cd = pargs[1]
+        config_dir = pargs[2]
+
+        # Create the output files
+        create_nlf_file(moz_dir, ab_cd, config_dir)
+    elif options.convert:
         if len(args) != 2:
             p.error("--convert-utf8-utf16le needs both of <src> <dest>")
         convert_utf8_utf16le(*args)
--- a/toolkit/xre/nsXREDirProvider.cpp
+++ b/toolkit/xre/nsXREDirProvider.cpp
@@ -94,16 +94,18 @@
 #elif defined(XP_WIN) || defined(XP_OS2)
 #define APP_REGISTRY_NAME "registry.dat"
 #else
 #define APP_REGISTRY_NAME "appreg"
 #endif
 
 #define PREF_OVERRIDE_DIRNAME "preferences"
 
+#define WEBAPPRT_APPID "webapprt@mozilla.org"
+
 static already_AddRefed<nsILocalFile>
 CloneAndAppend(nsIFile* aFile, const char* name)
 {
   nsCOMPtr<nsIFile> file;
   aFile->Clone(getter_AddRefs(file));
   nsCOMPtr<nsILocalFile> lfile = do_QueryInterface(file);
   lfile->AppendNative(nsDependentCString(name));
   return lfile.forget();
@@ -420,34 +422,36 @@ nsXREDirProvider::GetFile(const char* aP
     }
   }
 
   NS_ADDREF(*aFile = file);
   return NS_OK;
 }
 
 static void
-LoadAppDirIntoArray(nsIFile* aXULAppDir,
-                    const char *const *aAppendList,
-                    nsCOMArray<nsIFile>& aDirectories)
+LoadDirIntoArray(nsIFile* dir,
+                 const char *const *aAppendList,
+                 nsCOMArray<nsIFile>& aDirectories)
 {
-  if (!aXULAppDir)
+  if (!dir)
     return;
 
   nsCOMPtr<nsIFile> subdir;
-  aXULAppDir->Clone(getter_AddRefs(subdir));
+  dir->Clone(getter_AddRefs(subdir));
   if (!subdir)
     return;
 
-  for (; *aAppendList; ++aAppendList)
-    subdir->AppendNative(nsDependentCString(*aAppendList));
+  for (const char *const *a = aAppendList; *a; ++a) {
+    subdir->AppendNative(nsDependentCString(*a));
+  }
 
   bool exists;
-  if (NS_SUCCEEDED(subdir->Exists(&exists)) && exists)
+  if (NS_SUCCEEDED(subdir->Exists(&exists)) && exists) {
     aDirectories.AppendObject(subdir);
+  }
 }
 
 static void
 LoadDirsIntoArray(nsCOMArray<nsIFile>& aSourceDirs,
                   const char *const* aAppendList,
                   nsCOMArray<nsIFile>& aDirectories)
 {
   nsCOMPtr<nsIFile> appended;
@@ -456,22 +460,20 @@ LoadDirsIntoArray(nsCOMArray<nsIFile>& a
   for (PRInt32 i = 0; i < aSourceDirs.Count(); ++i) {
     aSourceDirs[i]->Clone(getter_AddRefs(appended));
     if (!appended)
       continue;
 
     nsCAutoString leaf;
     appended->GetNativeLeafName(leaf);
     if (!Substring(leaf, leaf.Length() - 4).Equals(NS_LITERAL_CSTRING(".xpi"))) {
-      for (const char *const *a = aAppendList; *a; ++a)
-        appended->AppendNative(nsDependentCString(*a));
+      LoadDirIntoArray(appended,
+                       aAppendList,
+                       aDirectories);
     }
-
-    if (NS_SUCCEEDED(appended->Exists(&exists)) && exists)
-      aDirectories.AppendObject(appended);
   }
 }
 
 NS_IMETHODIMP
 nsXREDirProvider::GetFiles(const char* aProperty, nsISimpleEnumerator** aResult)
 {
   nsresult rv;
 
@@ -637,20 +639,27 @@ nsXREDirProvider::GetFilesInternal(const
     LoadDirsIntoArray(mExtensionDirectories,
                       kAppendNothing, directories);
 
     rv = NS_NewArrayEnumerator(aResult, directories);
   }
   else if (!strcmp(aProperty, NS_APP_PREFS_DEFAULTS_DIR_LIST)) {
     nsCOMArray<nsIFile> directories;
 
-    LoadAppDirIntoArray(mXULAppDir, kAppendPrefDir, directories);
+    LoadDirIntoArray(mXULAppDir, kAppendPrefDir, directories);
     LoadDirsIntoArray(mAppBundleDirectories,
                       kAppendPrefDir, directories);
 
+    // Include the WebappRT-specific prefs dir if we're running WebappRT.
+    if (gAppData && !strcmp(gAppData->ID, WEBAPPRT_APPID)) {
+      const char *const kAppendAppIDPrefDir[] =
+        { "defaults", "pref", gAppData->ID, nsnull };
+      LoadDirIntoArray(mXULAppDir, kAppendAppIDPrefDir, directories);
+    }
+
     rv = NS_NewArrayEnumerator(aResult, directories);
   }
   else if (!strcmp(aProperty, NS_EXT_PREFS_DEFAULTS_DIR_LIST)) {
     nsCOMArray<nsIFile> directories;
 
     LoadDirsIntoArray(mExtensionDirectories,
                       kAppendPrefDir, directories);
 
@@ -667,19 +676,19 @@ nsXREDirProvider::GetFilesInternal(const
     rv = NS_NewArrayEnumerator(aResult, directories);
   }
   else if (!strcmp(aProperty, NS_APP_CHROME_DIR_LIST)) {
     // NS_APP_CHROME_DIR_LIST is only used to get default (native) icons
     // for OS window decoration.
 
     static const char *const kAppendChromeDir[] = { "chrome", nsnull };
     nsCOMArray<nsIFile> directories;
-    LoadAppDirIntoArray(mXULAppDir,
-                        kAppendChromeDir,
-                        directories);
+    LoadDirIntoArray(mXULAppDir,
+                     kAppendChromeDir,
+                     directories);
     LoadDirsIntoArray(mAppBundleDirectories,
                       kAppendChromeDir,
                       directories);
     LoadDirsIntoArray(mExtensionDirectories,
                       kAppendChromeDir,
                       directories);
 
     rv = NS_NewArrayEnumerator(aResult, directories);
new file mode 100644
--- /dev/null
+++ b/webapprt/Makefile.in
@@ -0,0 +1,48 @@
+# 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/.
+
+DEPTH     = ..
+topsrcdir = @top_srcdir@
+srcdir    = @srcdir@
+VPATH     = @srcdir@
+
+include $(topsrcdir)/config/config.mk
+
+DIRS = $(NULL)
+
+ifneq (,$(filter WINNT,$(OS_ARCH)))
+DIRS += win
+else
+ifeq ($(OS_ARCH),Darwin)
+DIRS += mac
+endif # mac
+endif # windows
+
+EXTRA_PP_COMPONENTS = \
+  WebappRTComponents.manifest \
+  WebappRTCommandLineHandler.js \
+  WebappRTDirectoryProvider.js \
+  $(NULL)
+
+EXTRA_JS_MODULES = \
+	WebappRT.jsm \
+	$(NULL)
+
+include $(topsrcdir)/config/rules.mk
+
+libs:: prefs.js
+	$(NSINSTALL) -D $(DIST)/bin/defaults/pref/webapprt@mozilla.org
+	$(INSTALL) $^ $(DIST)/bin/defaults/pref/webapprt@mozilla.org
+
+GRE_MILESTONE := $(shell tail -n 1 $(topsrcdir)/config/milestone.txt 2>/dev/null || tail -1 $(topsrcdir)/config/milestone.txt)
+GRE_BUILDID := $(shell cat $(DEPTH)/config/buildid)
+DEFINES += -DGRE_MILESTONE=$(GRE_MILESTONE) -DGRE_BUILDID=$(GRE_BUILDID)
+
+webapprt.ini: webapprt.ini.in $(DEPTH)/config/buildid $(topsrcdir)/config/milestone.txt
+	$(PYTHON) $(topsrcdir)/config/Preprocessor.py $(DEFINES) $< > $@
+
+libs:: webapprt.ini
+	$(INSTALL) webapprt.ini $(DIST)/bin
+
+GARBAGE += webapprt.ini
new file mode 100644
--- /dev/null
+++ b/webapprt/WebappRT.jsm
@@ -0,0 +1,55 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+const EXPORTED_SYMBOLS = ["WebappRT"];
+
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+const Cu = Components.utils;
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+
+XPCOMUtils.defineLazyGetter(this, "FileUtils", function() {
+  Cu.import("resource://gre/modules/FileUtils.jsm");
+  return FileUtils;
+});
+
+let WebappRT = {
+  get config() {
+    let webappFile = FileUtils.getFile("AppRegD", ["webapp.json"]);
+    let inputStream = Cc["@mozilla.org/network/file-input-stream;1"].
+                      createInstance(Ci.nsIFileInputStream);
+    inputStream.init(webappFile, -1, 0, 0);
+    let json = Cc["@mozilla.org/dom/json;1"].createInstance(Ci.nsIJSON);
+    let config = json.decodeFromStream(inputStream, webappFile.fileSize);
+
+    // Memoize the getter, freezing the `config` object in the meantime so
+    // consumers don't inadvertently (or intentionally) change it, as the object
+    // is meant to be a read-only representation of the webapp's configuration.
+    config = deepFreeze(config);
+    delete this.config;
+    Object.defineProperty(this, "config", { get: function getConfig() config });
+    return this.config;
+  }
+};
+
+function deepFreeze(o) {
+  // First, freeze the object.
+  Object.freeze(o);
+
+  // Then recursively call deepFreeze() to freeze its properties.
+  for (let p in o) {
+    // If the object is on the prototype, not an object, or is already frozen,
+    // skip it.  Note that this might leave an unfrozen reference somewhere in
+    // the object if there is an already frozen object containing an unfrozen
+    // object.
+    if (!o.hasOwnProperty(p) || !(typeof o[p] == "object") ||
+        Object.isFrozen(o[p]))
+      continue;
+
+    deepFreeze(o[p]);
+  }
+
+  return o;
+}
new file mode 100644
--- /dev/null
+++ b/webapprt/WebappRTCommandLineHandler.js
@@ -0,0 +1,31 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+const Cu = Components.utils;
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
+
+function CommandLineHandler() {}
+
+CommandLineHandler.prototype = {
+  classID: Components.ID("{6d69c782-40a3-469b-8bfd-3ee366105a4a}"),
+
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsICommandLineHandler]),
+
+  handle: function handle(cmdLine) {
+    Services.ww.openWindow(null,
+                           "chrome://webapprt/content/webapp.xul",
+                           "_blank",
+                           "chrome,dialog=no,all,resizable",
+                           null);
+  },
+
+  helpInfo : "",
+};
+
+let components = [CommandLineHandler];
+let NSGetFactory = XPCOMUtils.generateNSGetFactory(components);
new file mode 100644
--- /dev/null
+++ b/webapprt/WebappRTComponents.manifest
@@ -0,0 +1,9 @@
+# WebappRTCommandLineHandler.js
+component {6d69c782-40a3-469b-8bfd-3ee366105a4a} WebappRTCommandLineHandler.js application=webapprt@mozilla.org
+contract @mozilla.org/webapprt/clh;1 {6d69c782-40a3-469b-8bfd-3ee366105a4a} application=webapprt@mozilla.org
+category command-line-handler x-default @mozilla.org/webapprt/clh;1 application=webapprt@mozilla.org
+
+# WebappRTDirectoryProvider.js
+component {e1799fda-4b2f-4457-b671-e0641d95698d} WebappRTDirectoryProvider.js application=webapprt@mozilla.org
+contract @mozilla.org/webapprt/directory-provider;1 {e1799fda-4b2f-4457-b671-e0641d95698d} application=webapprt@mozilla.org
+category xpcom-directory-providers webapprt-directory-provider @mozilla.org/webapprt/directory-provider;1 application=webapprt@mozilla.org
new file mode 100644
--- /dev/null
+++ b/webapprt/WebappRTDirectoryProvider.js
@@ -0,0 +1,34 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+const Cu = Components.utils;
+
+const WEBAPP_REGISTRY_DIR = "WebappRegD";
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/WebappRT.jsm");
+
+function DirectoryProvider() {}
+
+DirectoryProvider.prototype = {
+  classID: Components.ID("{e1799fda-4b2f-4457-b671-e0641d95698d}"),
+
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIDirectoryServiceProvider]),
+
+  getFile: function(prop, persistent) {
+    if (prop == WEBAPP_REGISTRY_DIR) {
+      let dir = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile);
+      dir.initWithPath(WebappRT.config.registryDir);
+      return dir;
+    }
+
+    // We return null to show failure instead of throwing an error,
+    // which works with the way the interface is called (per bug 529077).
+    return null;
+  }
+};
+
+const NSGetFactory = XPCOMUtils.generateNSGetFactory([DirectoryProvider]);
new file mode 100644
--- /dev/null
+++ b/webapprt/content/webapp.js
@@ -0,0 +1,77 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+const Cu = Components.utils;
+
+Cu.import("resource://gre/modules/WebappRT.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
+
+function onLoad() {
+  window.removeEventListener("load", onLoad, false);
+
+  let installRecord = WebappRT.config.app;
+  let manifest = WebappRT.config.app.manifest;
+
+  // Set the title of the window to the name of the webapp.
+  document.documentElement.setAttribute("title", manifest.name);
+
+  // Load the webapp's launch path.
+  let url = Services.io.newURI(installRecord.origin, null, null);
+  if (manifest.launch_path)
+    url = Services.io.newURI(manifest.launch_path, null, url);
+  document.getElementById("content").setAttribute("src", url.spec);
+}
+window.addEventListener("load", onLoad, false);
+
+#ifdef XP_MACOSX
+// On Mac, we dynamically create the label for the Quit menuitem, using
+// a string property to inject the name of the webapp into it.
+window.addEventListener("load", function onLoadUpdateMenuItems() {
+  window.removeEventListener("load", onLoadUpdateMenuItems, false);
+  let installRecord = WebappRT.config.app;
+  let manifest = WebappRT.config.app.manifest;
+  let bundle =
+    Services.strings.createBundle("chrome://webapprt/locale/webapp.properties");
+  let quitLabel = bundle.formatStringFromName("quitApplicationCmdMac.label",
+                                              [manifest.name], 1);
+  let hideLabel = bundle.formatStringFromName("hideApplicationCmdMac.label",
+                                              [manifest.name], 1);
+  document.getElementById("menu_FileQuitItem").setAttribute("label", quitLabel);
+  document.getElementById("menu_mac_hide_app").setAttribute("label", hideLabel);
+}, false);
+#endif
+
+function updateEditUIVisibility() {
+#ifndef XP_MACOSX
+  let editMenuPopupState = document.getElementById("menu_EditPopup").state;
+
+  // The UI is visible if the Edit menu is opening or open, if the context menu
+  // is open, or if the toolbar has been customized to include the Cut, Copy,
+  // or Paste toolbar buttons.
+  gEditUIVisible = editMenuPopupState == "showing" ||
+                   editMenuPopupState == "open";
+
+  // If UI is visible, update the edit commands' enabled state to reflect
+  // whether or not they are actually enabled for the current focus/selection.
+  if (gEditUIVisible) {
+    goUpdateGlobalEditMenuItems();
+  }
+
+  // Otherwise, enable all commands, so that keyboard shortcuts still work,
+  // then lazily determine their actual enabled state when the user presses
+  // a keyboard shortcut.
+  else {
+    goSetCommandEnabled("cmd_undo", true);
+    goSetCommandEnabled("cmd_redo", true);
+    goSetCommandEnabled("cmd_cut", true);
+    goSetCommandEnabled("cmd_copy", true);
+    goSetCommandEnabled("cmd_paste", true);
+    goSetCommandEnabled("cmd_selectAll", true);
+    goSetCommandEnabled("cmd_delete", true);
+    goSetCommandEnabled("cmd_switchTextDirection", true);
+  }
+#endif
+}
new file mode 100644
--- /dev/null
+++ b/webapprt/content/webapp.xul
@@ -0,0 +1,156 @@
+<?xml version="1.0"?>
+
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+   - License, v. 2.0. If a copy of the MPL was not distributed with this file,
+   - You can obtain one at http://mozilla.org/MPL/2.0/.  -->
+
+<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
+
+<?xul-overlay href="chrome://global/content/editMenuOverlay.xul"?>
+
+<!DOCTYPE window [
+<!ENTITY % webappDTD SYSTEM "chrome://webapprt/locale/webapp.dtd">
+%webappDTD;
+]>
+
+<window windowtype="webapprt:webapp"
+        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+        width="1024" height="768">
+
+<script type="application/javascript" src="chrome://global/content/globalOverlay.js"/>
+<script type="application/javascript" src="chrome://webapprt/content/webapp.js"/>
+
+<commandset id="mainCommandSet">
+  <command id="cmd_quitApplication" oncommand="goQuitApplication()"/>
+  <commandset id="editMenuCommands"/>
+</commandset>
+
+<keyset id="mainKeyset">
+  <key id="key_undo"
+       key="&undoCmd.key;"
+       modifiers="accel"/>
+  <key id="key_redo" key="&undoCmd.key;" modifiers="accel,shift"/>
+  <key id="key_cut"
+       key="&cutCmd.key;"
+       modifiers="accel"/>
+  <key id="key_copy"
+       key="&copyCmd.key;"
+       modifiers="accel"/>
+  <key id="key_paste"
+       key="&pasteCmd.key;"
+       modifiers="accel"/>
+  <key id="key_delete" keycode="VK_DELETE" command="cmd_delete"/>
+  <key id="key_selectAll" key="&selectAllCmd.key;" modifiers="accel"/>
+  <key id="key_quitApplication"
+       key="&quitApplicationCmdMac.key;"
+       command="cmd_quitApplication"
+       modifiers="accel"/>
+  <key id="key_hideThisAppCmdMac"
+       key="&hideThisAppCmdMac.key;"
+       modifiers="accel"/>
+  <key id="key_hideOtherAppsCmdMac"
+       key="&hideOtherAppsCmdMac.key;"
+       modifiers="accel,alt"/>
+</keyset>
+
+  <menubar id="main-menubar">
+
+#ifndef XP_MACOSX
+    <!-- On Mac, the Quit item gets moved to the Application menu by nsMenuBarX.
+       - And right now it's the only item in the File menu.  So if we put it
+       - into that menu on Mac, the File menu shows up empty on that OS.
+       - To work around that problem, we put the item into the Edit menu on Mac
+       - (from which nsMenuBarX still moves it properly), and we don't create
+       - the File menu in the first place on that OS.
+       -
+       - But if you are adding a persistent item to the File menu on Mac,
+       - then that workaround is no longer necessary, and you can move the Quit
+       - item up here. -->
+    <menu id="file-menu" label="&fileMenu.label;"
+          accesskey="&fileMenu.accesskey;">
+      <menupopup id="menu_FilePopup">
+        <menuitem id="menu_FileQuitItem"
+#ifdef XP_WIN
+                  label="&quitApplicationCmdWin.label;"
+                  accesskey="&quitApplicationCmdWin.accesskey;"
+#else
+                  label="&quitApplicationCmd.label;"
+                  accesskey="&quitApplicationCmd.accesskey;"
+#endif
+#ifdef XP_UNIX
+                  key="key_quitApplication"
+#endif
+                  command="cmd_quitApplication"/>
+      </menupopup>
+    </menu>
+#endif
+
+    <menu id="edit-menu" label="&editMenu.label;"
+          accesskey="&editMenu.accesskey;">
+      <menupopup id="menu_EditPopup"
+                 onpopupshowing="updateEditUIVisibility()"
+                 onpopuphidden="updateEditUIVisibility()">
+
+#ifdef XP_MACOSX
+        <!-- These items get moved to the Application menu by nsMenuBarX.
+           - They can live in any menu.
+           -
+           - See the comment on the File menu above for why the Quit item is
+           - here, and note that JavaScript code dynamically updates the labels
+           - of the Quit and Hide items, which include the name of the app. -->
+        <menuitem id="menu_FileQuitItem"
+                  label="&quitApplicationCmd.label;"
+                  key="key_quitApplication"
+                  command="cmd_quitApplication"/>
+        <menuitem id="menu_mac_hide_app"
+                  key="key_hideThisAppCmdMac"/>
+        <menuitem id="menu_mac_hide_others"
+                  label="&hideOtherAppsCmdMac.label;"
+                  key="key_hideOtherAppsCmdMac"/>
+        <menuitem id="menu_mac_show_all" label="&showAllAppsCmdMac.label;"/>
+#endif
+
+        <menuitem id="menu_undo"
+                  label="&undoCmd.label;"
+                  key="key_undo"
+                  accesskey="&undoCmd.accesskey;"
+                  command="cmd_undo"/>
+        <menuitem id="menu_redo"
+                  label="&redoCmd.label;"
+                  key="key_redo"
+                  accesskey="&redoCmd.accesskey;"
+                  command="cmd_redo"/>
+        <menuseparator/>
+        <menuitem id="menu_cut"
+                  label="&cutCmd.label;"
+                  key="key_cut"
+                  accesskey="&cutCmd.accesskey;"
+                  command="cmd_cut"/>
+        <menuitem id="menu_copy"
+                  label="&copyCmd.label;"
+                  key="key_copy"
+                  accesskey="&copyCmd.accesskey;"
+                  command="cmd_copy"/>
+        <menuitem id="menu_paste"
+                  label="&pasteCmd.label;"
+                  key="key_paste"
+                  accesskey="&pasteCmd.accesskey;"
+                  command="cmd_paste"/>
+        <menuitem id="menu_delete"
+                  label="&deleteCmd.label;"
+                  key="key_delete"
+                  accesskey="&deleteCmd.accesskey;"
+                  command="cmd_delete"/>
+        <menuseparator/>
+        <menuitem id="menu_selectAll"
+                  label="&selectAllCmd.label;"
+                  key="key_selectAll"
+                  accesskey="&selectAllCmd.accesskey;"
+                  command="cmd_selectAll"/>
+      </menupopup>
+    </menu>
+  </menubar>
+
+  <browser type="content" id="content" flex="1"/>
+
+</window>
new file mode 100644
--- /dev/null
+++ b/webapprt/jar.mn
@@ -0,0 +1,4 @@
+webapprt.jar:
+%  content webapprt %content/
+*       content/webapp.js                         (content/webapp.js)
+*       content/webapp.xul                        (content/webapp.xul)
new file mode 100644
--- /dev/null
+++ b/webapprt/locales/en-US/webapp-uninstaller/webapp-uninstaller.properties
@@ -0,0 +1,10 @@
+# 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/.
+
+# LOCALIZATION NOTE: This file must be saved as UTF8
+# LOCALIZATION NOTE: $AppName will be replaced with the (already localized)
+#                    name of the webapp being uninstalled.
+
+^UninstallCaption=$AppName Uninstall
+UN_CONFIRM_UNINSTALL=$AppName will be uninstalled from your computer.
new file mode 100644
--- /dev/null
+++ b/webapprt/mac/Makefile.in
@@ -0,0 +1,47 @@
+# 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/.
+
+DEPTH		= ../..
+topsrcdir	= @top_srcdir@
+srcdir		= @srcdir@
+VPATH		= @srcdir@
+
+# This switches $(INSTALL) to copy mode, like $(SYSINSTALL), so things that
+# shouldn't get 755 perms need $(IFLAGS1) for either way of calling nsinstall.
+NSDISTMODE = copy
+
+include $(topsrcdir)/config/config.mk
+
+PROGRAM = webapprt-stub$(BIN_SUFFIX)
+
+CMMSRCS = webapprt.mm
+
+# Don't create a dependency on mozglue, which is impossible (difficult?)
+# to dynamically link into our executable, as we copy it to arbitrary locations.
+MOZ_GLUE_LDFLAGS =
+MOZ_GLUE_PROGRAM_LDFLAGS =
+
+# mozglue uses mfbt's Assertions.cpp, which provides MOZ_ASSERT, which lots
+# of code in libxpcom uses, so we have to do the same.
+VPATH += $(topsrcdir)/mfbt
+CPPSRCS = Assertions.cpp
+
+LOCAL_INCLUDES += -I$(topsrcdir)/toolkit/xre
+LOCAL_INCLUDES += -I$(topsrcdir)/xpcom/base
+LOCAL_INCLUDES += -I$(topsrcdir)/xpcom/build
+LOCAL_INCLUDES += -I$(DEPTH)/build
+
+DEFINES += -DXPCOM_GLUE
+STL_FLAGS=
+
+LIBS = \
+  $(XPCOM_STANDALONE_GLUE_LDOPTS) \
+  $(NULL)
+
+include $(topsrcdir)/config/rules.mk
+
+GRE_BUILDID := $(shell cat $(DEPTH)/config/buildid)
+DEFINES += -DGRE_BUILDID=$(GRE_BUILDID)
+
+webapprt.$(OBJ_SUFFIX): $(DEPTH)/config/buildid
new file mode 100644
--- /dev/null
+++ b/webapprt/mac/webapprt.mm
@@ -0,0 +1,356 @@
+/* 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/. */
+
+//PLAN:
+
+ // open my bundle, check for an override binary signature
+ // find the newest firefox. open its bundle, get the version number.
+ // if the firefox version is different than ours:
+ //   delete our own binary,
+ //   copy the newer webapprt binary from Firefox
+ //   exec it, and quit
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <sys/stat.h>
+
+#include <Cocoa/Cocoa.h>
+#include <Foundation/Foundation.h>
+
+#include "nsXPCOMGlue.h"
+#include "nsINIParser.h"
+#include "nsXPCOMPrivate.h"              // for MAXPATHLEN and XPCOM_DLL
+#include "nsXULAppAPI.h"
+#include "nsComponentManagerUtils.h"
+#include "nsCOMPtr.h"
+#include "nsILocalFile.h"
+#include "nsStringGlue.h"
+
+const char WEBAPPRT_EXECUTABLE[] = "webapprt-stub";
+const char FXAPPINI_NAME[] = "application.ini";
+const char WEBAPPINI_NAME[] = "webapp.ini";
+const char WEBRTINI_NAME[] = "webapprt.ini";
+
+//need the correct relative path here
+const char APP_CONTENTS_PATH[] = "/Contents/MacOS/";
+
+void ExecNewBinary(NSString* launchPath);
+
+NSString *PathToWebRT(NSString* alternateBinaryID);
+
+NSException* MakeException(NSString* name, NSString* message);
+
+void DisplayErrorAlert(NSString* title, NSString* message);
+
+XRE_GetFileFromPathType XRE_GetFileFromPath;
+XRE_CreateAppDataType XRE_CreateAppData;
+XRE_FreeAppDataType XRE_FreeAppData;
+XRE_mainType XRE_main;
+
+const nsDynamicFunctionLoad kXULFuncs[] = {
+  { "XRE_GetFileFromPath", (NSFuncPtr*) &XRE_GetFileFromPath },
+  { "XRE_CreateAppData", (NSFuncPtr*) &XRE_CreateAppData },
+  { "XRE_FreeAppData", (NSFuncPtr*) &XRE_FreeAppData },
+  { "XRE_main", (NSFuncPtr*) &XRE_main },
+  { nsnull, nsnull }
+};
+
+nsresult
+AttemptGRELoad(char *greDir)
+{
+  nsresult rv;
+  char xpcomDLLPath[MAXPATHLEN];
+  snprintf(xpcomDLLPath, MAXPATHLEN, "%s%s", greDir, XPCOM_DLL);
+
+  rv = XPCOMGlueStartup(xpcomDLLPath);
+
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  rv = XPCOMGlueLoadXULFunctions(kXULFuncs);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  return rv;
+}
+
+// Copied from toolkit/xre/nsAppData.cpp.
+void
+SetAllocatedString(const char *&str, const char *newvalue)
+{
+  NS_Free(const_cast<char*>(str));
+  if (newvalue) {
+    str = NS_strdup(newvalue);
+  } else {
+    str = nsnull;
+  }
+}
+
+
+int
+main(int argc, char **argv)
+{
+  NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+
+  NSString *firefoxPath = nil;
+  NSString *alternateBinaryID = nil;
+
+  //this is our version, to be compared with the version of the binary we are asked to use
+  NSString* myVersion = [NSString stringWithFormat:@"%s", NS_STRINGIFY(GRE_BUILDID)];
+
+  NSLog(@"MY WEBAPPRT BUILDID: %@", myVersion);
+
+
+  //I need to look in our bundle first, before deciding what firefox binary to use
+  NSBundle* myBundle = [NSBundle mainBundle];
+  NSString* myBundlePath = [myBundle bundlePath];
+  alternateBinaryID = [myBundle objectForInfoDictionaryKey:@"FirefoxBinary"];
+  NSLog(@"found override firefox binary: %@", alternateBinaryID);
+
+  @try {
+    //find a webapprt binary to launch with.  throws an exception with error dialog if none found.
+    firefoxPath = PathToWebRT(alternateBinaryID);
+    NSLog(@"USING FIREFOX : %@", firefoxPath);
+
+    NSString *myWebRTPath = [myBundle pathForAuxiliaryExecutable: @"webapprt"];
+    if (!myWebRTPath) {
+      @throw MakeException(@"Missing WebRT Files", @"Cannot locate binary for this application");
+    }
+
+    //GET FIREFOX BUILD ID
+    NSString *firefoxINIFilePath = [NSString stringWithFormat:@"%@%s%s", firefoxPath, APP_CONTENTS_PATH, FXAPPINI_NAME];
+    nsINIParser ffparser;
+    NSLog(@"Looking for firefox ini file here: %@", firefoxINIFilePath);
+
+    if (NS_FAILED(ffparser.Init([firefoxINIFilePath UTF8String]))) {
+      NSLog(@"Unable to locate Firefox application.ini");
+      @throw MakeException(@"Error", @"Unable to parse environment files for application startup");
+    }
+
+    char ffVersChars[MAXPATHLEN];
+    if (NS_FAILED(ffparser.GetString("App", "BuildID", ffVersChars, MAXPATHLEN))) {
+      NSLog(@"Unable to retrieve Firefox BuildID");
+      @throw MakeException(@"Error", @"Unable to determine Firefox version.");
+    }
+    NSString* firefoxVersion = [NSString stringWithFormat:@"%s", ffVersChars];
+
+    NSLog(@"FIREFOX WEBAPPRT BUILDID: %@", firefoxVersion);
+    //GOT FIREFOX BUILD ID
+
+    //COMPARE MY BUILD ID AND FIREFOX BUILD ID
+    if ([myVersion compare: firefoxVersion] != NSOrderedSame) {
+      //we are going to assume that if they are different, we need to re-copy the webapprt, regardless of whether
+      // it is newer or older.  If we don't find a webapprt, then the current Firefox must not be new enough to run webapps.
+      NSLog(@"### This Application has an old webrt. Updating it.");
+      NSLog(@"### My webapprt path: %@", myWebRTPath);
+
+      NSFileManager* fileClerk = [[NSFileManager alloc] init];
+      NSError *errorDesc = nil;
+
+      //we know the firefox path, so copy the new webapprt here
+      NSString *newWebRTPath = [NSString stringWithFormat: @"%@%s%s", firefoxPath, APP_CONTENTS_PATH, WEBAPPRT_EXECUTABLE];
+      NSLog(@"### Firefox webapprt path: %@", newWebRTPath);
+      if (![fileClerk fileExistsAtPath:newWebRTPath]) {
+        NSString* msg = [NSString stringWithFormat: @"This version of Firefox (%@) cannot run web applications, because it is not recent enough or damaged", firefoxVersion];
+        @throw MakeException(@"Missing WebRT Files", msg);
+      }
+
+      [fileClerk removeItemAtPath: myWebRTPath error: &errorDesc];
+      if (errorDesc != nil) {
+        NSLog(@"failed to unlink old binary file at path: %@ with error: %@", myWebRTPath, errorDesc);
+        @throw MakeException(@"Unable To Update", @"Failed preparation for runtime update");
+      }
+
+      [fileClerk copyItemAtPath: newWebRTPath toPath: myWebRTPath error: &errorDesc];
+      [fileClerk release];
+      if (errorDesc != nil) {
+        NSLog(@"failed to copy new webrt file: %@", errorDesc);
+        @throw MakeException(@"Unable To Update", @"Failed to update runtime");
+      } else {
+        NSLog(@"### Successfully updated webapprt, relaunching");
+      }
+
+      //execv the new binary, and ride off into the sunset
+      ExecNewBinary(myWebRTPath);
+
+    } else {
+      //we are ready to load XUL and such, and go go go
+
+      NSLog(@"This Application has the newest webrt.  Launching!");
+
+      int result = 0;
+      char rtINIPath[MAXPATHLEN];
+
+      // Set up our environment to know where webapp.ini was loaded from.
+      char appEnv[MAXPATHLEN];
+      snprintf(appEnv, MAXPATHLEN, "%s%s%s", [myBundlePath UTF8String], APP_CONTENTS_PATH, WEBAPPINI_NAME);
+      if (setenv("XUL_APP_FILE", appEnv, 1)) {
+        NSLog(@"Couldn't set XUL_APP_FILE to: %s", appEnv);
+        @throw MakeException(@"Error", @"Unable to set webapp INI file.");
+      }
+      NSLog(@"Set XUL_APP_FILE to: %s", appEnv);
+
+      //CONSTRUCT GREDIR AND CALL XPCOMGLUE WITH IT
+      char greDir[MAXPATHLEN];
+      snprintf(greDir, MAXPATHLEN, "%s%s", [firefoxPath UTF8String], APP_CONTENTS_PATH);
+      if (!NS_SUCCEEDED(AttemptGRELoad(greDir))) {
+          @throw MakeException(@"Error", @"Unable to load XUL files for application startup");
+      }
+
+      // NOTE: The GRE has successfully loaded, so we can use XPCOM now
+
+      NS_LogInit();
+      { // Scope for any XPCOM stuff we create
+        nsINIParser parser;
+        if (NS_FAILED(parser.Init(appEnv))) {
+          NSLog(@"%s was not found\n", appEnv);
+          @throw MakeException(@"Error", @"Unable to parse environment files for application startup");
+        }
+
+        // Get the path to the runtime's INI file.  This should be in the
+        // same directory as the GRE.
+        snprintf(rtINIPath, MAXPATHLEN, "%s%s%s", [firefoxPath UTF8String], APP_CONTENTS_PATH, WEBRTINI_NAME);
+        NSLog(@"webapprt.ini path: %s", rtINIPath);
+        if (![[NSFileManager defaultManager] fileExistsAtPath:[NSString stringWithFormat:@"%s", rtINIPath]]) {
+          NSString* msg = [NSString stringWithFormat: @"This copy of Firefox (%@) cannot run web applications, because it is missing important files", firefoxVersion];
+          @throw MakeException(@"Missing WebRT Files", msg);
+        }
+
+        // Load the runtime's INI from its path.
+        nsCOMPtr<nsILocalFile> rtINI;
+        if (NS_FAILED(XRE_GetFileFromPath(rtINIPath, getter_AddRefs(rtINI)))) {
+          NSLog(@"Runtime INI path not recognized: '%s'\n", rtINIPath);
+          @throw MakeException(@"Error", @"Incorrect path to base INI file.");
+        }
+
+        if (!rtINI) {
+          NSLog(@"Error: missing webapprt.ini");
+          @throw MakeException(@"Error", @"Missing base INI file.");
+        }
+
+        nsXREAppData *webShellAppData;
+        if (NS_FAILED(XRE_CreateAppData(rtINI, &webShellAppData))) {
+          NSLog(@"Couldn't read webapprt.ini: %s", rtINIPath);
+          @throw MakeException(@"Error", @"Unable to parse base INI file.");
+        }
+
+        char profile[MAXPATHLEN];
+        if (NS_FAILED(parser.GetString("Webapp", "Profile", profile, MAXPATHLEN))) {
+          NSLog(@"Unable to retrieve profile from web app INI file");
+          @throw MakeException(@"Error", @"Unable to retrieve installation profile.");
+        }
+        NSLog(@"setting app profile: %s", profile);
+        SetAllocatedString(webShellAppData->profile, profile);
+
+        nsCOMPtr<nsILocalFile> directory;
+        if (NS_FAILED(XRE_GetFileFromPath(greDir, getter_AddRefs(directory)))) {
+          NSLog(@"Unable to open app dir");
+          @throw MakeException(@"Error", @"Unable to open application directory.");
+        }
+
+        nsCOMPtr<nsILocalFile> xreDir;
+        if (NS_FAILED(XRE_GetFileFromPath(greDir, getter_AddRefs(xreDir)))) {
+          NSLog(@"Unable to open XRE dir");
+          @throw MakeException(@"Error", @"Unable to open application XRE directory.");
+        }
+
+        xreDir.forget(&webShellAppData->xreDirectory);
+        NS_IF_RELEASE(webShellAppData->directory);
+        directory.forget(&webShellAppData->directory);
+
+        // There is only XUL.
+        result = XRE_main(argc, argv, webShellAppData);
+
+        XRE_FreeAppData(webShellAppData);
+      }
+      NS_LogTerm();
+
+      return result;
+    }
+
+  }
+  @catch (NSException *e) {
+    NSLog(@"got exception: %@", e);
+    DisplayErrorAlert([e name], [e reason]);
+  }
+  @finally {
+    [pool drain];
+  }
+  return 0;
+}  //end main
+
+
+NSException*
+MakeException(NSString* name, NSString* message)
+{
+  NSException* myException = [NSException
+        exceptionWithName:name
+        reason:message
+        userInfo:nil];
+  return myException;
+}
+
+void
+DisplayErrorAlert(NSString* title, NSString* message)
+{
+  CFUserNotificationDisplayNotice(0, kCFUserNotificationNoteAlertLevel,
+    NULL, NULL, NULL,
+    (CFStringRef)title,
+    (CFStringRef)message,
+    CFSTR("Quit")
+    );
+}
+
+/* Find the currently installed Firefox, if any, and return
+ * an absolute path to it. may return nil */
+NSString
+*PathToWebRT(NSString* alternateBinaryID)
+{
+  //default is firefox
+  NSString *binaryPath = nil;
+
+  //we look for these flavors of Firefox, in this order
+  NSArray* launchBinarySearchList = [NSArray arrayWithObjects: @"org.mozilla.nightly",
+                                                                @"org.mozilla.aurora",
+                                                                @"org.mozilla.firefox", nil];
+
+  //if they provided a manual override, use that.  If they made an error, it will fail to launch
+  if (alternateBinaryID != nil && ([alternateBinaryID length] > 0)) {
+    binaryPath = [[NSWorkspace sharedWorkspace] absolutePathForAppBundleWithIdentifier:alternateBinaryID];
+    if (binaryPath == nil || [binaryPath length] == 0) {
+      @throw MakeException(@"WebappRT Not Found",
+                            [NSString stringWithFormat:@"Failed to locate specified override runtime with signature '%@'", alternateBinaryID]);
+    }
+    return binaryPath;
+  }
+
+  //No override found, loop through the various flavors of firefox we have
+  for (NSString* signature in launchBinarySearchList) {
+    NSLog(@"SEARCHING for webapprt, trying: %@", signature);
+    binaryPath = [[NSWorkspace sharedWorkspace] absolutePathForAppBundleWithIdentifier:signature];
+    if (binaryPath && [binaryPath length] > 0) {
+      return binaryPath;
+    }
+  }
+
+  NSLog(@"unable to find a valid webrt path");
+  @throw MakeException(@"Missing Runtime", @"Mozilla Apps require Firefox to be installed");
+
+  return nil;
+}
+
+void
+ExecNewBinary(NSString* launchPath)
+{
+  NSLog(@" launching webrt at path: %@\n", launchPath);
+
+  const char *const newargv[] = {[launchPath UTF8String], NULL};
+
+  NSLog(@"COMMAND LINE: '%@ %s'", launchPath, newargv[0]);
+  execv([launchPath UTF8String], (char **)newargv);
+}
new file mode 100644
--- /dev/null
+++ b/webapprt/prefs.js
@@ -0,0 +1,13 @@
+/* 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/. */
+
+pref("browser.chromeURL", "chrome://webapprt/content/webapp.xul");
+
+// We set this to the value of DEFAULT_HIDDENWINDOW_URL in nsAppShellService.cpp
+// so our app is treated as not having an application-provided hidden window.
+// Ideally, we could just leave it out, but because we are being distributed
+// in a unified directory with Firefox, Firefox's preferences are being read
+// before ours, which means this preference is being set by Firefox, and we need
+// to set it here to override the Firefox-provided value.
+pref("browser.hiddenWindowChromeURL", "resource://gre-resources/hiddenWindow.html");
new file mode 100644
--- /dev/null
+++ b/webapprt/webapprt.ini.in
@@ -0,0 +1,18 @@
+#if 0
+; This Source Code Form is subject to the terms of the Mozilla Public
+; License, v. 2.0. If a copy of the MPL was not distributed with this file,
+; You can obtain one at http://mozilla.org/MPL/2.0/.
+#endif
+
+#filter substitution
+
+[App]
+ID=webapprt@mozilla.org
+Vendor=Mozilla
+Name=Webapp Runtime
+Version=@GRE_MILESTONE@
+BuildID=@GRE_BUILDID@
+
+[Gecko]
+MinVersion=@GRE_MILESTONE@
+MaxVersion=@GRE_MILESTONE@
new file mode 100644
--- /dev/null
+++ b/webapprt/win/Makefile.in
@@ -0,0 +1,125 @@
+# 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/.
+
+DEPTH     = ../..
+topsrcdir = @top_srcdir@
+srcdir    = @srcdir@
+VPATH     = @srcdir@
+
+include $(DEPTH)/config/autoconf.mk
+
+# Build a binary bootstrapping with XRE_main
+PROGRAM   = webapprt-stub$(BIN_SUFFIX)
+
+CPPSRCS   = webapprt.cpp
+
+# Don't create a dependency on mozglue, which is impossible (difficult?)
+# to dynamically link into our executable, as we copy it to arbitrary locations.
+MOZ_GLUE_LDFLAGS =
+
+# mozglue uses mfbt's Assertions.cpp, which provides MOZ_ASSERT, which lots
+# of code in libxpcom uses, so we have to do the same.
+VPATH += $(topsrcdir)/mfbt
+CPPSRCS += Assertions.cpp
+
+LOCAL_INCLUDES += -I$(topsrcdir)/toolkit/xre
+LOCAL_INCLUDES += -I$(topsrcdir)/xpcom/base
+LOCAL_INCLUDES += -I$(topsrcdir)/xpcom/build
+LOCAL_INCLUDES += -I$(DEPTH)/build
+
+DEFINES += -DXPCOM_GLUE
+STL_FLAGS=
+
+LIBS = \
+  $(XPCOM_STANDALONE_GLUE_LDOPTS) \
+  $(NULL)
+
+ifndef MOZ_WINCONSOLE
+ifdef MOZ_DEBUG
+MOZ_WINCONSOLE = 1
+else
+MOZ_WINCONSOLE = 0
+endif
+endif
+
+# Installer stuff
+include $(topsrcdir)/toolkit/mozapps/installer/package-name.mk
+
+CONFIG_DIR = instgen
+SFX_MODULE = $(topsrcdir)/other-licenses/7zstub/firefox/7zSD.sfx
+APP_VERSION := $(shell cat $(topsrcdir)/browser/config/version.txt)
+DEFINES += -DAPP_VERSION=$(APP_VERSION)
+
+include $(topsrcdir)/config/config.mk
+
+ifdef _MSC_VER
+# Always enter a Windows program through wmain, whether or not we're
+# a console application.
+WIN32_EXE_LDFLAGS += -ENTRY:wmainCRTStartup
+endif
+
+OS_LIBS += $(call EXPAND_LIBNAME,shell32)
+
+RCINCLUDE = webapprt.rc
+ifndef GNU_CC
+RCFLAGS += -I$(srcdir)
+else
+RCFLAGS += --include-dir $(srcdir)
+endif
+ifdef DEBUG
+RCFLAGS += -DDEBUG
+endif
+
+# Uninstaller
+ifdef LOCALE_MERGEDIR
+PPL_LOCALE_ARGS = \
+  --l10n-dir=$(LOCALE_MERGEDIR)/webapprt/webapp-uninstaller \
+  --l10n-dir=$(call EXPAND_LOCALE_SRCDIR,webapprt/locales)/webapp-uninstaller \
+  --l10n-dir=$(topsrcdir)/webapprt/locales/en-US/webapp-uninstaller \
+  $(NULL)
+else
+PPL_LOCALE_ARGS=$(call EXPAND_LOCALE_SRCDIR,webapprt/locales)/webapp-uninstaller
+endif
+
+libs::
+	$(RM) -r $(CONFIG_DIR)
+	$(MKDIR) $(CONFIG_DIR)
+	$(PYTHON) $(topsrcdir)/config/Preprocessor.py -Fsubstitution \
+		$(DEFINES) $(ACDEFINES) \
+		$(srcdir)/webapp-uninstaller.nsi.in > $(CONFIG_DIR)/webapp-uninstaller.nsi
+	$(PYTHON) \
+		$(topsrcdir)/toolkit/mozapps/installer/windows/nsis/preprocess-locale.py \
+		--create-nlf-file $(topsrcdir) $(AB_CD) $(CONFIG_DIR)
+	$(PYTHON) \
+		$(topsrcdir)/toolkit/mozapps/installer/windows/nsis/preprocess-locale.py \
+		--preprocess-single-file $(topsrcdir) $(PPL_LOCALE_ARGS) $(CONFIG_DIR) \
+		webapp-uninstaller.properties webapp-uninstaller-locale.nsh
+	$(MAKE) webapp_uninstaller
+
+GARBAGE_DIRS += instgen
+
+include $(topsrcdir)/config/rules.mk
+include $(topsrcdir)/toolkit/mozapps/installer/windows/nsis/makensis.mk
+
+GRE_BUILDID := $(shell cat $(DEPTH)/config/buildid)
+DEFINES += -DGRE_BUILDID=$(GRE_BUILDID)
+
+webapprt.$(OBJ_SUFFIX): $(DEPTH)/config/buildid
+
+# Control the default heap size.
+# This is the heap returned by GetProcessHeap().
+# As we use the CRT heap, the default size is too large and wastes VM.
+#
+# The default heap size is 1MB on Win32.
+# The heap will grow if need be.
+#
+# Set it to 256k.  See bug 127069.
+#
+ifndef GNU_CC
+LDFLAGS += /HEAP:0x40000
+ifeq ($(OS_TEST),x86_64)
+# set stack to 2MB on x64 build.  See bug 582910
+LDFLAGS += -STACK:2097152
+endif
+endif
new file mode 100644
--- /dev/null
+++ b/webapprt/win/webapp-uninstaller.nsi.in
@@ -0,0 +1,163 @@
+# 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/.
+
+# Required Plugins:
+# ShellLink   http://nsis.sourceforge.net/ShellLink_plug-in
+
+; Set verbosity to 3 (e.g. no script) to lessen the noise in the build logs
+!verbose 3
+
+; 7-Zip provides better compression than the lzma from NSIS so we add the files
+; uncompressed and let the application installer compress it.
+SetDatablockOptimize on
+SetCompress off
+CRCCheck on
+SilentInstall silent
+
+RequestExecutionLevel user
+
+!addplugindir ./
+
+; prevents compiling of the reg write logging.
+!define NO_LOG
+
+; Variables
+Var AppFilename
+Var AppName
+Var AppRTTempDir
+
+; Variables/macros used by common.nsh
+Var TmpVal
+!define FileMainEXE           "$AppFilename.exe"
+
+; Other included files may depend upon these includes!
+; The following includes are provided by NSIS.
+!include "FileFunc.nsh"
+!insertmacro GetParameters
+!insertmacro un.RefreshShellIcons
+!include "LogicLib.nsh"
+!include "WinMessages.nsh"
+!include "WinVer.nsh"
+!include "WordFunc.nsh"
+
+; File properties, version strings, etc
+!define CompanyName           "Mozilla Corporation"
+!define UninstallerName       "Mozilla Webapp Runtime App Uninstaller"
+!define UninstallerFilename   "webapp-uninstaller.exe"
+!define AppVersion            "@APP_VERSION@"
+VIProductVersion "1.0.0.0"
+VIAddVersionKey "ProductName"      "${UninstallerName}"
+VIAddVersionKey "CompanyName"      "${CompanyName}"
+VIAddVersionKey "LegalCopyright"   "${CompanyName}"
+VIAddVersionKey "FileVersion"      "${AppVersion}"
+VIAddVersionKey "ProductVersion"   "${AppVersion}"
+VIAddVersionKey "FileDescription"  "${UninstallerName}"
+VIAddVersionKey "OriginalFilename" "${UninstallerFilename}"
+
+; Mozilla custom include
+!include "common.nsh"
+!insertmacro un.DeleteShortcuts
+!insertmacro un.RegCleanUninstall
+!insertmacro un.ParseUninstallLog
+
+Name "Mozilla Web App Runtime App"
+OutFile "${UninstallerFilename}"
+ShowUnInstDetails nevershow
+
+# Create a blank page so that the default pages (instfiles) don't appear
+UninstPage custom un.blankPage
+
+################################################################################
+# Install Sections
+# The "installer" that is generated by this file will be run during the build
+# process to generate an uninstaller.  We call `WriteUninstaller` during
+# `onInit` so this section is empty.
+Section ""
+SectionEnd
+
+################################################################################
+# This is where uninstallation happens
+################################################################################
+Function un.blankPage
+  MessageBox MB_OKCANCEL "$(UN_CONFIRM_UNINSTALL)" /SD IDOK IDCANCEL done
+
+  ; Delete the app exe to prevent launching the app while we are uninstalling.
+  ClearErrors
+  ${DeleteFile} "$INSTDIR\${FileMainEXE}"
+  ${If} ${Errors}
+    ; If the app is running, rename the EXE out of the way
+    CreateDirectory "$AppRTTempDir"
+    Rename "$INSTDIR\${FileMainEXE}" "$AppRTTempDir\${FileMainEXE}"
+    ClearErrors
+  ${EndIf}
+
+
+  SetShellVarContext current  ; Set SHCTX to HKCU
+
+  ; Remove our entry in the "Uninstall" key
+  ${un.RegCleanUninstall}
+
+  ; Remove our shortcuts from start menu, desktop, and taskbar
+  ${un.DeleteShortcuts}
+
+  ; Parse the uninstall log to remove all installed
+  ; files / directories this install is responsible for.
+  ${un.ParseUninstallLog}
+
+  ; Remove the uninstall directory that we control
+  RmDir /r "$INSTDIR\uninstall"
+
+  ; Remove the installation directory if it is empty
+  ${RemoveDir} "$INSTDIR"
+
+  ; Refresh shell icons to reflect the changes we've made
+  ${un.RefreshShellIcons}
+
+  done:
+FunctionEnd
+
+################################################################################
+# Language
+
+!verbose push
+!verbose 3
+!include "webapp-uninstaller-locale.nsh"
+!verbose pop
+
+; Set this after the locale files to override it if it is in the locale. Using
+; " " for BrandingText will hide the "Nullsoft Install System..." branding.
+BrandingText " "
+
+# Initialization Functions
+Function .onInit
+  GetTempFileName $0
+  Delete "$0"
+  CreateDirectory "$0"
+  SetOutPath "$0"
+
+  StrCpy $1 "$0\${UninstallerFilename}"
+  WriteUninstaller "$1"
+
+  ${GetParameters} $2
+  StrCpy $2 "_?=$EXEDIR $2"
+  Exec '"$1" $2'
+  Quit
+FunctionEnd
+
+Function un.onInit
+  StrCpy $LANGUAGE 0
+
+  ${un.GetParent} "$INSTDIR" $INSTDIR
+  ${un.GetLongPath} "$INSTDIR" $INSTDIR
+
+  ReadINIStr $AppFilename "$INSTDIR\webapp.ini" "WebappRT" "Executable"
+  ReadINIStr $AppName "$INSTDIR\webapp.ini" "Webapp" "Name"
+
+  ${Unless} ${FileExists} "$INSTDIR\${FileMainEXE}"
+    Abort
+  ${EndUnless}
+
+  StrCpy $AppRTTempDir "$TEMP\moz_webapprt"
+  RmDir /r "$AppRTTempDir"
+FunctionEnd
new file mode 100644
--- /dev/null
+++ b/webapprt/win/webapprt.cpp
@@ -0,0 +1,513 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+
+/* 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/. */
+
+// System headers (alphabetical)
+#include <fcntl.h>
+#include <io.h>
+#include <share.h>
+#include <sys/stat.h>
+#include <windows.h>
+
+// Mozilla headers (alphabetical)
+#include "nsILocalFile.h"
+#include "nsINIParser.h"
+#include "nsWindowsWMain.cpp"   // we want a wmain entry point
+#include "nsXPCOMGlue.h"
+#include "nsXPCOMPrivate.h"     // for MAXPATHLEN and XPCOM_DLL
+#include "nsXULAppAPI.h"
+
+XRE_GetFileFromPathType XRE_GetFileFromPath;
+XRE_CreateAppDataType XRE_CreateAppData;
+XRE_FreeAppDataType XRE_FreeAppData;
+XRE_mainType XRE_main;
+
+namespace {
+  const char kAPP_INI[] = "application.ini";
+  const char kWEBAPP_INI[] = "webapp.ini";
+  const char kWEBAPPRT_INI[] = "webapprt.ini";
+  const char kAPP_ENV_PREFIX[] = "XUL_APP_FILE=";
+  const char kAPP_RT[] = "webapprt-stub.exe";
+
+  const wchar_t kAPP_RT_BACKUP[] = L"webapprt.old";
+
+  wchar_t curExePath[MAXPATHLEN];
+  wchar_t backupFilePath[MAXPATHLEN];
+  wchar_t iconPath[MAXPATHLEN];
+  char profile[MAXPATHLEN];
+  int* pargc;
+  char*** pargv;
+
+  // Copied from toolkit/xre/nsAppData.cpp.
+  void
+  SetAllocatedString(const char *&str, const char *newvalue)
+  {
+    NS_Free(const_cast<char*>(str));
+    if (newvalue) {
+      str = NS_strdup(newvalue);
+    }
+    else {
+      str = nsnull;
+    }
+  }
+
+  nsresult
+  joinPath(char* const dest,
+           char const* const dir,
+           char const* const leaf,
+           size_t bufferSize)
+  {
+    size_t dirLen = strlen(dir);
+    size_t leafLen = strlen(leaf);
+    bool needsSeparator = (dirLen != 0
+                        && dir[dirLen-1] != '\\'
+                        && leafLen != 0
+                        && leaf[0] != '\\');
+
+    if (dirLen + (needsSeparator? 1 : 0) + leafLen >= bufferSize) {
+      return NS_ERROR_FAILURE;
+    }
+
+    strncpy(dest, dir, bufferSize);
+    char* destEnd = dest + dirLen;
+    if (needsSeparator) {
+      *(destEnd++) = '\\';
+    }
+
+    strncpy(destEnd, leaf, leafLen);
+    return NS_OK;
+  }
+
+  /**
+   * A helper class which calls NS_LogInit/NS_LogTerm in its scope.
+   */
+  class ScopedLogging
+  {
+    public:
+      ScopedLogging() { NS_LogInit(); }
+      ~ScopedLogging() { NS_LogTerm(); }
+  };
+
+  /**
+   * A helper class for scope-guarding nsXREAppData.
+   */
+  class ScopedXREAppData
+  {
+    public:
+      ScopedXREAppData()
+        : mAppData(NULL) { }
+
+      nsresult
+      create(nsILocalFile* aINIFile)
+      {
+        return XRE_CreateAppData(aINIFile, &mAppData);
+      }
+
+      ~ScopedXREAppData()
+      {
+        if (NULL != mAppData) {
+          XRE_FreeAppData(mAppData);
+        }
+      }
+
+      nsXREAppData* const
+      operator->()
+      {
+        return get();
+      }
+
+      nsXREAppData
+      operator*()
+      {
+        return *get();
+      }
+
+      operator
+      nsXREAppData*()
+      {
+        return get();
+      }
+    private:
+      nsXREAppData* mAppData;
+      nsXREAppData* const get() { return mAppData; }
+  };
+
+  void
+  Output(const wchar_t *fmt, ... )
+  {
+    va_list ap;
+    va_start(ap, fmt);
+
+    wchar_t msg[1024];
+    _vsnwprintf_s(msg, _countof(msg), _countof(msg), fmt, ap);
+
+    MessageBoxW(NULL, msg, L"WebappRT", MB_OK);
+
+    va_end(ap);
+  }
+
+  void
+  Output(const char *fmt, ... )
+  {
+    va_list ap;
+    va_start(ap, fmt);
+
+    char msg[1024];
+    vsnprintf(msg, sizeof(msg), fmt, ap);
+
+    wchar_t wide_msg[1024];
+    MultiByteToWideChar(CP_UTF8,
+                        0,
+                        msg,
+                        -1,
+                        wide_msg,
+                        _countof(wide_msg));
+    Output(wide_msg);
+
+    va_end(ap);
+  }
+
+  const nsDynamicFunctionLoad kXULFuncs[] = {
+      { "XRE_GetFileFromPath", (NSFuncPtr*) &XRE_GetFileFromPath },
+      { "XRE_CreateAppData", (NSFuncPtr*) &XRE_CreateAppData },
+      { "XRE_FreeAppData", (NSFuncPtr*) &XRE_FreeAppData },
+      { "XRE_main", (NSFuncPtr*) &XRE_main },
+      { nsnull, nsnull }
+  };
+
+  bool
+  AttemptCopyAndLaunch(wchar_t* src)
+  {
+    // Rename the old app executable
+    if (FALSE == ::MoveFileExW(curExePath,
+                               backupFilePath,
+                               MOVEFILE_REPLACE_EXISTING)) {
+      return false;
+    }
+
+    // Copy webapprt-stub.exe from the Firefox dir to the app's dir
+    if (FALSE == ::CopyFileW(src,
+                             curExePath,
+                             TRUE)) {
+      // Try to move the old file back to its original location
+      ::MoveFileW(backupFilePath,
+                  curExePath);
+      return false;
+    }
+
+    // XXX: We will soon embed the app's icon in the EXE here
+
+    STARTUPINFOW si;
+    PROCESS_INFORMATION pi;
+
+    ::ZeroMemory(&si, sizeof(si));
+    si.cb = sizeof(si);
+    ::ZeroMemory(&pi, sizeof(pi));
+
+    if (!CreateProcessW(curExePath, // Module name
+                        NULL,       // Command line
+                        NULL,       // Process handle not inheritable
+                        NULL,       // Thread handle not inheritable
+                        FALSE,      // Set handle inheritance to FALSE
+                        0,          // No creation flags
+                        NULL,       // Use parent's environment block
+                        NULL,       // Use parent's starting directory
+                        &si,
+                        &pi)) {
+      return false;
+    }
+
+    // Close process and thread handles.
+    CloseHandle( pi.hProcess );
+    CloseHandle( pi.hThread );
+
+    return true;
+  }
+
+  bool
+  AttemptCopyAndLaunch(char* srcUtf8)
+  {
+    wchar_t src[MAXPATHLEN];
+    if (0 == MultiByteToWideChar(CP_UTF8,
+                                 0,
+                                 srcUtf8,
+                                 -1,
+                                 src,
+                                 MAXPATHLEN)) {
+      return false;
+    }
+
+    return AttemptCopyAndLaunch(src);
+  }
+
+  bool
+  AttemptGRELoadAndLaunch(char* greDir)
+  {
+    nsresult rv;
+
+    char xpcomDllPath[MAXPATHLEN];
+    rv = joinPath(xpcomDllPath, greDir, XPCOM_DLL, MAXPATHLEN);
+    NS_ENSURE_SUCCESS(rv, false);
+
+    rv = XPCOMGlueStartup(xpcomDllPath);
+    NS_ENSURE_SUCCESS(rv, false);
+
+    rv = XPCOMGlueLoadXULFunctions(kXULFuncs);
+    NS_ENSURE_SUCCESS(rv, false);
+
+    // NOTE: The GRE has successfully loaded, so we can use XPCOM now
+    { // Scope for any XPCOM stuff we create
+
+      ScopedLogging log;
+
+      // Get the path to the runtime's INI file.  This should be in the
+      // same directory as the GRE.
+      char rtIniPath[MAXPATHLEN];
+      rv = joinPath(rtIniPath, greDir, kWEBAPPRT_INI, MAXPATHLEN);
+      NS_ENSURE_SUCCESS(rv, false);
+
+      // Load the runtime's INI from its path.
+      nsCOMPtr<nsILocalFile> rtINI;
+      rv = XRE_GetFileFromPath(rtIniPath, getter_AddRefs(rtINI));
+      NS_ENSURE_SUCCESS(rv, false);
+
+      if (!rtINI) {
+        return false;
+      }
+
+      ScopedXREAppData webShellAppData;
+      rv = webShellAppData.create(rtINI);
+      NS_ENSURE_SUCCESS(rv, rv);
+
+      SetAllocatedString(webShellAppData->profile, profile);
+
+      nsCOMPtr<nsILocalFile> directory;
+      rv = XRE_GetFileFromPath(greDir,
+                               getter_AddRefs(directory));
+      NS_ENSURE_SUCCESS(rv, false);
+
+      nsCOMPtr<nsILocalFile> xreDir;
+      rv = XRE_GetFileFromPath(greDir,
+                               getter_AddRefs(xreDir));
+      NS_ENSURE_SUCCESS(rv, false);
+
+      xreDir.forget(&webShellAppData->xreDirectory);
+      NS_IF_RELEASE(webShellAppData->directory);
+      directory.forget(&webShellAppData->directory);
+
+      // There is only XUL.
+      XRE_main(*pargc, *pargv, webShellAppData);
+    }
+
+    return true;
+  }
+
+  bool
+  AttemptLoadFromDir(char* firefoxDir)
+  {
+    nsresult rv;
+
+    // Here we're going to open Firefox's application.ini
+    char appIniPath[MAXPATHLEN];
+    rv = joinPath(appIniPath, firefoxDir, kAPP_INI, MAXPATHLEN);
+    NS_ENSURE_SUCCESS(rv, false);
+
+    nsINIParser parser;
+    rv = parser.Init(appIniPath);
+    NS_ENSURE_SUCCESS(rv, false);
+
+    // Get buildid of FF we're trying to load
+    char buildid[MAXPATHLEN]; // This isn't a path, so MAXPATHLEN doesn't
+                              // necessarily make sense, but it's a
+                              // convenient number to use.
+    rv = parser.GetString("App",
+                          "BuildID",
+                          buildid,
+                          MAXPATHLEN);
+    NS_ENSURE_SUCCESS(rv, false);
+
+    if (0 == strcmp(buildid, NS_STRINGIFY(GRE_BUILDID))) {
+      return AttemptGRELoadAndLaunch(firefoxDir);
+    }
+
+    char webAppRTExe[MAXPATHLEN];
+    rv = joinPath(webAppRTExe, firefoxDir, kAPP_RT, MAXPATHLEN);
+    NS_ENSURE_SUCCESS(rv, false);
+
+    return AttemptCopyAndLaunch(webAppRTExe);
+  }
+
+  bool
+  GetFirefoxDirFromRegistry(char* firefoxDir)
+  {
+    HKEY key;
+    wchar_t wideGreDir[MAXPATHLEN];
+
+    if (ERROR_SUCCESS !=
+                RegOpenKeyExW(HKEY_LOCAL_MACHINE,
+                              L"SOFTWARE\\Microsoft\\Windows"
+                              L"\\CurrentVersion\\App paths\\firefox.exe",
+                              0,
+                              KEY_READ,
+                              &key)) {
+      return false;
+    }
+
+    DWORD length = MAXPATHLEN * sizeof(wchar_t);
+    // XXX: When Vista/XP64 become our minimum supported client, we can use
+    //      RegGetValue instead
+    if (ERROR_SUCCESS != RegQueryValueExW(key,
+                                          L"Path",
+                                          NULL,
+                                          NULL,
+                                          reinterpret_cast<BYTE*>(wideGreDir),
+                                          &length)) {
+      RegCloseKey(key);
+      return false;
+    };
+    RegCloseKey(key);
+
+    // According to this article, we need to write our own null terminator:
+    // http://msdn.microsoft.com/en-us/library/ms724911%28v=vs.85%29.aspx
+    length = length / sizeof(wchar_t);
+    if (wideGreDir[length] != L'\0') {
+      if (length >= MAXPATHLEN) {
+        return false;
+      }
+      wideGreDir[length] = L'\0';
+    }
+
+    if (0 == WideCharToMultiByte(CP_UTF8,
+                                 0,
+                                 wideGreDir,
+                                 -1,
+                                 firefoxDir,
+                                 MAXPATHLEN,
+                                 NULL,
+                                 NULL)) {
+      return false;
+    }
+
+    return true;
+  }
+};
+
+
+
+//////////////////////////////////////////////////////////////////////////////
+// main
+//
+// Note: XPCOM cannot be used until AttemptGRELoad has returned successfully.
+int
+main(int argc, char* argv[])
+{
+  pargc = &argc;
+  pargv = &argv;
+  nsresult rv;
+  char buffer[MAXPATHLEN];
+  wchar_t wbuffer[MAXPATHLEN];
+
+  // Set up curEXEPath
+  if (!GetModuleFileNameW(0, wbuffer, MAXPATHLEN)) {
+    Output("Couldn't calculate the application directory.");
+    return 255;
+  }
+  wcsncpy(curExePath, wbuffer, MAXPATHLEN);
+
+  // Get the current directory into wbuffer
+  wchar_t* lastSlash = wcsrchr(wbuffer, L'\\');
+  if (!lastSlash) {
+    Output("Application directory format not understood.");
+    return 255;
+  }
+  *(++lastSlash) = L'\0';
+
+  // Set up backup file path
+  if (wcslen(wbuffer) + _countof(kAPP_RT_BACKUP) >= MAXPATHLEN) {
+    Output("Application directory path is too long (couldn't set up backup file path).");
+  }
+  wcsncpy(lastSlash, kAPP_RT_BACKUP, _countof(kAPP_RT_BACKUP));
+  wcsncpy(backupFilePath, wbuffer, MAXPATHLEN);
+
+  *lastSlash = L'\0';
+
+  // Convert current directory to utf8 and stuff it in buffer
+  if (0 == WideCharToMultiByte(CP_UTF8,
+                               0,
+                               wbuffer,
+                               -1,
+                               buffer,
+                               MAXPATHLEN,
+                               NULL,
+                               NULL)) {
+    Output("Application directory could not be processed.");
+    return 255;
+  }
+
+  // Set up appIniPath with path to webapp.ini.
+  // This should be in the same directory as the running executable.
+  char appIniPath[MAXPATHLEN];
+  if (NS_FAILED(joinPath(appIniPath, buffer, kWEBAPP_INI, MAXPATHLEN))) {
+    Output("Path to webapp.ini could not be processed.");
+    return 255;
+  }
+
+  // Open webapp.ini as an INI file (as opposed to using the
+  // XRE webapp.ini-specific processing we do later)
+  nsINIParser parser;
+  if (NS_FAILED(parser.Init(appIniPath))) {
+    Output("Could not open webapp.ini");
+    return 255;
+  }
+
+  // Set up our environment to know where webapp.ini was loaded from.
+  char appEnv[MAXPATHLEN + _countof(kAPP_ENV_PREFIX)];
+  strcpy(appEnv, kAPP_ENV_PREFIX);
+  strcpy(appEnv + _countof(kAPP_ENV_PREFIX) - 1, appIniPath);
+  if (putenv(appEnv)) {
+    Output("Couldn't set up app environment");
+    return 255;
+  }
+
+  // Get profile dir from webapp.ini
+  if (NS_FAILED(parser.GetString("Webapp",
+                                 "Profile",
+                                 profile,
+                                 MAXPATHLEN))) {
+    Output("Unable to retrieve profile from web app INI file");
+    return 255;
+  }
+
+  char firefoxDir[MAXPATHLEN];
+
+  // First attempt at loading Firefox binaries:
+  //   Get the location of Firefox from our webapp.ini
+
+  // XXX: This string better be UTF-8...
+  rv = parser.GetString("WebappRT",
+                        "InstallDir",
+                        firefoxDir,
+                        MAXPATHLEN);
+  if (NS_SUCCEEDED(rv)) {
+    if (AttemptLoadFromDir(firefoxDir)) {
+      return 0;
+    }
+  }
+
+  // Second attempt at loading Firefox binaries:
+  //   Get the location of Firefox from the registry
+  rv = GetFirefoxDirFromRegistry(firefoxDir);
+  if (NS_SUCCEEDED(rv)) {
+    if (AttemptLoadFromDir(firefoxDir)) {
+      // XXX: Write gre dir location to webapp.ini
+      return 0;
+    }
+  }
+
+  // We've done all we know how to do to try to find and launch FF
+  Output("This app requires that Firefox version 14 or above is installed."
+         " Firefox 14+ has not been detected.");
+  return 255;
+}
new file mode 100644
--- /dev/null
+++ b/webapprt/win/webapprt.exe.manifest
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+
+<!-- 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/.  -->
+
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
+<assemblyIdentity
+        version="1.0.0.0"
+        processorArchitecture="*"
+        name="Mozilla.WebAppRT"
+        type="win32"
+/>
+<description>Mozilla Webapp Runtime</description>
+<dependency>
+        <dependentAssembly>
+                <assemblyIdentity
+                        type="win32"
+                        name="Microsoft.Windows.Common-Controls"
+                        version="6.0.0.0"
+                        processorArchitecture="*"
+                        publicKeyToken="6595b64144ccf1df"
+                        language="*"
+                />
+        </dependentAssembly>
+</dependency>
+<ms_asmv3:trustInfo xmlns:ms_asmv3="urn:schemas-microsoft-com:asm.v3">
+  <ms_asmv3:security>
+    <ms_asmv3:requestedPrivileges>
+      <ms_asmv3:requestedExecutionLevel level="asInvoker" uiAccess="false" />
+    </ms_asmv3:requestedPrivileges>
+  </ms_asmv3:security>
+</ms_asmv3:trustInfo>
+  <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
+    <application>
+      <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}" />
+    </application>
+  </compatibility>
+</assembly>
new file mode 100644
--- /dev/null
+++ b/webapprt/win/webapprt.rc
@@ -0,0 +1,6 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+1 24 "webapprt.exe.manifest"
--- a/xpcom/glue/FileUtils.h
+++ b/xpcom/glue/FileUtils.h
@@ -35,16 +35,18 @@
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef mozilla_FileUtils_h
 #define mozilla_FileUtils_h
 
+#include "nscore.h" // nsnull
+
 #if defined(XP_UNIX) || defined(XP_OS2)
 # include <unistd.h>
 #elif defined(XP_WIN)
 # include <io.h>
 #endif
 #include "prio.h"
 
 #include "mozilla/Scoped.h"
--- a/xpfe/appshell/src/nsAppShellService.cpp
+++ b/xpfe/appshell/src/nsAppShellService.cpp
@@ -116,18 +116,32 @@ nsAppShellService::CreateHiddenWindow()
 {
   nsresult rv;
   PRInt32 initialHeight = 100, initialWidth = 100;
     
 #ifdef XP_MACOSX
   PRUint32    chromeMask = 0;
   nsAdoptingCString prefVal =
       Preferences::GetCString("browser.hiddenWindowChromeURL");
-  const char* hiddenWindowURL = prefVal.get() ? prefVal.get() : DEFAULT_HIDDENWINDOW_URL;
-  mApplicationProvidedHiddenWindow = prefVal.get() ? true : false;
+
+  // Set mApplicationProvidedHiddenWindow to true only if there is a hidden
+  // window chrome URL in preferences AND it is set to a non-default value.
+  // This enables an app that doesn't have a hidden window (like WebappRT)
+  // to share an app directory with one that does (like Firefox), the former
+  // taking advantage of this behavior to "unset" the latter's hidden window
+  // pref by setting it to the default value.
+  //
+  // (Ideally, the former would be able to simply unset the latter's pref,
+  // but there is no way to do that; even more ideally, the two apps would not
+  // share an app directory, but in the case of WebappRT and Firefox that's
+  // a longer-term fix.)
+  //
+  mApplicationProvidedHiddenWindow = prefVal.get() && strcmp(prefVal.get(), DEFAULT_HIDDENWINDOW_URL) ? true : false;
+
+  const char* hiddenWindowURL = mApplicationProvidedHiddenWindow ? prefVal.get() : DEFAULT_HIDDENWINDOW_URL;
 #else
   static const char hiddenWindowURL[] = DEFAULT_HIDDENWINDOW_URL;
   PRUint32    chromeMask =  nsIWebBrowserChrome::CHROME_ALL;
 #endif
 
   nsCOMPtr<nsIURI> url;
   rv = NS_NewURI(getter_AddRefs(url), hiddenWindowURL);
   NS_ENSURE_SUCCESS(rv, rv);