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 213215 2ec73f026a68e49843701dc92fa423b770171ad4
parent 213214 b7f8089105b216ecfe8167e2a6f5dfd703dab935
child 213216 3a3f72171ac4b57014e338f325ef8d521e9cec82
push id3857
push userraliiev@mozilla.com
push dateTue, 02 Sep 2014 16:39:23 +0000
treeherdermozilla-beta@5638b907b505 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmyk, keeler
bugs1029674
milestone33.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 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
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
index 4cff6e9f269a7491845ef3b2253fcc109f553840..74a35a4208dcee164af51abcdd5715f3d90a67ea
GIT binary patch
literal 4222
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
BRqFr%
index 484cf04b3bf92a3392cb1273da12e295e3efc06e..950255b4928f4a410885a55ff113570f721032bd
GIT binary patch
literal 4220
zc$|$_2Q-}979J)#i73&7MATq(2}YkAWwcSEMTp)RHAIOPK@fE?Y7ixQZ=+6#-bEWR
zYDknp2$3iEt+(!t_3nD_{A-`J_W!T_?Y+;x&-wPzQUhM00RRBR0C-e_QpeTejsXGy
zz?={OAP3L`?4Gzd^9u<}3b;Dk=;;ywfSXA=Fi#&c0N#}iAOP^^^iKCgcEXqW+#mG{
ztvBRWGu2RTqT(*5jz*$cp2T@lsJL_dAu`kXFs35hkbRYL3fg)LqGLRiKg%3rU(eIe
zW&nAGJP%v=@yR!H8W#}Y^K-uib5GL1Psp6W91xV67xGR@vkwy#<h#9$GDK5z4=vmT
zra@5Y>kQ|v>Eq&n8XaY?A(Gve5oIEbB!B~p!m|kDxr4`6v1#joMbNbK+&-bmRw_3h
zy;^Eyv@!~2dODx}{10-__3=3qMm?%bIHdMqm^*hX%&$11dg^Y_Ec@ak_ZM;<OnUU$
z{^N>mmcXT@n6xS#ylgo%d;H7eZ<WV)H|=9-BWbB;zS@L``w8AH_hd5fCkaqH<Ck3>
zZSvA;<AIR8y*{B#7nGGkOKun1m;An7ue6o#!=YgR98;HzA2LEGXq}g5^7O`5M8rX{
z_`JVt+$-u_l^r%UHFEFe_D+4XbLyl%^}8pak0S~$7|rV2rHS-MY!!i~!u9c?gL+3J
zl&-nm#L<45p6EJJ#ThVr`MWuL67MUB?^*Cs=vh@5Cm@eDot9kd)8}5}s314owcGJL
zaZS`tU=Im5?|S`ut|Ufgs;@xaa4H=2yFw57`0Pn%<lB22afvViyoPdXC_<Z-TxGby
zu~+lG%&KST{nAO-;HB!EkOzd-ht8fIS#o4<9YeDas7+RfuYO23Cr{c97s18+J24uC
z!H(1!ljTmJ51H;&skiRswP(q7t%)Z*X(&<cepe*WZ0brYd*46K{n?rxHFNq<cCFk4
ziU#_nIhJoi_DU<fi5vwq>b{DV6ZEmnABmmkMam6*CY{}9#6q>7=>?Y}CasdEof6wf
zQjrbMq7>>ibQ*2rDhtkF_3{S!jg~n=cC4_e61AItZBxq*%JsEd1>=?w`eZ!oT{1i7
zrj*VI2gX+}*j|i=TP31LR6-Fz$G9&uZpy~M^UMub!7Qro|3o)r7S5jc(?8bizM=5e
z=M9Rtn#_y?O|oh10IB9dR*7nkH{LF8R>(QJvd!TF+2KutOlE`WDfH>v*h0fZ+Xs54
zHWHTgjp!$RADD-pFU<7y|Iz(u%rvWr;&c616z=Cc@hPUe0bsGL<K97W;*$n1U;0&o
zm6el9Huh1zTWD#WTxiqQy2q{zyTY7#JGTAhy4WN)m-j0r%0+B=3asZelHvL;d56?(
z3YID_rO6)_gx#fSKK?ZHO(#;Q?cDIsh>WQi?Bj%u^(R)xrTs6{H-d^@2_^tyBXQ*H
zOBiwPYd=`A*Rsp%-)7Q2eG&{*z9)~ZzK^y;M6GN(ystS)-G1)P5#vvpi1wcr8`;g<
zm-DZ9Dq)6fGwd;Ld7;bIhs)tXn6}e?ZgnyFz)V2etmre9pU0AS%+5o&zIeNJ6+4#G
z*Oit!U8OGd+O6sNA)P|mpb=1#DbU6cA9dyF`rTprjTpNudZC~YRfF7Oi42O-=Csnn
zq*>}GnHC3fVAlu_D^SU@N}`;qvfPS(r(iJ1cB7v?khpE+X-(d<;@5mY$~hmUzG8!W
z)vx&g-I45qo_WZwd1KWV9+-+fq-R{~;t)?a$bt9UQ+S=Vh&cYO$+w|UZqM1Id^4#}
zge$y;hqh%m@Ezs%$ZR-_ve|5ez8geYqefqnr<BJW-I#t=i=<!^ElCWDyFD_CfEALw
zk>+Xnkorbne)e8|Jp`<2e3}~dLKF2(I~zNJdT2%XX^pj_c-}TFcpg!54H`w3bLb0n
zo9~b$RIxv0`w;}{pHrG@@*Sq?oT(U}luSA@xJjAv-oERT{~LqcsAy9$owXCHq#c)p
z%tmB06?Q}Bj&kP<kTj01QCcoPTO;rt<q#Nir{`)S6|*)J;bTt4rG5`(h=}iNvm=Oy
z-x_4_I^%ML$I4q0QTJSV;a}X9<4R=|DezfRv?O=P?x7@pT>^D6r(D}x;$xSebpX}X
zgcjNX2njS`<&Z(r!qh1pB+u{@p!FLYU<E@}T`siP3nBf*2F{jNJ_5ELPLBV?1<Fu0
z(hv~k0@<RubkJa3lv}$<4P9!Q9rXi`x7f}(iJ0fROM3y*+h@Wb%kVqgbL%=}6_-y$
zdQEC8xCSi9VM{x_;*LjOcS3LQlgL<1ae#_*rI<ZDVRhv_JE5w>lm^_78)hXu#012=
z1L_B_{T}Y>gl6LKg?0xI0JwG$u9La5owXI*L%`e0!rayMpJJAY8@BHPk)wBfNps*^
zBblns&z`e<*f5XIzGsge3{+b2su=UmVu{^ki#s?4bI1U06QJ>SSDyQX=Mj4`wMR}0
zXjG|HYQ*kJ1YUpTx)vKaGR{BiOY;FvAmT6;pqj&V{flwYS4Lk(wf7r^Wx_VFiCaE^
zuSZ0oIkk29AgXfW;iQ*oeSR!zs+gYTsyAA?E<y02Tty6#QJZ8GS3l<))^!;M<7}uR
zZ~XSQ`huloqMT3pl=q_=mU@S8k2cnD8wqz%;85(*Vdz5jDZyWBGTJt)q=x1VNxT98
z6km|~)r+@KWnFoG6%B}>ynqf&Uc}bI6pb#LTpK+?ro<!d<f*qjLO7@T)T5)r=Ls_$
zOK1HD)OAY93FnEGqsh`zw<yw5OD^O!9Y{n-7%v`A&;;+cgw`|Chf;8EvUCv478KpZ
zKCrTF%Wchuzg$6v`@WX4mc(Jb#uI8)kj(Uf@j*y;W2mwT9~GR;-JOay0~bZQA4R1m
zjKh%=P#nvT4RkV_N8Us5kPf{fbYE@>=Ona-SVO`KIMER8<rZ|WNr>p@W`!9!%4O2B
zrPpV<x&-SJxl|#X5P}uJV@s>Y&K4_HfR}KPn2@lrKO$|nC~I>DO`Re|n?(vafhH5<
zGT3LI6?{0T85>VbnJWZ0=jB$_Z*-)w{CvpjRK3x%hg(?}fEzYORn7mj?mIu+bzg}>
zd73)oVVe7bQH#<l&^FBL4*ok%A5RBMmU^Gv{uXg0Uw+y&<b^m_kGpTv!2NZ(T+_X0
z_`GFKh=0q>HbOTlQ#m9HQyBeb_@{z6h+tZ2V1X-%?ck~29h`p1WJgrjg0)qY^j-7C
z`+Q4<x@75_>_s-MUyE#-O%;8kyHQ(vH+`sIFIq3Po*oyo+VsaNJ9-sK(zNSIAHF*9
zjG6RzSG}j{?9Oqs;^b*SayQhBD3&5!W0h9P7%WXk+q3M}Tb13q;3!Ug8)rcV6an6R
zJkm!hB7AORF8mlo3Eb0_m%a%U5Wngq4$;^5vbF|-<0NGtWP~9iTk-hqghKFZ-1_iY
zurn5?ti1Af`z2(?k?|8LPr9;h?F&_AI=t>m)8XpK;^W0Ef=f_Ro$!Lqc`Q_C@swHc
zjJRsw1{_ey2Qi0_kTkO-n9cjSV%Ch}vJQ59zC<B!dCRiSF%(tDX}h1Y?~!y!7os$V
z8xyM!T%srT?3TZij71FZ-<f7uG&#vTS&U#s*~sc!yPmUT=2X`9Hw{#oyG*~Vouiw_
z*yYq|i7QHcGz`RK&FPWKdU*L}Y!4oLt&fzPVMOS=Bz*K)xTm#FS8<J7-=@u~20xZB
z*L^m{S+*WKeAS5otkTKekTvG)?QLDYd0OPu=S`(=_l+OJJCOpGnpm!EI6XI(g%!6I
zxdx^l%y(5p?#X~WTM5@-A?n{P#kDy9pdlu+px{^<?d)@WVFIS_WwU%paiYEdRwXf5
zSRuJbD_VKQ_*+)8V0CT&jb#4&Y1^C9Iet%8z%}#TW8J-vn^br$thx_w_!81~yaQAF
z?09><Gx}0zyAU81zgbVE!Bh{{L!P*J!UkEA@Na1MG{>;VkQ^1TXD~b>yLn;;)BotV
z>e`L?(&%{D);4UW`A}c`C^pt*u(Qr3%qXS2Zx!yg%{_DW2^~>tl9GqNGl?I@g5|p(
zU@ys(wxBi+Gxm77nTA0r57K6iw~C32qLJiL0qGe`#|s@wFs<*~2YfE;3+1x=sE)F_
zPdUlcoCw*3a{sb(pOS`y<gc7<^f87Gf<*jIiNvziZ~b6j29ra~$YsAIe4bcNd6;3r
z&@jI=5hjrjZA}=P&RA@EY&cpB-kj#cmDWfO;wBdd_*`m5tNdHeGV9XIc>%wlUy|RY
ztW+<wUqbQw`2~ZJ4XF>S-R0p|oup9X=N%dtuGRrfh%AvFxr$DB_Ni;~3ydB@2l~~4
z!lg9ACTHX|G75@^eEu*Q;F+2!Qo04$zppc(K;hVU$*IH?U)|7!Msq<VG=FyrmDf;#
zD8qCGpb)(wsLOK@0KF%b^}IP)JLzM*&xm0>;<YuSe`C^EaxRT<wg&iz7l>m%3nP22
zz=&kAMx!&3QE#)Iu*}9#h~M`ypS~TDdXBM!9g7#=l4If-E0LFH&I&a$Zstg&HT=<;
z5c{y{)$yHv_mTTpl=EWpw@QAY$Qy)m)qC0;9f~?W*Py58VQV4$X7%$#ns%nKz<S?Q
z8oOyImvZ31e#x$X-~#rlu=Ews{jv=1#kQhSx2S?KY<FfxIUz!nJscSpCm~Ko#}&S@
zXlxYtd;Ww<tnmsLn&O3A|9k#+mT&=kxQlbinvRy%2X*Z#9WCLSng>-N6_xSY3U!SM
zF`);d(@<eC@li3c`Ug-^F-;*TR6~0VuSoFUe+XzVyj;ABf3e_r_&0(7eDVCxs0scx
z|3BBz%ZQhomtO<#PsFPiKK^$ra~bb)ckvfq>jmEb*=zg)r2+mkJ^yC};f)eqChN<#
zm$$}Ww(;bbHp*qw%LV?IDIvvwFZas`mrLYt2v`40!Dy)w5dQj1bn)z5IQ9NB*?$4K
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;