Bug 1029674 - Fix installation of apps with custom origin. r=myk,keeler
authorMarco Castelluccio <mar.castelluccio@studenti.unina.it>
Fri, 04 Jul 2014 15:23:16 +0200
changeset 192436 2ec73f026a68e49843701dc92fa423b770171ad4
parent 192435 b7f8089105b216ecfe8167e2a6f5dfd703dab935
child 192437 3a3f72171ac4b57014e338f325ef8d521e9cec82
push id7612
push userttaubert@mozilla.com
push dateSun, 06 Jul 2014 16:17:15 +0000
treeherderfx-team@1dc6b294800d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmyk, keeler
bugs1029674
milestone33.0a1
Bug 1029674 - Fix installation of apps with custom origin. r=myk,keeler
browser/modules/WebappManager.jsm
dom/apps/src/Webapps.jsm
dom/apps/tests/signed/corrupt_app_1.zip
dom/apps/tests/signed/origin_app_1.zip
dom/apps/tests/signed/unknown_issuer_app_1.zip
dom/apps/tests/signed/valid_app_1.zip
dom/apps/tests/signed/valid_app_2.zip
mobile/android/modules/WebappManager.jsm
security/manager/ssl/tests/unit/test_signed_apps/gentestfiles/create_test_files.sh
security/manager/ssl/tests/unit/test_signed_apps/gentestfiles/unsigned_app_origin_toolkit_webapps/index.html
security/manager/ssl/tests/unit/test_signed_apps/gentestfiles/unsigned_app_origin_toolkit_webapps/manifest.webapp
security/manager/ssl/tests/unit/test_signed_apps/trusted_ca1.der
security/manager/ssl/tests/unit/test_signed_apps/unknown_issuer_app_1.zip
security/manager/ssl/tests/unit/test_signed_apps/unsigned_app_1.zip
security/manager/ssl/tests/unit/test_signed_apps/valid_app_1.zip
toolkit/webapps/LinuxNativeApp.js
toolkit/webapps/MacNativeApp.js
toolkit/webapps/NativeApp.jsm
toolkit/webapps/WinNativeApp.js
toolkit/webapps/tests/chrome.ini
toolkit/webapps/tests/data/custom_origin.webapp
toolkit/webapps/tests/data/custom_origin.webapp^headers^
toolkit/webapps/tests/data/custom_origin.zip
toolkit/webapps/tests/head.js
toolkit/webapps/tests/test_custom_origin.xul
toolkit/webapps/tests/test_hosted.xul
toolkit/webapps/tests/test_hosted_icons.xul
toolkit/webapps/tests/test_hosted_launch.xul
toolkit/webapps/tests/test_hosted_launch_no_registry.xul
toolkit/webapps/tests/test_hosted_uninstall.xul
toolkit/webapps/tests/test_hosted_update_from_webapp_runtime.xul
toolkit/webapps/tests/test_packaged.xul
toolkit/webapps/tests/test_packaged_icons.xul
toolkit/webapps/tests/test_packaged_launch.xul
toolkit/webapps/tests/test_packaged_launch_no_registry.xul
toolkit/webapps/tests/test_packaged_uninstall.xul
toolkit/webapps/tests/test_packaged_update_from_webapp_runtime.xul
webapprt/WebappManager.jsm
webapprt/WebappRT.jsm
--- a/browser/modules/WebappManager.jsm
+++ b/browser/modules/WebappManager.jsm
@@ -156,21 +156,21 @@ this.WebappManager = {
         } catch (ex) {
           Cu.reportError("Error installing webapp: " + ex);
           DOMApplicationRegistry.denyInstall(aData);
           cleanup();
           return;
         }
 
         DOMApplicationRegistry.confirmInstall(aData, localDir,
-          (aManifest, aZipPath) => Task.spawn((function*() {
+          (aApp, aManifest, aZipPath) => Task.spawn((function*() {
             try {
-              yield nativeApp.install(aManifest, aZipPath);
+              yield nativeApp.install(aApp, aManifest, aZipPath);
               yield this.installations[manifestURL].promise;
-              notifyInstallSuccess(aData.app, nativeApp, bundle);
+              notifyInstallSuccess(aApp, nativeApp, bundle);
             } catch (ex) {
               Cu.reportError("Error installing webapp: " + ex);
               // TODO: Notify user that the installation has failed
             } finally {
               cleanup();
             }
           }).bind(this))
         );
--- a/dom/apps/src/Webapps.jsm
+++ b/dom/apps/src/Webapps.jsm
@@ -2563,17 +2563,17 @@ this.DOMApplicationRegistry = {
       // the installing page about the successful install, after which it'll
       // respond Webapps:Install:Return:Ack, which calls onInstallSuccessAck.
       this.broadcastMessage("Webapps:Install:Return:OK", aData);
     }
 
     if (!aData.isPackage) {
       this.updateAppHandlers(null, app.manifest, app);
       if (aInstallSuccessCallback) {
-        aInstallSuccessCallback(app.manifest);
+        aInstallSuccessCallback(app, app.manifest);
       }
     }
 
     Services.obs.notifyObservers(null, "webapps-installed",
       JSON.stringify({ manifestURL: app.manifestURL }));
 
     if (aData.forceSuccessAck) {
       // If it's a local install, there's no content process so just
@@ -2652,17 +2652,17 @@ this.DOMApplicationRegistry = {
     yield ScriptPreloader.preload(aNewApp, aManifest);
 
     this.broadcastMessage("Webapps:FireEvent", {
       eventType: ["downloadsuccess", "downloadapplied"],
       manifestURL: aNewApp.manifestURL
     });
 
     if (aInstallSuccessCallback) {
-      aInstallSuccessCallback(aManifest, zipFile.path);
+      aInstallSuccessCallback(aNewApp, aManifest, zipFile.path);
     }
   }),
 
   _nextLocalId: function() {
     let id = Services.prefs.getIntPref("dom.mozApps.maxLocalId") + 1;
 
     while (this.getManifestURLByLocalId(id)) {
       id++;
index aac6bb860eea838d85a362385c1939acfd3517e4..e767c316b01a450b9ea9d72629149d185cffed31
GIT binary patch
literal 4591
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
index ddee95ecedcaad0b8c6972232ac2d559bd210b79..d907597eb6a6a772eb305af7f14db58c2ba9ec54
GIT binary patch
literal 4248
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
Y|57mO%J>AoK3~0fzFjy?_%qpm0mg(^bN~PV
index 15728f23f55cdad94c5c60b990dcb54ce33e2c75..374e45f6f83d20a0df2b0b64efa906b7f70a84b3
GIT binary patch
literal 4220
zc$|$_2T+tt65f?8APADPAUP~KCkYRhUBV(FNsr8uVTrQjph#GF1PPKQgXAcJ1ed(z
zCFd*(D3S!>@LgSbQupd^rh2OS|Es_5ndzCYM^_66_YMF6AOs+y5>(r7m9!7w0RZOs
z000Sq767+&aTX93krH%uwl#Qw2f*1*g6Vtu5Ced?TQ~r~UqktWQ^j###tURyEZP9$
zR*P+{Fx7MyhoCdiOqOw;q?+y=e~3<XJc_A|Fk)M$$3oxTRfm~i@@E)h>>GIcSq;@=
zGcLmCe=PfEP96jV_*{N%S(KJC^b<D6GY15v<b{^Y>hvxK1^IqjX){7oa%1M1anjV=
z(l_ZYT+_!S0JShRFAULs%di^3q7;B*wGC(y&T|jQTDfiKu$p@3JrDaSX|`&G$;hoT
zW8<|E1%{_{=^=lRfNqb?n$jCkWFpk-z728b?u7f5B-CK}gJ#$k9=ktRYG*K@%|?!?
zv|8aTEybi&!+_aJXtwwlC&yJM{M+^@sz@rzsa@NM2tOhI3Qq>}exd-aa{<Nmk!CO5
zRvvYtqTAzYG(lM}sYu{qy~*7T24(N~`VNKqXBj$O{4$=wf;M@1CeEmLo;~|kA~A<l
zjEkkr)!bv%(jxI*dEfEy$pvLnFNFUTG(CLZWl^Vwy)2RTh_y1%Or#+`Y|!9nnA|nD
zi!j<x#}i#IrZT0#R#84{Pvni8`aKIV0zIz|=LF<YrBjjUE`RJXi3)N%AY+f`iEE~G
zQt*&;^KN+9z?DSLNU@8<8$oeESzhR&9G^YmoKYmb6_=<l2yCpdfu`zFk!TJzI`-&v
z%ddNe$&^jF1~1j*glgc|96EcpXDJc8wPR+~p|)A=z7IpYIC;{jU4$0$@5N{r20K!|
zny7FB^<}zOr`(m!d!MD$xgn8Y*;oqhDlZm%YvxL&D1(f1_unv}WK73o*C}a`HqtK5
zG93%stFG}TaunQw_^MQm)1nxs37zLeD~x<5oZY9y!}R<Og3D4Ttdl355?hH<G8+A(
z?l)+|n(X4L3eNQ#lnwKnta60mEc)0|EoQ$~?23b0L)}inn3XzhGSFt97|z)Ivg4Tp
zeXPrB&!V<lRq6*ZNfiJM{a5)hGgdktf4755MllG|@<Heff-Ub7iFzVqB(n2yi?m3G
zk$#|AF^vr%`*x5;vWDZew~L!~#w^X7?V$q2p>4xVM#D)g`m6|5Xq0HDVPIw}Y1Pn#
zw(RX=#DvUG_4fbK^?1}QtC;j-!*1KbWw`{Ffj>Y&JnN)qP=fHZ(aV>19dB*zw2GB&
zgzqj|4wegT-r4lnmuI^_YyN)q{fkZU32rX$SXJ_c)rgn+AI>7_Zts({%d(R)RePyU
z{IJ09DogW0-l1(im2LZ!8}Tvq6*gw|9|>EVmewa_{V&qDf{J5>5&)>k0}{5SMG0=Q
zA1te6*>4((GO3<g2IHtnE3ekbpy8=eYugUpwWlecLcBR*kmQMI<fQoUe%@ClWaU%I
zCmF3qA52=FKj7*;$l*yfdr$T8or`H7Bi_xoDn8i!Jf^%8HXi)V1@?FAtD`x+ooOkP
z)ezY~-I_xVY3{!nG{%u)2(&f2(T4kUlYfYID+ZoLD;yLGHq0%Nd__9)Hm$5MX@=4=
z)8d<wg6lI6Yf$NmW}*^UO=<06hfpxcZmXXykg#?5X>Fc=$)9{U<g-4iy(NayHGlE}
zx+2+xJo7R-=S;xQJr*m!WO&BCTfp#ifgE^`pCamQL?v$AohS-}a(m7s<v)>K#$V$#
zI<$L3eZx`t3$ZPSaW<>1@OQ%~o3@b`Brhvsj;JSN>oQ1L#Yz){;@F30QuPaoUd!>c
z^rgIhs5~Q`-=MCbV{(=f^<1azSTB2ZyzP-S{_+M(Wyzdfc<@|mDH$}1IOot8$8D}%
z3SZOyjP*wlsDD-!+w40;(J@syHX)UCWXMeZvfI9M8Tr~UH!9jp9JX;vk+kQMklB>+
zmSS~F{+?RLbCBEtYm=N(eztaCIXOmQ@!p49i4=@_&{Q9D3NDCrn^9_fZz~)x9&vY&
z&g-1Z5rI;+BB1<$`y5%)nd3@f94YuwN~|;&1AioSqdtMMgj1=ti15VavK|0lPiUbU
zP$z;0tR2!xS(rJcgOuqm0lL4j!KzRg*yTz?UCGU#Y~XBV?IURC;pF&tT%ZQkx)};0
zpC?{0mkS!Kk8*o2T1%6X2B*~UC|d29m5d45U-}Xt_vu_@`pu1Y_uTq+MU|CP(H_&f
zO0EG568)t;UJ1ve-Muhs0U~(|EC;A0SC-MkQ@_6A!(JG8h}@9-AB{7T9^!)H-T@7R
zWWR^IKCY8Ebfw(`0sv%J;X0W+!)>e)9)jN17Ur(5e;2cS+>m`Ihy=aodou^IGn@%_
z_77p|+cJ;NmbPCV3{+k7svPysVnS`R#(g_e;E)Hf<Dr53Yau=nd4ygJ?;|G!wX3zN
zv{Cz#fwyB_H&B7YV*)e2clr=`q7K*qa1Ph)&nCsY^uF|3-CKokL~QlP@A?3Cj|jqY
z>gw}B6cvO+NiWiR{g|}Ciyv01U+X?_2|{3URTc@1Tcx77`Z-^-Y|7J_WJ6VW<3D|Z
z%v(t%D*04ky&u;yH8>nU-r6|WO1Rgi5Vm@B7&c#XhWBetMp|c7Qw}zF6L0~5$}3X8
zb@dgh_CQ%cQ(Ij~SrDeLENW+A)`>2j*cds=cp0AoSEk%C4duk9LZYK1=I~!Rmdzjs
zAh0qL{5e9kXyUY#bkekxohuoo0f`EW0ONr|ra*Q{U4QyVvIuVCbdbIsD7uqvVC|C~
zw+-u!l}h4+?;9^Sk~nNOc*3j;k{SBwHA1_Z!qiOpC=kT%?i5t74x(;;jiS&JIXEB*
zC&g7B9q2I4^tFzZPUX3YiN$wcX^G&(w^6rIk0{_obLy?MpnFV1Nj|<+8CRlRxy9Tl
z3yT7iq|s8v;RMGg1vf>;zXWn)+Q++kdMfwiD^QWQI&+u)Xk)G`mvM9rOd2WmY{(yD
z=5mjcuX~!zK%X-&vU#DJv<MCiH8i_CKDf+h*z_uk?}wkj7$xF+{adp?LiFH#vxoY}
z0dRP+!dLmv!}p5^wF*j{6g@c4RNJs&XW(|S3jS*8OZKC|>VYi1Tm;!$WDLzTMT<e`
zrF&Ft_UYx@L#<0lI`7ovR*Plc@ROakgFH?-U(bCb&#~fKp5rs;tl1wEwZqQ$e-!t@
zq4kIpPU^3M_D&h!_qKyIiFJ}7MEVH=$o=;9>}jjXRUQ9YD_Jp>>VC_YpLgG$O||E0
zyt_nZ)m>cn;t>VzmLt866bkkJwCq}dDvnQ!k=?^sBhiC#N3SB|eXGKQQ-NfleXDRf
zy2~069f<aMuulmqGYUw<@Jxb%osUmev<Aqc@rs25(=pJs5R}}Kq70ll*0FClVZot}
z9XuUqCE)s0FXd6S{2aZUn~S1u4o4-QaX23FW73eu{@8}}3E5HNTL)%M@8o+o7Rpay
zna`Y=S`A&sT?#0V8?9~D&&LRuogGaJKFioPIh@f<4-PRctYs|o86KaCCG2^fi~rGI
z0$#S>7<dxpX@fC+>~PmZgB3}Q*sXeY5SnUrB%nxhxc!lM@DOD?z8(HxLO{_<KiRgz
zNjPEC)xIHE>pZ5=v>!Kl?V?qd?S9MQ=MpMv3gUq2TGLo<cW(WuC%x=mZL>}nhvoJr
z2}c*hk3`$|mQ*4KWTVL5l<B4T%5&xz)vpbt(}ff4{<!LbOXzPSsz+LVtw~Q0-9wV-
z3nHM=EWyP)0-y?dt1-UL;A)xNM~}!ISb->7Z5x#}SceimTc>>wDp`j#@pd(Tga!ZI
z647+QH#$=Rfi?r4>+7GPg%u0(l2ihURbs*-DS{3&M&|WpU&kZM_cm~kw`A)1CQYZS
zC!OO1LNOcNT$|0AZjaAXZ6A7FtZ$ujBuR&;(U}|?MwXq{48SF4LjvPR6gixjg=gpN
zMT})GRVG_2Ehy2UZ@#ysue`RsEJ<@4m3MJDD^<x=qxr_^R4Hn*>sNm|o?K4xM23Qs
z#i+)k^ow5(rRSNuYVvutl?CQtnlhpJi-To|t@S&rh$@@y!tUsjGRINL?}BS`s4RX<
zV?BekFPhss(U2P1x;9cTIkH~e<b9tEQ)#D%)+gGTjOmNMJSgl*OC-1{Vd3H%_F;uS
z*#vPvbuB(td*+LMFwUjNp16)g{k+`ltHTb1!;n2`Iz;}EH=_Oq;P>n6(C_IW_)7aJ
zq`zNZ`s&0O$dDF4j{tarR7-#tGcZ&G1C5I=-8^y?8~5ym=m-do96|^BAvhvscNp0j
z*^xIHYoNR^72;^Eyy#buLmc^Ud@4{mr>1L8Ka^V%{p685S3Gj(Kb=CAwKdh%^dAU9
z)eSKHu^}Kp__DlC`+{yU`wW|Tj-{RrEQCg^kq-u@gS#~r$^B>qaeNv&-X!N$>noX&
zs%`^w!*|G2(l*9IrkfkN;qQrV<gd*H)C8fy`;${x1JzI=WJmF=f6qOUA47Wkb5`67
zYT6an9n7qyQ9Hxb9Qzvrs-OEhn4fuTk9f{wYh2_$JAG-z+vP8?mL@sZp6r%$PV42~
zyQS%+4edAfdaX<|N<gT<Pcr1)eYaTYF^@C^w?WvTp@^6Vv58FYM$PMFj8bNW#~zXW
zz5uf5Z#4I>G?gp4{htc}w?YWoBV3$IH(<KDeGt8Bm@XJx0}|F2fsBdkK(w&p!Xg@@
zP)NPTh`1J5Oh*DNRxKtX0viPu3;k=0aO_3aO4fgFGw=p8&fh;g|J`bXe~$mpGxR#*
z_2T7c1OA10>&nOfE@iIcU9T>F;k~=U`#)=qpP+Ye{+^!y-GYE41lP&>y6yF|@yj-j
w<l0HOZhAe#f0^Qu{`Y*pj&MCiej(uB`Y#EitA&UE^Ebg&-@9_^{nupw35=#to&W#<
index 4cff6e9f269a7491845ef3b2253fcc109f553840..74a35a4208dcee164af51abcdd5715f3d90a67ea
GIT binary patch
literal 4222
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
BRqFr%
index 484cf04b3bf92a3392cb1273da12e295e3efc06e..950255b4928f4a410885a55ff113570f721032bd
GIT binary patch
literal 4220
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
C2S&jF
--- a/mobile/android/modules/WebappManager.jsm
+++ b/mobile/android/modules/WebappManager.jsm
@@ -173,17 +173,17 @@ this.WebappManager = {
 
   askInstall: function(aData) {
     let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile);
     file.initWithPath(aData.profilePath);
 
     this._deleteAppcachePath(aData.app.manifest);
 
     DOMApplicationRegistry.registryReady.then(() => {
-      DOMApplicationRegistry.confirmInstall(aData, file, (function(aManifest) {
+      DOMApplicationRegistry.confirmInstall(aData, file, (function(aApp, aManifest) {
         this._postInstall(aData.profilePath, aManifest, aData.app.origin, aData.app.apkPackageName);
       }).bind(this));
     });
   },
 
   _postInstall: function(aProfilePath, aNewManifest, aOrigin, aApkPackageName) {
     // aOrigin may now point to the app: url that hosts this app.
     sendMessageToJava({
old mode 100644
new mode 100755
--- a/security/manager/ssl/tests/unit/test_signed_apps/gentestfiles/create_test_files.sh
+++ b/security/manager/ssl/tests/unit/test_signed_apps/gentestfiles/create_test_files.sh
@@ -1,19 +1,20 @@
 #!/bin/bash
 
 export NSS_DEFAULT_DB_TYPE=sql
 
 export BASE_PATH=`dirname $0`
 export SIGN_SCR_LOC=.
 export APPS_TEST_LOC=../../../../../../../dom/apps/tests/signed
+export TOOLKIT_WEBAPPS_TEST_LOC=../../../../../../../toolkit/webapps/tests/data/
 
 # Creates the entry zip files (unsigned apps) from the source directories
 packageApps() {
-APPS="unsigned_app_1 unsigned_app_origin"
+APPS="unsigned_app_1 unsigned_app_origin unsigned_app_origin_toolkit_webapps"
 OLD_PWD=`pwd`
 cd ${BASE_PATH}
 for i in $APPS
 do
   echo "Creating $i.zip"
   cd $i && zip -r ../$i.zip . && cd ..
 done
 cd ${OLD_PWD}
@@ -164,17 +165,23 @@ signApp $DB_PATH ${BASE_PATH}/unsigned_a
         $TEST_APP_PATH/unknown_issuer_app_1.zip \
         $INVALID_UID 1 ${UNTRUSTED_EE}
 
 # And finally a priviledged signed file that includes the origin on the manifest
 # to avoid that reverting again
 PRIV_UID=`uuidgen`
 signApp $DB_PATH ${BASE_PATH}/unsigned_app_origin.zip \
         $TEST_APP_PATH/origin_app_1.zip \
-        $INVALID_UID 1 ${TRUSTED_EE}
+        $PRIV_UID 1 ${TRUSTED_EE}
+
+# A privileged signed app needed for a toolkit/webapps test
+PRIV_TOOLKIT_UID=`uuidgen`
+signApp $DB_PATH ${BASE_PATH}/unsigned_app_origin_toolkit_webapps.zip \
+        $TEST_APP_PATH/custom_origin.zip \
+        $PRIV_TOOLKIT_UID 1 ${TRUSTED_EE}
 
 # Now let's copy the trusted cert to the app directory so we have everything
 # on the same place...
 cp ${DB_PATH}/${TRUSTED_CA}.der ${TEST_APP_PATH}
 
 cat <<EOF
 
 All done. The new test files are in ${TEST_APP_PATH}. You should copy the
@@ -187,18 +194,20 @@ EOF
 echo "Do you wish me to do that for you now?"
 select answer in "Yes" "No"
 do
   case $answer in
     Yes) break;;
     No) echo "Ok, not installing the new files"
         echo "You should run: "
         echo cp ${TEST_APP_PATH}/* ${BASE_PATH}/${APPS_TEST_LOC}
-        echo cp ${TEST_APP_PATH}/*  ${TEST_APP_PATH}/../unsigned_app_1.zip ${BASE_PATH}/..
+        echo cp ${TEST_APP_PATH}/* ${TEST_APP_PATH}/../unsigned_app_1.zip ${BASE_PATH}/..
+        echo cp ${TEST_APP_PATH}/* ${BASE_PATH}/${TOOLKIT_WEBAPPS_TEST_LOC}
         echo "to install them"
         exit 0;;
   esac
 done
 
 cp ${TEST_APP_PATH}/* ${BASE_PATH}/${APPS_TEST_LOC}
 cp ${TEST_APP_PATH}/* ${TEST_APP_PATH}/../unsigned_app_1.zip ${BASE_PATH}/..
+cp ${TEST_APP_PATH}/* ${BASE_PATH}/${TOOLKIT_WEBAPPS_TEST_LOC}
 
 echo "Done!"
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_signed_apps/gentestfiles/unsigned_app_origin_toolkit_webapps/index.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Test app</title>
+</head>
+<body>
+Test app:
+<iframe src="http://127.0.0.1:8888/chrome/toolkit/webapps/tests/app.sjs?appreq"></iframe>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_signed_apps/gentestfiles/unsigned_app_origin_toolkit_webapps/manifest.webapp
@@ -0,0 +1,9 @@
+{
+  "name": "Custom Origin Test",
+  "version": 1,
+  "size": 777,
+  "package_path": "custom_origin.zip",
+  "launch_path": "/index.html",
+  "origin": "app://test.origin.privileged.app",
+  "type": "privileged"
+}
index 261f4b5bcc26c54dc67040f5dce346f9090c9fce..0200a7f35d1c8344a816e4109bd9cce07327f21f
GIT binary patch
literal 898
zc$_n6Vy-i2VoF=U%*4pV#K>sC%f_kI=F#?@mywa1mBApzklTQhjX9KsO_(V(7{=ic
zW^#5k6f+P3aoB}<ee+B6N)j{k6v8r7%ME1>q(S0b!XmB}iMa(iIf)9+`9%f!MTsSu
z`FVzd2K*ptW?_!7#GK3&1!qSC1#w<O69Y3NGb2M&6Jw(&34S93Q)B@;xuc0u0XYB|
zSs9p{7<n0h;v7s(jEoGMwtlMl`htV|_$rgdt|xuEyb?-gygPF$FPKA`VeZX$OF~z1
z$?yKS)kSMYtmJn0D<620Wt6-^+FW~PKD%NfBNJ+{{X@l-DZhMISM%g&c09czqFYh?
zYwml;u;<$rzE76W*AKJhZS~Li^(gn$^yW>I9xU9uqrhlk*IKnHhc_4N`lKCy|LAz4
z)9JH+inNkB%wtuzPyC**P=0A)<-6GL4&iQp1NcJQUKbu{maOQ_neq08%%rC_+TZug
zy!A`FMJ7|Seqw$C?}Q__cjn8Bxd|s4d-9eQPJ9@akfy7D<?FIdMPA*d%vT)dM5Rpp
z;1l@lR@%vp42z%CYD;K$?O~bcVt4AwM<zz*#nuLv;DD1AW@P-&!fL<_qzuGB0;(VZ
z9s@2m4sA9@R#tXqW&=KuI6p|7g_((o1ub<kgTi!X*_=h0{e3<2>bv8<EJ#S+ShTw1
z%kuN{?j37czT2lI{QIJ?nUzZJPRnF$0<tF=-c6gm>(-&fKPRnvS1+BQdgWN^EbenB
zWY3nCrMqy=KGu}_+9qVG=6r4bGO0q9qbyz5msxDTA+dIg;7%8-&WGDe{gxa!sCBz9
z<>sEeWv3pil(wC;_GtJe^5)&+mXDkp^wt|Zp7~s_Q#59m(F^tXXGJHxd0#V!?~49o
zw&p|G`S`t&&oi%vDZH>3nlf?Ai{H7n`-BoUUg16YqLh8oMq7@Br%Ts9oWay%T-G(?
uz$ccQOj_FO&KYEJY!q#e{=HzQMZJjkX}xLJznY1&wodl+{dvvfh6n&%l}^?G
index 15728f23f55cdad94c5c60b990dcb54ce33e2c75..374e45f6f83d20a0df2b0b64efa906b7f70a84b3
GIT binary patch
literal 4220
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
index d534fc138718e15e6eb9a68ec848c4cc7845ed2b..731e050aba5e5b175cea6f4393e4d6541bcb382b
GIT binary patch
literal 2282
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
GBl`{X2l7e)
index 4cff6e9f269a7491845ef3b2253fcc109f553840..74a35a4208dcee164af51abcdd5715f3d90a67ea
GIT binary patch
literal 4222
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
BRqFr%
--- a/toolkit/webapps/LinuxNativeApp.js
+++ b/toolkit/webapps/LinuxNativeApp.js
@@ -42,27 +42,27 @@ NativeApp.prototype = {
 
   /**
    * Creates a native installation of the web app in the OS
    *
    * @param aManifest {Object} the manifest data provided by the web app
    * @param aZipPath {String} path to the zip file for packaged apps (undefined
    *                          for hosted apps)
    */
-  install: Task.async(function*(aManifest, aZipPath) {
+  install: Task.async(function*(aApp, aManifest, aZipPath) {
     if (this._dryRun) {
       return;
     }
 
     // If the application is already installed, this is a reinstallation.
-    if (WebappOSUtils.getInstallPath(this.app)) {
-      return yield this.prepareUpdate(aManifest, aZipPath);
+    if (WebappOSUtils.getInstallPath(aApp)) {
+      return yield this.prepareUpdate(aApp, aManifest, aZipPath);
     }
 
-    this._setData(aManifest);
+    this._setData(aApp, aManifest);
 
     // The installation directory name is: sanitized app name +
     // "-" + manifest url hash.
     let installDir = OS.Path.join(HOME_DIR, "." + this.uniqueName);
 
     let dir = getFile(TMP_DIR, this.uniqueName);
     dir.createUnique(Ci.nsIFile.DIRECTORY_TYPE, PERMS_DIRECTORY);
     let tmpDir = dir.path;
@@ -96,24 +96,24 @@ NativeApp.prototype = {
 
   /**
    * Creates an update in a temporary directory to be applied later.
    *
    * @param aManifest {Object} the manifest data provided by the web app
    * @param aZipPath {String} path to the zip file for packaged apps (undefined
    *                          for hosted apps)
    */
-  prepareUpdate: Task.async(function*(aManifest, aZipPath) {
+  prepareUpdate: Task.async(function*(aApp, aManifest, aZipPath) {
     if (this._dryRun) {
       return;
     }
 
-    this._setData(aManifest);
+    this._setData(aApp, aManifest);
 
-    let installDir = WebappOSUtils.getInstallPath(this.app);
+    let installDir = WebappOSUtils.getInstallPath(aApp);
     if (!installDir) {
       throw ERR_NOT_INSTALLED;
     }
 
     let baseName = OS.Path.basename(installDir)
     let oldUniqueName = baseName.substring(1, baseName.length);
     if (this.uniqueName != oldUniqueName) {
       // Bug 919799: If the app is still in the registry, migrate its data to
@@ -137,22 +137,22 @@ NativeApp.prototype = {
       yield OS.File.removeDir(updateDir, { ignoreAbsent: true });
       throw ex;
     }
   }),
 
   /**
    * Applies an update.
    */
-  applyUpdate: Task.async(function*() {
+  applyUpdate: Task.async(function*(aApp) {
     if (this._dryRun) {
       return;
     }
 
-    let installDir = WebappOSUtils.getInstallPath(this.app);
+    let installDir = WebappOSUtils.getInstallPath(aApp);
     let updateDir = OS.Path.join(installDir, "update");
 
     let backupDir = yield this._backupInstallation(installDir);
 
     try {
       yield this._applyTempInstallation(updateDir, installDir);
     } catch (ex) {
       yield this._restoreInstallation(backupDir, installDir);
--- a/toolkit/webapps/MacNativeApp.js
+++ b/toolkit/webapps/MacNativeApp.js
@@ -40,27 +40,27 @@ NativeApp.prototype = {
 
   /**
    * Creates a native installation of the web app in the OS
    *
    * @param aManifest {Object} the manifest data provided by the web app
    * @param aZipPath {String} path to the zip file for packaged apps (undefined
    *                          for hosted apps)
    */
-  install: Task.async(function*(aManifest, aZipPath) {
+  install: Task.async(function*(aApp, aManifest, aZipPath) {
     if (this._dryRun) {
       return;
     }
 
     // If the application is already installed, this is a reinstallation.
-    if (WebappOSUtils.getInstallPath(this.app)) {
-      return yield this.prepareUpdate(aManifest, aZipPath);
+    if (WebappOSUtils.getInstallPath(aApp)) {
+      return yield this.prepareUpdate(aApp, aManifest, aZipPath);
     }
 
-    this._setData(aManifest);
+    this._setData(aApp, aManifest);
 
     let localAppDir = getFile(this._rootInstallDir);
     if (!localAppDir.isWritable()) {
       throw("Not enough privileges to install apps");
     }
 
     let destinationName = yield getAvailableFileName([ this._rootInstallDir ],
                                                      this.appNameAsFilename,
@@ -101,24 +101,24 @@ NativeApp.prototype = {
 
   /**
    * Creates an update in a temporary directory to be applied later.
    *
    * @param aManifest {Object} the manifest data provided by the web app
    * @param aZipPath {String} path to the zip file for packaged apps (undefined
    *                          for hosted apps)
    */
-  prepareUpdate: Task.async(function*(aManifest, aZipPath) {
+  prepareUpdate: Task.async(function*(aApp, aManifest, aZipPath) {
     if (this._dryRun) {
       return;
     }
 
-    this._setData(aManifest);
+    this._setData(aApp, aManifest);
 
-    let [ oldUniqueName, installDir ] = WebappOSUtils.getLaunchTarget(this.app);
+    let [ oldUniqueName, installDir ] = WebappOSUtils.getLaunchTarget(aApp);
     if (!installDir) {
       throw ERR_NOT_INSTALLED;
     }
 
     if (this.uniqueName != oldUniqueName) {
       // Bug 919799: If the app is still in the registry, migrate its data to
       // the new format.
       throw ERR_UPDATES_UNSUPPORTED_OLD_NAMING_SCHEME;
@@ -142,22 +142,22 @@ NativeApp.prototype = {
       yield OS.File.removeDir(updateDir, { ignoreAbsent: true });
       throw ex;
     }
   }),
 
   /**
    * Applies an update.
    */
-  applyUpdate: Task.async(function*() {
+  applyUpdate: Task.async(function*(aApp) {
     if (this._dryRun) {
       return;
     }
 
-    let installDir = WebappOSUtils.getInstallPath(this.app);
+    let installDir = WebappOSUtils.getInstallPath(aApp);
     let updateDir = OS.Path.join(installDir, "update");
 
     let backupDir = yield this._backupInstallation(installDir);
 
     try {
       // Move the update directory to the /Applications directory
       yield this._applyTempInstallation(updateDir, installDir);
     } catch (ex) {
--- a/toolkit/webapps/NativeApp.jsm
+++ b/toolkit/webapps/NativeApp.jsm
@@ -65,18 +65,16 @@ function CommonNativeApp(aApp, aManifest
   if (aApp.updateManifest) {
     this.isPackaged = true;
   }
 
   this.categories = aCategories.slice(0);
 
   this.registryDir = aRegistryDir || OS.Constants.Path.profileDir;
 
-  this.app = aApp;
-
   this._dryRun = false;
   try {
     if (Services.prefs.getBoolPref("browser.mozApps.installer.dry_run")) {
       this._dryRun = true;
     }
   } catch (ex) {}
 }
 
@@ -95,19 +93,19 @@ CommonNativeApp.prototype = {
 
   /**
    * This function reads and parses the data from the app
    * manifest and stores it in the NativeApp object.
    *
    * @param aManifest {Object} the manifest data provided by the web app
    *
    */
-  _setData: function(aManifest) {
-    let manifest = new ManifestHelper(aManifest, this.app.origin);
-    let origin = Services.io.newURI(this.app.origin, null, null);
+  _setData: function(aApp, aManifest) {
+    let manifest = new ManifestHelper(aManifest, aApp.origin);
+    let origin = Services.io.newURI(aApp.origin, null, null);
 
     this.iconURI = Services.io.newURI(manifest.biggestIconURL || DEFAULT_ICON_URL,
                                       null, null);
 
     if (manifest.developer) {
       if (manifest.developer.name) {
         let devName = manifest.developer.name.substr(0, 128);
         if (devName) {
@@ -135,35 +133,35 @@ CommonNativeApp.prototype = {
     }
 
     this.webappJson = {
       // The app registry is the Firefox profile from which the app
       // was installed.
       "registryDir": this.registryDir,
       "app": {
         "manifest": aManifest,
-        "origin": this.app.origin,
-        "manifestURL": this.app.manifestURL,
-        "installOrigin": this.app.installOrigin,
+        "origin": aApp.origin,
+        "manifestURL": aApp.manifestURL,
+        "installOrigin": aApp.installOrigin,
         "categories": this.categories,
-        "receipts": this.app.receipts,
-        "installTime": this.app.installTime,
+        "receipts": aApp.receipts,
+        "installTime": aApp.installTime,
       }
     };
 
-    if (this.app.etag) {
-      this.webappJson.app.etag = this.app.etag;
+    if (aApp.etag) {
+      this.webappJson.app.etag = aApp.etag;
     }
 
-    if (this.app.packageEtag) {
-      this.webappJson.app.packageEtag = this.app.packageEtag;
+    if (aApp.packageEtag) {
+      this.webappJson.app.packageEtag = aApp.packageEtag;
     }
 
-    if (this.app.updateManifest) {
-      this.webappJson.app.updateManifest = this.app.updateManifest;
+    if (aApp.updateManifest) {
+      this.webappJson.app.updateManifest = aApp.updateManifest;
     }
 
     this.runtimeFolder = OS.Constants.Path.libDir;
   },
 
   /**
    * This function retrieves the icon for an app.
    * If the retrieving fails, it uses the default chrome icon.
--- a/toolkit/webapps/WinNativeApp.js
+++ b/toolkit/webapps/WinNativeApp.js
@@ -67,27 +67,27 @@ NativeApp.prototype = {
 
   /**
    * Creates a native installation of the web app in the OS
    *
    * @param aManifest {Object} the manifest data provided by the web app
    * @param aZipPath {String} path to the zip file for packaged apps (undefined
    *                          for hosted apps)
    */
-  install: Task.async(function*(aManifest, aZipPath) {
+  install: Task.async(function*(aApp, aManifest, aZipPath) {
     if (this._dryRun) {
       return;
     }
 
     // If the application is already installed, this is a reinstallation.
-    if (WebappOSUtils.getInstallPath(this.app)) {
-      return yield this.prepareUpdate(aManifest, aZipPath);
+    if (WebappOSUtils.getInstallPath(aApp)) {
+      return yield this.prepareUpdate(aApp, aManifest, aZipPath);
     }
 
-    this._setData(aManifest);
+    this._setData(aApp, aManifest);
 
     let installDir = OS.Path.join(APP_DATA_DIR, this.uniqueName);
 
     // Create a temporary installation directory.
     let dir = getFile(TMP_DIR, this.uniqueName);
     dir.createUnique(Ci.nsIFile.DIRECTORY_TYPE, PERMS_DIRECTORY);
     let tmpDir = dir.path;
 
@@ -123,24 +123,24 @@ NativeApp.prototype = {
 
   /**
    * Creates an update in a temporary directory to be applied later.
    *
    * @param aManifest {Object} the manifest data provided by the web app
    * @param aZipPath {String} path to the zip file for packaged apps (undefined
    *                          for hosted apps)
    */
-  prepareUpdate: Task.async(function*(aManifest, aZipPath) {
+  prepareUpdate: Task.async(function*(aApp, aManifest, aZipPath) {
     if (this._dryRun) {
       return;
     }
 
-    this._setData(aManifest);
+    this._setData(aApp, aManifest);
 
-    let installDir = WebappOSUtils.getInstallPath(this.app);
+    let installDir = WebappOSUtils.getInstallPath(aApp);
     if (!installDir) {
       throw ERR_NOT_INSTALLED;
     }
 
     if (this.uniqueName != OS.Path.basename(installDir)) {
       // Bug 919799: If the app is still in the registry, migrate its data to
       // the new format.
       throw ERR_UPDATES_UNSUPPORTED_OLD_NAMING_SCHEME;
@@ -166,22 +166,22 @@ NativeApp.prototype = {
       yield OS.File.removeDir(updateDir, { ignoreAbsent: true });
       throw ex;
     }
   }),
 
   /**
    * Applies an update.
    */
-  applyUpdate: Task.async(function*() {
+  applyUpdate: Task.async(function*(aApp) {
     if (this._dryRun) {
       return;
     }
 
-    let installDir = WebappOSUtils.getInstallPath(this.app);
+    let installDir = WebappOSUtils.getInstallPath(aApp);
     let updateDir = OS.Path.join(installDir, "update");
 
     yield this._getShortcutName(installDir);
 
     let backupDir = yield this._backupInstallation(installDir);
 
     try {
       yield this._applyTempInstallation(updateDir, installDir);
--- a/toolkit/webapps/tests/chrome.ini
+++ b/toolkit/webapps/tests/chrome.ini
@@ -1,16 +1,17 @@
 [DEFAULT]
 skip-if = os == "mac" && os_version == "10.6" # see bug 993690
 support-files =
   head.js
   app.sjs
-  data/app/index.html
-  data/icon.png
+  data/*
 
+[test_custom_origin.xul]
+skip-if = asan
 [test_hosted.xul]
 [test_packaged.xul]
 [test_hosted_launch.xul]
 skip-if = asan
 [test_hosted_launch_no_registry.xul]
 skip-if = asan
 [test_packaged_launch.xul]
 skip-if = asan
new file mode 100644
--- /dev/null
+++ b/toolkit/webapps/tests/data/custom_origin.webapp
@@ -0,0 +1,9 @@
+{
+  "name": "Custom Origin Test",
+  "version": 1,
+  "size": 777,
+  "package_path": "custom_origin.zip",
+  "launch_path": "/index.html",
+  "origin": "app://test.origin.privileged.app",
+  "type": "privileged"
+}
new file mode 100644
--- /dev/null
+++ b/toolkit/webapps/tests/data/custom_origin.webapp^headers^
@@ -0,0 +1,1 @@
+Content-Type: application/x-web-app-manifest+json
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..94d0d634548ce30242267538cbad0b135678d773
GIT binary patch
literal 2512
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
GVgCf_@G7<d
--- a/toolkit/webapps/tests/head.js
+++ b/toolkit/webapps/tests/head.js
@@ -2,16 +2,17 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
 
 Cu.import("resource://gre/modules/osfile.jsm");
 Cu.import("resource://gre/modules/Task.jsm");
 Cu.import("resource://gre/modules/Promise.jsm");
 Cu.import("resource://gre/modules/WebappOSUtils.jsm");
+Cu.import("resource://gre/modules/NativeApp.jsm");
 
 const LINUX = navigator.platform.startsWith("Linux");
 const MAC = navigator.platform.startsWith("Mac");
 const WIN = navigator.platform.startsWith("Win");
 
 const PR_RDWR        = 0x04;
 const PR_CREATE_FILE = 0x08;
 const PR_TRUNCATE    = 0x20;
@@ -110,21 +111,21 @@ function setDryRunPref() {
     if (old_dry_run === undefined) {
       Services.prefs.clearUserPref("browser.mozApps.installer.dry_run");
     } else {
       Services.prefs.setBoolPref("browser.mozApps.installer.dry_run", old_dry_run);
     }
   });
 }
 
-function TestAppInfo(aApp) {
+function TestAppInfo(aApp, aIsPackaged) {
   this.appProcess = Cc["@mozilla.org/process/util;1"].
                     createInstance(Ci.nsIProcess);
 
-  this.isPackaged = !!aApp.updateManifest;
+  this.isPackaged = aIsPackaged;
 
   if (LINUX) {
     this.installPath = OS.Path.join(OS.Constants.Path.homeDir,
                                     "." + WebappOSUtils.getUniqueName(aApp));
     this.exePath = OS.Path.join(this.installPath, "webapprt-stub");
 
     this.iconFile = OS.Path.join(this.installPath, "icon.png");
 
@@ -338,19 +339,20 @@ function buildAppPackage(aManifest, aIco
 
   let zipWriter = Cc["@mozilla.org/zipwriter;1"].createInstance(Ci.nsIZipWriter);
   zipWriter.open(zipFile, PR_RDWR | PR_CREATE_FILE | PR_TRUNCATE);
   zipWriter.addEntryFile("index.html",
                          Ci.nsIZipWriter.COMPRESSION_NONE,
                          getFile(getTestFilePath("data/app/index.html")),
                          false);
 
-  var manStream = Cc["@mozilla.org/io/string-input-stream;1"].
+  let manifestJSON = JSON.stringify(aManifest);
+  let manStream = Cc["@mozilla.org/io/string-input-stream;1"].
                   createInstance(Ci.nsIStringInputStream);
-  manStream.setData(aManifest, aManifest.length);
+  manStream.setData(manifestJSON, manifestJSON.length);
   zipWriter.addEntryStream("manifest.webapp", Date.now(),
                            Ci.nsIZipWriter.COMPRESSION_NONE,
                            manStream, false);
 
   if (aIconFile) {
     zipWriter.addEntryFile(aIconFile.leafName,
                            Ci.nsIZipWriter.COMPRESSION_NONE,
                            aIconFile,
@@ -392,8 +394,40 @@ function generateDataURI(aFile) {
 
   var stream = Cc["@mozilla.org/binaryinputstream;1"].
                createInstance(Ci.nsIBinaryInputStream);
   stream.setInputStream(inputStream);
 
   return "data:" + contentType + ";base64," +
          btoa(stream.readBytes(stream.available()));
 }
+
+function confirmNextInstall() {
+  let popupPanel = window.top.QueryInterface(Ci.nsIInterfaceRequestor).
+                              getInterface(Ci.nsIWebNavigation).
+                              QueryInterface(Ci.nsIDocShell).
+                              chromeEventHandler.ownerDocument.defaultView.
+                              PopupNotifications.panel;
+
+  popupPanel.addEventListener("popupshown", function onPopupShown() {
+    popupPanel.removeEventListener("popupshown", onPopupShown, false);
+    this.childNodes[0].button.doCommand();
+  }, false);
+}
+
+let readJSON = Task.async(function*(aPath) {
+  let decoder = new TextDecoder();
+  let data = yield OS.File.read(aPath);
+  return JSON.parse(decoder.decode(data));
+});
+
+let setMacRootInstallDir = Task.async(function*(aPath) {
+  let oldRootInstallDir = NativeApp.prototype._rootInstallDir;
+
+  NativeApp.prototype._rootInstallDir = OS.Path.join(OS.Constants.Path.homeDir,
+                                                     "Applications");
+  yield OS.File.makeDir(NativeApp.prototype._rootInstallDir,
+                        { ignoreExisting: true });
+
+  SimpleTest.registerCleanupFunction(function() {
+    NativeApp.prototype._rootInstallDir = oldRootInstallDir;
+  });
+});
copy from toolkit/webapps/tests/test_packaged_launch.xul
copy to toolkit/webapps/tests/test_custom_origin.xul
--- a/toolkit/webapps/tests/test_packaged_launch.xul
+++ b/toolkit/webapps/tests/test_custom_origin.xul
@@ -1,90 +1,111 @@
 <?xml version="1.0"?>
 <?xml-stylesheet type="text/css" href="chrome://global/skin"?>
 <?xml-stylesheet type="text/css" href="/tests/SimpleTest/test.css"?>
 <!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=981249
+https://bugzilla.mozilla.org/show_bug.cgi?id=1029674
 -->
-<window title="Mozilla Bug 981249"
+<window title="Mozilla Bug 1029674"
         xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
   <script type="application/javascript"
           src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
   <script type="application/javascript"
           src="chrome://mochikit/content/chrome-harness.js"></script>
   <script type="application/javascript" src="head.js"/>
 
   <!-- test results are displayed in the html:body -->
   <body xmlns="http://www.w3.org/1999/xhtml">
-  <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=981249"
-     target="_blank">Mozilla Bug 981249</a>
+  <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1029674"
+     target="_blank">Mozilla Bug 1029674</a>
   </body>
 
 <script type="application/javascript">
 <![CDATA[
 
-/** Test for Bug 981249 **/
+/** Test for Bug 1029674 **/
 
 "use strict";
 
 SimpleTest.waitForExplicitFinish();
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/NativeApp.jsm");
 Cu.import("resource://gre/modules/WebappOSUtils.jsm");
 Cu.import("resource://gre/modules/Promise.jsm");
 
-let manifest = {
-  name: "test_desktop_packaged_launch",
-  version: "0.1a",
-  size: 777,
-  package_path: "/data/app.zip",
-  launch_path: "/index.html",
-};
+let runTest = Task.async(function*() {
+  let manifest = yield readJSON(getTestFilePath("data/custom_origin.webapp"));
 
-let app = {
-  name: "test_desktop_packaged_launch",
-  manifestURL: "http://127.0.0.1:8888/sample.manifest",
-  manifest: manifest,
-  updateManifest: manifest,
-  origin: "app://test_desktop_packaged_launch/",
-  categories: [],
-  installOrigin: "http://127.0.0.1:8888/",
-  receipts: [],
-  installTime: Date.now(),
-};
+  let app = {
+    name: manifest.name,
+    manifestURL: "http://test/chrome/toolkit/webapps/tests/data/custom_origin.webapp",
+    origin: "app://test.origin.privileged.app",
+  };
 
-let testAppInfo = new TestAppInfo(app);
+  let testAppInfo = new TestAppInfo(app, true);
 
-let runTest = Task.async(function*() {
   // Get to a clean state before the test
   yield testAppInfo.cleanup();
 
   SimpleTest.registerCleanupFunction(() => testAppInfo.cleanup());
 
   setDryRunPref();
 
-  let zipPath = buildAppPackage(manifest);
+  // Use the test root certificate for the test
+  Cu.import("resource://gre/modules/StoreTrustAnchor.jsm");
+  let oldIndex = TrustedRootCertificate.index;
+  TrustedRootCertificate.index = Ci.nsIX509CertDB.AppXPCShellRoot;
+
+  SimpleTest.registerCleanupFunction(function() {
+    TrustedRootCertificate.index = oldIndex;
+  });
 
-  let nativeApp = new NativeApp(app, manifest, app.categories);
-  ok(nativeApp, "NativeApp object created");
+  // Allow signed apps to be installed from the test origin
+  let oldSignedAppOrigins;
+  try {
+    oldSignedAppOrigins = Services.prefs.getCharPref("dom.mozApps.signed_apps_installable_from");
+  } catch (ex) {}
 
-  testAppInfo.profileDir = nativeApp.createProfile();
-  ok(testAppInfo.profileDir && testAppInfo.profileDir.exists(), "Profile directory created");
+  let newSignedAppOrigins = oldSignedAppOrigins.concat(",chrome://mochitests");
+  Services.prefs.setCharPref("dom.mozApps.signed_apps_installable_from", newSignedAppOrigins);
+
+  SimpleTest.registerCleanupFunction(function() {
+    Services.prefs.setCharPref("dom.mozApps.signed_apps_installable_from", oldSignedAppOrigins);
+  });
 
   // On Mac build servers, we don't have enough privileges to write to /Applications,
   // so we install apps in a user-owned directory.
   if (MAC) {
-    nativeApp._rootInstallDir = OS.Path.join(OS.Constants.Path.homeDir, "Applications");
-    yield OS.File.makeDir(nativeApp._rootInstallDir, { ignoreExisting: true });
+    yield setMacRootInstallDir(OS.Path.join(OS.Constants.Path.homeDir, "Applications"));
   }
 
-  // Install application
-  info("Test installation");
-  yield nativeApp.install(manifest, zipPath);
+  confirmNextInstall();
+
+  let request = navigator.mozApps.installPackage("http://test/chrome/toolkit/webapps/tests/data/custom_origin.webapp");
+
+  let (deferred = Promise.defer()) {
+    request.onerror = function() {
+      deferred.reject(this.error.name);
+    };
+    request.onsuccess = deferred.resolve;
+    yield deferred.promise;
+  }
+
+  let appObject = request.result;
+  ok(appObject, "app is non-null");
+
+  let (deferred = Promise.defer()) {
+    appObject.ondownloaderror = function() {
+      deferred.reject(this.error.name);
+    };
+    appObject.ondownloadapplied = deferred.resolve;
+    yield deferred.promise;
+  };
+
   while (!WebappOSUtils.isLaunchable(app)) {
     yield wait(1000);
   }
   ok(true, "App launchable");
 
   let exeFile = getFile(testAppInfo.exePath);
 
   ok(exeFile.isExecutable(), "webapprt executable is executable");
--- a/toolkit/webapps/tests/test_hosted.xul
+++ b/toolkit/webapps/tests/test_hosted.xul
@@ -54,85 +54,84 @@ let runTest = Task.async(function*() {
 
   setDryRunPref();
 
   let nativeApp = new NativeApp(app, manifest, app.categories);
   ok(nativeApp, "NativeApp object created");
 
   info("Test update for an uninstalled application");
   try {
-    yield nativeApp.prepareUpdate(manifest);
+    yield nativeApp.prepareUpdate(app, manifest);
     ok(false, "Didn't thrown");
   } catch (ex) {
     is(ex, "The application isn't installed", "Exception thrown");
   }
 
   testAppInfo.profileDir = nativeApp.createProfile();
   ok(testAppInfo.profileDir && testAppInfo.profileDir.exists(), "Profile directory created");
   ok((yield OS.File.exists(testAppInfo.profilesIni)), "profiles.ini file created");
 
   // On Mac build servers, we don't have enough privileges to write to /Applications,
   // so we install apps in a user-owned directory.
   if (MAC) {
-    nativeApp._rootInstallDir = OS.Path.join(OS.Constants.Path.homeDir, "Applications");
-    yield OS.File.makeDir(nativeApp._rootInstallDir, { ignoreExisting: true });
+    yield setMacRootInstallDir(OS.Path.join(OS.Constants.Path.homeDir, "Applications"));
   }
 
   // Install application
   info("Test installation");
-  yield nativeApp.install(manifest);
+  yield nativeApp.install(app, manifest);
   while (!WebappOSUtils.isLaunchable(app)) {
     yield wait(1000);
   }
   ok(true, "App launchable");
   ok((yield checkFiles(testAppInfo.installedFiles)), "Files correctly written");
   is(WebappOSUtils.getInstallPath(app), testAppInfo.installPath, "getInstallPath == installPath");
 
   let stat = yield OS.File.stat(testAppInfo.installPath);
   let installTime = stat.lastModificationDate;
 
   // Wait one second, otherwise the last modification date is the same.
   yield wait(1000);
 
   // Reinstall application
   info("Test reinstallation");
-  yield nativeApp.install(manifest);
+  yield nativeApp.install(app, manifest);
   while (!WebappOSUtils.isLaunchable(app)) {
     yield wait(1000);
   }
   ok(true, "App launchable");
   ok((yield checkFiles(testAppInfo.installedFiles)), "Installation not corrupted");
   ok((yield checkFiles(testAppInfo.tempUpdatedFiles)), "Files correctly written in the update subdirectory");
 
-  yield nativeApp.applyUpdate();
+  yield nativeApp.applyUpdate(app);
   while (!WebappOSUtils.isLaunchable(app)) {
     yield wait(1000);
   }
   ok(true, "App launchable");
   ok((yield checkFiles(testAppInfo.installedFiles)), "Installation not corrupted");
   ok(!(yield OS.File.exists(OS.Path.join(testAppInfo.installPath, "update"))), "Update directory removed");
   ok((yield checkDateHigherThan(testAppInfo.updatedFiles, installTime)), "Modification date higher");
 
   stat = yield OS.File.stat(testAppInfo.installPath);
   installTime = stat.lastModificationDate;
 
   // Wait one second, otherwise the last modification date is the same.
   yield wait(1000);
 
   // Update application
   info("Test update");
-  yield nativeApp.prepareUpdate(manifest);
+  yield nativeApp.prepareUpdate(app, manifest);
   while (!WebappOSUtils.isLaunchable(app)) {
     yield wait(1000);
   }
   ok(true, "App launchable");
   ok((yield checkFiles(testAppInfo.installedFiles)), "Installation not corrupted");
   ok((yield checkFiles(testAppInfo.tempUpdatedFiles)), "Files correctly written in the update subdirectory");
 
-  yield nativeApp.applyUpdate();
+  yield nativeApp.applyUpdate(app);
   while (!WebappOSUtils.isLaunchable(app)) {
     yield wait(1000);
   }
   ok(true, "App launchable");
   ok((yield checkFiles(testAppInfo.installedFiles)), "Installation not corrupted");
   ok(!(yield OS.File.exists(OS.Path.join(testAppInfo.installPath, "update"))), "Update directory removed");
   ok((yield checkDateHigherThan(testAppInfo.updatedFiles, installTime)), "Modification date higher");
 
--- a/toolkit/webapps/tests/test_hosted_icons.xul
+++ b/toolkit/webapps/tests/test_hosted_icons.xul
@@ -117,23 +117,22 @@ let runTest = Task.async(function*() {
 
     testAppInfo.profileDir = nativeApp.createProfile();
     ok(testAppInfo.profileDir && testAppInfo.profileDir.exists(), "Profile directory created");
     ok((yield OS.File.exists(testAppInfo.profilesIni)), "profiles.ini file created");
 
     // On Mac build servers, we don't have enough privileges to write to /Applications,
     // so we install apps in a user-owned directory.
     if (MAC) {
-      nativeApp._rootInstallDir = OS.Path.join(OS.Constants.Path.homeDir, "Applications");
-      yield OS.File.makeDir(nativeApp._rootInstallDir, { ignoreExisting: true });
+      yield setMacRootInstallDir(OS.Path.join(OS.Constants.Path.homeDir, "Applications"));
     }
 
     // Install application
     info("Test installation");
-    yield nativeApp.install(manifest);
+    yield nativeApp.install(app, manifest);
     while (!WebappOSUtils.isLaunchable(app)) {
       yield wait(1000);
     }
     ok(true, "App launchable");
 
     let stat = yield OS.File.stat(testAppInfo.iconFile);
     isfuzzy(stat.size, iconSizes[curTest][0], iconSizes[curTest][1],
             "Icon size correct");
--- a/toolkit/webapps/tests/test_hosted_launch.xul
+++ b/toolkit/webapps/tests/test_hosted_launch.xul
@@ -60,23 +60,22 @@ let runTest = Task.async(function*() {
   ok(nativeApp, "NativeApp object created");
 
   testAppInfo.profileDir = nativeApp.createProfile();
   ok(testAppInfo.profileDir && testAppInfo.profileDir.exists(), "Profile directory created");
 
   // On Mac build servers, we don't have enough privileges to write to /Applications,
   // so we install apps in a user-owned directory.
   if (MAC) {
-    nativeApp._rootInstallDir = OS.Path.join(OS.Constants.Path.homeDir, "Applications");
-    yield OS.File.makeDir(nativeApp._rootInstallDir, { ignoreExisting: true });
+    yield setMacRootInstallDir(OS.Path.join(OS.Constants.Path.homeDir, "Applications"));
   }
 
   // Install application
   info("Test installation");
-  yield nativeApp.install(manifest);
+  yield nativeApp.install(app, manifest);
   while (!WebappOSUtils.isLaunchable(app)) {
     yield wait(1000);
   }
   ok(true, "App launchable");
 
   let exeFile = getFile(testAppInfo.exePath);
 
   ok(exeFile.isExecutable(), "webapprt executable is executable");
--- a/toolkit/webapps/tests/test_hosted_launch_no_registry.xul
+++ b/toolkit/webapps/tests/test_hosted_launch_no_registry.xul
@@ -60,23 +60,22 @@ let runTest = Task.async(function*() {
   ok(nativeApp, "NativeApp object created");
 
   testAppInfo.profileDir = nativeApp.createProfile();
   ok(testAppInfo.profileDir && testAppInfo.profileDir.exists(), "Profile directory created");
 
   // On Mac build servers, we don't have enough privileges to write to /Applications,
   // so we install apps in a user-owned directory.
   if (MAC) {
-    nativeApp._rootInstallDir = OS.Path.join(OS.Constants.Path.homeDir, "Applications");
-    yield OS.File.makeDir(nativeApp._rootInstallDir, { ignoreExisting: true });
+    yield setMacRootInstallDir(OS.Path.join(OS.Constants.Path.homeDir, "Applications"));
   }
 
   // Install application
   info("Test installation");
-  yield nativeApp.install(manifest);
+  yield nativeApp.install(app, manifest);
   while (!WebappOSUtils.isLaunchable(app)) {
     yield wait(1000);
   }
   ok(true, "App launchable");
 
   let exeFile = getFile(testAppInfo.exePath);
 
   ok(exeFile.isExecutable(), "webapprt executable is executable");
--- a/toolkit/webapps/tests/test_hosted_uninstall.xul
+++ b/toolkit/webapps/tests/test_hosted_uninstall.xul
@@ -59,23 +59,22 @@ let runTest = Task.async(function*() {
 
   testAppInfo.profileDir = nativeApp.createProfile();
   ok(testAppInfo.profileDir && testAppInfo.profileDir.exists(), "Profile directory created");
   ok((yield OS.File.exists(testAppInfo.profilesIni)), "profiles.ini file created");
 
   // On Mac build servers, we don't have enough privileges to write to /Applications,
   // so we install apps in a user-owned directory.
   if (MAC) {
-    nativeApp._rootInstallDir = OS.Path.join(OS.Constants.Path.homeDir, "Applications");
-    yield OS.File.makeDir(nativeApp._rootInstallDir, { ignoreExisting: true });
+    yield setMacRootInstallDir(OS.Path.join(OS.Constants.Path.homeDir, "Applications"));
   }
 
   // Install application
   info("Test installation");
-  yield nativeApp.install(manifest);
+  yield nativeApp.install(app, manifest);
   while (!WebappOSUtils.isLaunchable(app)) {
     yield wait(1000);
   }
   ok(true, "App launchable");
   ok((yield checkFiles(testAppInfo.installedFiles)), "Files correctly written");
   is(WebappOSUtils.getInstallPath(app), testAppInfo.installPath, "getInstallPath == installPath");
 
   // Uninstall application
--- a/toolkit/webapps/tests/test_hosted_update_from_webapp_runtime.xul
+++ b/toolkit/webapps/tests/test_hosted_update_from_webapp_runtime.xul
@@ -65,31 +65,30 @@ let runTest = Task.async(function*() {
   ok(nativeApp, "NativeApp object created");
 
   testAppInfo.profileDir = nativeApp.createProfile();
   ok(testAppInfo.profileDir && testAppInfo.profileDir.exists(), "Profile directory created");
 
   // On Mac build servers, we don't have enough privileges to write to /Applications,
   // so we install apps in a user-owned directory.
   if (MAC) {
-    nativeApp._rootInstallDir = OS.Path.join(OS.Constants.Path.homeDir, "Applications");
-    yield OS.File.makeDir(nativeApp._rootInstallDir, { ignoreExisting: true });
+    yield setMacRootInstallDir(OS.Path.join(OS.Constants.Path.homeDir, "Applications"));
   }
 
   // Install application
   info("Test installation");
-  yield nativeApp.install(manifest);
+  yield nativeApp.install(app, manifest);
   while (!WebappOSUtils.isLaunchable(app)) {
     yield wait(1000);
   }
   ok(true, "App launchable");
 
   // Prepare update
   info("Test update");
-  yield nativeApp.prepareUpdate(updatedManifest);
+  yield nativeApp.prepareUpdate(app, updatedManifest);
   while (!WebappOSUtils.isLaunchable(app)) {
     yield wait(1000);
   }
   ok(true, "App launchable");
 
   // Let the webapp runtime apply the update. The app.sjs?appreq page is
   // accessed only if the app is actually updated (because the old manifest
   // contained a different launch path).
--- a/toolkit/webapps/tests/test_packaged.xul
+++ b/toolkit/webapps/tests/test_packaged.xul
@@ -45,17 +45,17 @@ let app = {
   updateManifest: manifest,
   origin: "http://example.com/",
   categories: [],
   installOrigin: "http://example.com/",
   receipts: [],
   installTime: Date.now(),
 };
 
-let testAppInfo = new TestAppInfo(app);
+let testAppInfo = new TestAppInfo(app, true);
 
 let runTest = Task.async(function*() {
   // Get to a clean state before the test
   yield testAppInfo.cleanup();
 
   SimpleTest.registerCleanupFunction(() => testAppInfo.cleanup());
 
   setDryRunPref();
@@ -63,36 +63,35 @@ let runTest = Task.async(function*() {
   let zipFile = yield OS.File.open(zipPath, { create: true });
   yield zipFile.close();
 
   let nativeApp = new NativeApp(app, manifest, app.categories);
   ok(nativeApp, "NativeApp object created");
 
   info("Test update for an application that isn't installed");
   try {
-    yield nativeApp.prepareUpdate(manifest, zipPath);
+    yield nativeApp.prepareUpdate(app, manifest, zipPath);
     ok(false, "Didn't thrown");
   } catch (ex) {
     is(ex, "The application isn't installed", "Exception thrown");
   }
 
   testAppInfo.profileDir = nativeApp.createProfile();
   ok(testAppInfo.profileDir && testAppInfo.profileDir.exists(), "Profile directory created");
   ok((yield OS.File.exists(testAppInfo.profilesIni)), "profiles.ini file created");
 
   // On Mac build servers, we don't have enough privileges to write to /Applications,
   // so we install apps in a user-owned directory.
   if (MAC) {
-    nativeApp._rootInstallDir = OS.Path.join(OS.Constants.Path.homeDir, "Applications");
-    yield OS.File.makeDir(nativeApp._rootInstallDir, { ignoreExisting: true });
+    yield setMacRootInstallDir(OS.Path.join(OS.Constants.Path.homeDir, "Applications"));
   }
 
   // Install application
   info("Test installation");
-  yield nativeApp.install(manifest, zipPath);
+  yield nativeApp.install(app, manifest, zipPath);
   while (!WebappOSUtils.isLaunchable(app)) {
     yield wait(1000);
   }
   ok(true, "App launchable");
   ok((yield checkFiles(testAppInfo.installedFiles)), "Files correctly written");
   is(WebappOSUtils.getInstallPath(app), testAppInfo.installPath, "getInstallPath == installPath");
 
   let stat = yield OS.File.stat(testAppInfo.installPath);
@@ -102,25 +101,25 @@ let runTest = Task.async(function*() {
   yield wait(1000);
 
   // Reinstall application
   info("Test reinstallation");
 
   zipFile = yield OS.File.open(zipPath, { create: true });
   yield zipFile.close();
 
-  yield nativeApp.install(manifest, zipPath);
+  yield nativeApp.install(app, manifest, zipPath);
   while (!WebappOSUtils.isLaunchable(app)) {
     yield wait(1000);
   }
   ok(true, "App launchable");
   ok((yield checkFiles(testAppInfo.installedFiles)), "Installation not corrupted");
   ok((yield checkFiles(testAppInfo.tempUpdatedFiles)), "Files correctly written in the update subdirectory");
 
-  yield nativeApp.applyUpdate();
+  yield nativeApp.applyUpdate(app);
   while (!WebappOSUtils.isLaunchable(app)) {
     yield wait(1000);
   }
   ok(true, "App launchable");
   ok((yield checkFiles(testAppInfo.installedFiles)), "Installation not corrupted");
   ok(!(yield OS.File.exists(OS.Path.join(testAppInfo.installPath, "update"))), "Update directory removed");
   ok((yield checkDateHigherThan(testAppInfo.updatedFiles, installTime)), "Modification date higher");
 
@@ -131,25 +130,25 @@ let runTest = Task.async(function*() {
   yield wait(1000);
 
   // Update application
   info("Test update");
 
   zipFile = yield OS.File.open(zipPath, { create: true });
   yield zipFile.close();
 
-  yield nativeApp.prepareUpdate(manifest, zipPath);
+  yield nativeApp.prepareUpdate(app, manifest, zipPath);
   while (!WebappOSUtils.isLaunchable(app)) {
     yield wait(1000);
   }
   ok(true, "App launchable");
   ok((yield checkFiles(testAppInfo.installedFiles)), "Installation not corrupted");
   ok((yield checkFiles(testAppInfo.tempUpdatedFiles)), "Files correctly written in the update subdirectory");
 
-  yield nativeApp.applyUpdate();
+  yield nativeApp.applyUpdate(app);
   while (!WebappOSUtils.isLaunchable(app)) {
     yield wait(1000);
   }
   ok(true, "App launchable");
   ok((yield checkFiles(testAppInfo.installedFiles)), "Installation not corrupted");
   ok(!(yield OS.File.exists(OS.Path.join(testAppInfo.installPath, "update"))), "Update directory removed");
   ok((yield checkDateHigherThan(testAppInfo.updatedFiles, installTime)), "Modification date higher");
 
--- a/toolkit/webapps/tests/test_packaged_icons.xul
+++ b/toolkit/webapps/tests/test_packaged_icons.xul
@@ -101,17 +101,17 @@ if (LINUX) {
     [14000, 2000],
     [27000, 2000],
     [27000, 2000],
     [27000, 2000],
     [14000, 2000],
   ];
 }
 
-let testAppInfo = new TestAppInfo(app);
+let testAppInfo = new TestAppInfo(app, true);
 
 let runTest = Task.async(function*() {
   SimpleTest.registerCleanupFunction(() => testAppInfo.cleanup());
 
   setDryRunPref();
 
   for (let curTest = 0; curTest < iconTests.length; curTest++) {
     // Get to a clean state before the test
@@ -127,23 +127,22 @@ let runTest = Task.async(function*() {
 
     testAppInfo.profileDir = nativeApp.createProfile();
     ok(testAppInfo.profileDir && testAppInfo.profileDir.exists(), "Profile directory created");
     ok((yield OS.File.exists(testAppInfo.profilesIni)), "profiles.ini file created");
 
     // On Mac build servers, we don't have enough privileges to write to /Applications,
     // so we install apps in a user-owned directory.
     if (MAC) {
-      nativeApp._rootInstallDir = OS.Path.join(OS.Constants.Path.homeDir, "Applications");
-      yield OS.File.makeDir(nativeApp._rootInstallDir, { ignoreExisting: true });
+    yield setMacRootInstallDir(OS.Path.join(OS.Constants.Path.homeDir, "Applications"));
     }
 
     // Install application
     info("Test installation");
-    yield nativeApp.install(manifest, zipPath);
+    yield nativeApp.install(app, manifest, zipPath);
     while (!WebappOSUtils.isLaunchable(app)) {
       yield wait(1000);
     }
     ok(true, "App launchable");
 
     let stat = yield OS.File.stat(testAppInfo.iconFile);
     isfuzzy(stat.size, iconSizes[curTest][0], iconSizes[curTest][1],
             "Icon size correct");
--- a/toolkit/webapps/tests/test_packaged_launch.xul
+++ b/toolkit/webapps/tests/test_packaged_launch.xul
@@ -47,17 +47,17 @@ let app = {
   updateManifest: manifest,
   origin: "app://test_desktop_packaged_launch/",
   categories: [],
   installOrigin: "http://127.0.0.1:8888/",
   receipts: [],
   installTime: Date.now(),
 };
 
-let testAppInfo = new TestAppInfo(app);
+let testAppInfo = new TestAppInfo(app, true);
 
 let runTest = Task.async(function*() {
   // Get to a clean state before the test
   yield testAppInfo.cleanup();
 
   SimpleTest.registerCleanupFunction(() => testAppInfo.cleanup());
 
   setDryRunPref();
@@ -68,23 +68,22 @@ let runTest = Task.async(function*() {
   ok(nativeApp, "NativeApp object created");
 
   testAppInfo.profileDir = nativeApp.createProfile();
   ok(testAppInfo.profileDir && testAppInfo.profileDir.exists(), "Profile directory created");
 
   // On Mac build servers, we don't have enough privileges to write to /Applications,
   // so we install apps in a user-owned directory.
   if (MAC) {
-    nativeApp._rootInstallDir = OS.Path.join(OS.Constants.Path.homeDir, "Applications");
-    yield OS.File.makeDir(nativeApp._rootInstallDir, { ignoreExisting: true });
+    yield setMacRootInstallDir(OS.Path.join(OS.Constants.Path.homeDir, "Applications"));
   }
 
   // Install application
   info("Test installation");
-  yield nativeApp.install(manifest, zipPath);
+  yield nativeApp.install(app, manifest, zipPath);
   while (!WebappOSUtils.isLaunchable(app)) {
     yield wait(1000);
   }
   ok(true, "App launchable");
 
   let exeFile = getFile(testAppInfo.exePath);
 
   ok(exeFile.isExecutable(), "webapprt executable is executable");
--- a/toolkit/webapps/tests/test_packaged_launch_no_registry.xul
+++ b/toolkit/webapps/tests/test_packaged_launch_no_registry.xul
@@ -47,17 +47,17 @@ let app = {
   updateManifest: manifest,
   origin: "app://test_desktop_packaged_launch_no_registry/",
   categories: [],
   installOrigin: "http://127.0.0.1:8888/",
   receipts: [],
   installTime: Date.now(),
 };
 
-let testAppInfo = new TestAppInfo(app);
+let testAppInfo = new TestAppInfo(app, true);
 
 let runTest = Task.async(function*() {
   // Get to a clean state before the test
   yield testAppInfo.cleanup();
 
   SimpleTest.registerCleanupFunction(() => testAppInfo.cleanup());
 
   setDryRunPref();
@@ -68,23 +68,22 @@ let runTest = Task.async(function*() {
   ok(nativeApp, "NativeApp object created");
 
   testAppInfo.profileDir = nativeApp.createProfile();
   ok(testAppInfo.profileDir && testAppInfo.profileDir.exists(), "Profile directory created");
 
   // On Mac build servers, we don't have enough privileges to write to /Applications,
   // so we install apps in a user-owned directory.
   if (MAC) {
-    nativeApp._rootInstallDir = OS.Path.join(OS.Constants.Path.homeDir, "Applications");
-    yield OS.File.makeDir(nativeApp._rootInstallDir, { ignoreExisting: true });
+    yield setMacRootInstallDir(OS.Path.join(OS.Constants.Path.homeDir, "Applications"));
   }
 
   // Install application
   info("Test installation");
-  yield nativeApp.install(manifest, zipPath);
+  yield nativeApp.install(app, manifest, zipPath);
   while (!WebappOSUtils.isLaunchable(app)) {
     yield wait(1000);
   }
   ok(true, "App launchable");
 
   let exeFile = getFile(testAppInfo.exePath);
 
   ok(exeFile.isExecutable(), "webapprt executable is executable");
--- a/toolkit/webapps/tests/test_packaged_uninstall.xul
+++ b/toolkit/webapps/tests/test_packaged_uninstall.xul
@@ -45,17 +45,17 @@ let app = {
   updateManifest: manifest,
   origin: "http://example.com/",
   categories: [],
   installOrigin: "http://example.com/",
   receipts: [],
   installTime: Date.now(),
 };
 
-let testAppInfo = new TestAppInfo(app);
+let testAppInfo = new TestAppInfo(app, true);
 
 let runTest = Task.async(function*() {
   // Get to a clean state before the test
   yield testAppInfo.cleanup();
 
   SimpleTest.registerCleanupFunction(() => testAppInfo.cleanup());
 
   setDryRunPref();
@@ -68,23 +68,22 @@ let runTest = Task.async(function*() {
 
   testAppInfo.profileDir = nativeApp.createProfile();
   ok(testAppInfo.profileDir && testAppInfo.profileDir.exists(), "Profile directory created");
   ok((yield OS.File.exists(testAppInfo.profilesIni)), "profiles.ini file created");
 
   // On Mac build servers, we don't have enough privileges to write to /Applications,
   // so we install apps in a user-owned directory.
   if (MAC) {
-    nativeApp._rootInstallDir = OS.Path.join(OS.Constants.Path.homeDir, "Applications");
-    yield OS.File.makeDir(nativeApp._rootInstallDir, { ignoreExisting: true });
+    yield setMacRootInstallDir(OS.Path.join(OS.Constants.Path.homeDir, "Applications"));
   }
 
   // Install application
   info("Test installation");
-  yield nativeApp.install(manifest, zipPath);
+  yield nativeApp.install(app, manifest, zipPath);
   while (!WebappOSUtils.isLaunchable(app)) {
     yield wait(1000);
   }
   ok(true, "App launchable");
   ok((yield checkFiles(testAppInfo.installedFiles)), "Files correctly written");
   is(WebappOSUtils.getInstallPath(app), testAppInfo.installPath, "getInstallPath == installPath");
 
   // Uninstall application
--- a/toolkit/webapps/tests/test_packaged_update_from_webapp_runtime.xul
+++ b/toolkit/webapps/tests/test_packaged_update_from_webapp_runtime.xul
@@ -55,17 +55,17 @@ let app = {
   updateManifest: manifest,
   origin: "app://test_desktop_packaged_launch/",
   categories: [],
   installOrigin: "http://127.0.0.1:8888/",
   receipts: [],
   installTime: Date.now(),
 };
 
-let testAppInfo = new TestAppInfo(app);
+let testAppInfo = new TestAppInfo(app, true);
 
 let runTest = Task.async(function*() {
   // Get to a clean state before the test
   yield testAppInfo.cleanup();
 
   SimpleTest.registerCleanupFunction(() => testAppInfo.cleanup());
 
   setDryRunPref();
@@ -76,34 +76,33 @@ let runTest = Task.async(function*() {
   ok(nativeApp, "NativeApp object created");
 
   testAppInfo.profileDir = nativeApp.createProfile();
   ok(testAppInfo.profileDir && testAppInfo.profileDir.exists(), "Profile directory created");
 
   // On Mac build servers, we don't have enough privileges to write to /Applications,
   // so we install apps in a user-owned directory.
   if (MAC) {
-    nativeApp._rootInstallDir = OS.Path.join(OS.Constants.Path.homeDir, "Applications");
-    yield OS.File.makeDir(nativeApp._rootInstallDir, { ignoreExisting: true });
+    yield setMacRootInstallDir(OS.Path.join(OS.Constants.Path.homeDir, "Applications"));
   }
 
   // Install application
   info("Test installation");
-  yield nativeApp.install(manifest, zipPath);
+  yield nativeApp.install(app, manifest, zipPath);
   while (!WebappOSUtils.isLaunchable(app)) {
     yield wait(1000);
   }
   ok(true, "App launchable");
 
   // Prepare update
   info("Test update");
 
   zipPath = buildAppPackage(updatedManifest);
 
-  yield nativeApp.prepareUpdate(updatedManifest, zipPath);
+  yield nativeApp.prepareUpdate(app, updatedManifest, zipPath);
   while (!WebappOSUtils.isLaunchable(app)) {
     yield wait(1000);
   }
   ok(true, "App launchable");
 
   // Let the webapp runtime apply the update. The app.sjs?appreq page is
   // accessed only if the app is actually updated (because the old manifest
   // contained a different launch path).
--- a/webapprt/WebappManager.jsm
+++ b/webapprt/WebappManager.jsm
@@ -37,17 +37,17 @@ this.WebappManager = {
         break;
     }
   },
 
   update: function(aApp, aManifest, aZipPath) {
     let nativeApp = new NativeApp(aApp, aManifest,
                                   WebappRT.config.app.categories,
                                   WebappRT.config.registryDir);
-    nativeApp.prepareUpdate(aManifest, aZipPath);
+    nativeApp.prepareUpdate(aApp, aManifest, aZipPath);
   },
 
   doInstall: function(data, window) {
     let jsonManifest = data.isPackage ? data.app.updateManifest : data.app.manifest;
     let manifest = new ManifestHelper(jsonManifest, data.app.origin);
     let name = manifest.name;
     let bundle = Services.strings.createBundle("chrome://webapprt/locale/webapp.properties");
 
@@ -74,18 +74,18 @@ this.WebappManager = {
       try {
         localDir = nativeApp.createProfile();
       } catch (ex) {
         DOMApplicationRegistry.denyInstall(aData);
         return;
       }
 
       DOMApplicationRegistry.confirmInstall(data, localDir,
-        function (aManifest, aZipPath) {
-          nativeApp.install(aManifest, aZipPath);
+        function (aApp, aManifest, aZipPath) {
+          nativeApp.install(aApp, aManifest, aZipPath);
         }
       );
     } else {
       DOMApplicationRegistry.denyInstall(data);
     }
   },
 
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
--- a/webapprt/WebappRT.jsm
+++ b/webapprt/WebappRT.jsm
@@ -72,17 +72,17 @@ this.WebappRT = {
     let webappJson = OS.Path.join(Services.dirsvc.get("AppRegD", Ci.nsIFile).path,
                                   "update", "webapp.json");
     let config = yield AppsUtils.loadJSONAsync(webappJson);
 
     let nativeApp = new NativeApp(config.app, config.app.manifest,
                                   config.app.categories,
                                   config.registryDir);
     try {
-      yield nativeApp.applyUpdate();
+      yield nativeApp.applyUpdate(config.app);
     } catch (ex) {
       return false;
     }
 
     // The update has been applied successfully, the new config file
     // is the config file that was in the update directory.
     this.config = config;