Merge pull request #2415 from dpalou/MOBILE-3401

Mobile 3401
main
Juan Leyva 2020-06-18 15:59:17 +02:00 committed by GitHub
commit 7abc385935
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 521 additions and 169 deletions

122
package-lock.json generated
View File

@ -3768,8 +3768,12 @@
"resolved": "https://registry.npmjs.org/cordova-plugin-whitelist/-/cordova-plugin-whitelist-1.3.4.tgz", "resolved": "https://registry.npmjs.org/cordova-plugin-whitelist/-/cordova-plugin-whitelist-1.3.4.tgz",
"integrity": "sha512-EYC5eQFVkoYXq39l7tYKE6lEjHJ04mvTmKXxGL7quHLdFPfJMNzru/UYpn92AOfpl3PQaZmou78C7EgmFOwFQQ==" "integrity": "sha512-EYC5eQFVkoYXq39l7tYKE6lEjHJ04mvTmKXxGL7quHLdFPfJMNzru/UYpn92AOfpl3PQaZmou78C7EgmFOwFQQ=="
}, },
"cordova-plugin-wkuserscript": {
"version": "git+https://github.com/moodlemobile/cordova-plugin-wkuserscript.git#6413f4bb3c2565f353e690b5c1450b69ad9e860e",
"from": "git+https://github.com/moodlemobile/cordova-plugin-wkuserscript.git"
},
"cordova-plugin-wkwebview-cookies": { "cordova-plugin-wkwebview-cookies": {
"version": "git+https://github.com/moodlemobile/cordova-plugin-wkwebview-cookies.git#8e319b9cc5887611bd8972152e4377757986d570", "version": "git+https://github.com/moodlemobile/cordova-plugin-wkwebview-cookies.git#8c3a289e29b33edecff15f470c1630baf4ec3e88",
"from": "git+https://github.com/moodlemobile/cordova-plugin-wkwebview-cookies.git" "from": "git+https://github.com/moodlemobile/cordova-plugin-wkwebview-cookies.git"
}, },
"cordova-plugin-zip": { "cordova-plugin-zip": {
@ -6232,8 +6236,7 @@
"ansi-regex": { "ansi-regex": {
"version": "2.1.1", "version": "2.1.1",
"resolved": false, "resolved": false,
"integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8="
"optional": true
}, },
"aproba": { "aproba": {
"version": "1.2.0", "version": "1.2.0",
@ -6254,14 +6257,12 @@
"balanced-match": { "balanced-match": {
"version": "1.0.0", "version": "1.0.0",
"resolved": false, "resolved": false,
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c="
"optional": true
}, },
"brace-expansion": { "brace-expansion": {
"version": "1.1.11", "version": "1.1.11",
"resolved": false, "resolved": false,
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
"optional": true,
"requires": { "requires": {
"balanced-match": "^1.0.0", "balanced-match": "^1.0.0",
"concat-map": "0.0.1" "concat-map": "0.0.1"
@ -6276,20 +6277,17 @@
"code-point-at": { "code-point-at": {
"version": "1.1.0", "version": "1.1.0",
"resolved": false, "resolved": false,
"integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c="
"optional": true
}, },
"concat-map": { "concat-map": {
"version": "0.0.1", "version": "0.0.1",
"resolved": false, "resolved": false,
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
"optional": true
}, },
"console-control-strings": { "console-control-strings": {
"version": "1.1.0", "version": "1.1.0",
"resolved": false, "resolved": false,
"integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4="
"optional": true
}, },
"core-util-is": { "core-util-is": {
"version": "1.0.2", "version": "1.0.2",
@ -6414,8 +6412,7 @@
"inherits": { "inherits": {
"version": "2.0.3", "version": "2.0.3",
"resolved": false, "resolved": false,
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
"optional": true
}, },
"ini": { "ini": {
"version": "1.3.5", "version": "1.3.5",
@ -6427,7 +6424,6 @@
"version": "1.0.0", "version": "1.0.0",
"resolved": false, "resolved": false,
"integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
"optional": true,
"requires": { "requires": {
"number-is-nan": "^1.0.0" "number-is-nan": "^1.0.0"
} }
@ -6442,7 +6438,6 @@
"version": "3.0.4", "version": "3.0.4",
"resolved": false, "resolved": false,
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
"optional": true,
"requires": { "requires": {
"brace-expansion": "^1.1.7" "brace-expansion": "^1.1.7"
} }
@ -6486,7 +6481,6 @@
"version": "0.5.1", "version": "0.5.1",
"resolved": false, "resolved": false,
"integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
"optional": true,
"requires": { "requires": {
"minimist": "0.0.8" "minimist": "0.0.8"
}, },
@ -6494,8 +6488,7 @@
"minimist": { "minimist": {
"version": "0.0.8", "version": "0.0.8",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
"integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0="
"optional": true
} }
} }
}, },
@ -6594,8 +6587,7 @@
"number-is-nan": { "number-is-nan": {
"version": "1.0.1", "version": "1.0.1",
"resolved": false, "resolved": false,
"integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0="
"optional": true
}, },
"object-assign": { "object-assign": {
"version": "4.1.1", "version": "4.1.1",
@ -6607,7 +6599,6 @@
"version": "1.4.0", "version": "1.4.0",
"resolved": false, "resolved": false,
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
"optional": true,
"requires": { "requires": {
"wrappy": "1" "wrappy": "1"
} }
@ -6729,7 +6720,6 @@
"version": "1.0.2", "version": "1.0.2",
"resolved": false, "resolved": false,
"integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
"optional": true,
"requires": { "requires": {
"code-point-at": "^1.0.0", "code-point-at": "^1.0.0",
"is-fullwidth-code-point": "^1.0.0", "is-fullwidth-code-point": "^1.0.0",
@ -6749,7 +6739,6 @@
"version": "3.0.1", "version": "3.0.1",
"resolved": false, "resolved": false,
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
"optional": true,
"requires": { "requires": {
"ansi-regex": "^2.0.0" "ansi-regex": "^2.0.0"
} }
@ -6805,8 +6794,7 @@
"wrappy": { "wrappy": {
"version": "1.0.2", "version": "1.0.2",
"resolved": false, "resolved": false,
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
"optional": true
}, },
"yallist": { "yallist": {
"version": "3.0.3", "version": "3.0.3",
@ -7290,8 +7278,7 @@
"version": "2.1.1", "version": "2.1.1",
"resolved": false, "resolved": false,
"integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
"dev": true, "dev": true
"optional": true
}, },
"aproba": { "aproba": {
"version": "1.2.0", "version": "1.2.0",
@ -7315,15 +7302,13 @@
"version": "1.0.0", "version": "1.0.0",
"resolved": false, "resolved": false,
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
"dev": true, "dev": true
"optional": true
}, },
"brace-expansion": { "brace-expansion": {
"version": "1.1.11", "version": "1.1.11",
"resolved": false, "resolved": false,
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
"dev": true, "dev": true,
"optional": true,
"requires": { "requires": {
"balanced-match": "^1.0.0", "balanced-match": "^1.0.0",
"concat-map": "0.0.1" "concat-map": "0.0.1"
@ -7340,22 +7325,19 @@
"version": "1.1.0", "version": "1.1.0",
"resolved": false, "resolved": false,
"integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=",
"dev": true, "dev": true
"optional": true
}, },
"concat-map": { "concat-map": {
"version": "0.0.1", "version": "0.0.1",
"resolved": false, "resolved": false,
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
"dev": true, "dev": true
"optional": true
}, },
"console-control-strings": { "console-control-strings": {
"version": "1.1.0", "version": "1.1.0",
"resolved": false, "resolved": false,
"integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=",
"dev": true, "dev": true
"optional": true
}, },
"core-util-is": { "core-util-is": {
"version": "1.0.2", "version": "1.0.2",
@ -7486,8 +7468,7 @@
"version": "2.0.3", "version": "2.0.3",
"resolved": false, "resolved": false,
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
"dev": true, "dev": true
"optional": true
}, },
"ini": { "ini": {
"version": "1.3.5", "version": "1.3.5",
@ -7501,7 +7482,6 @@
"resolved": false, "resolved": false,
"integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
"dev": true, "dev": true,
"optional": true,
"requires": { "requires": {
"number-is-nan": "^1.0.0" "number-is-nan": "^1.0.0"
} }
@ -7518,7 +7498,6 @@
"resolved": false, "resolved": false,
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
"dev": true, "dev": true,
"optional": true,
"requires": { "requires": {
"brace-expansion": "^1.1.7" "brace-expansion": "^1.1.7"
} }
@ -7527,15 +7506,13 @@
"version": "0.0.8", "version": "0.0.8",
"resolved": false, "resolved": false,
"integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
"dev": true, "dev": true
"optional": true
}, },
"minipass": { "minipass": {
"version": "2.3.5", "version": "2.3.5",
"resolved": false, "resolved": false,
"integrity": "sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA==", "integrity": "sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA==",
"dev": true, "dev": true,
"optional": true,
"requires": { "requires": {
"safe-buffer": "^5.1.2", "safe-buffer": "^5.1.2",
"yallist": "^3.0.0" "yallist": "^3.0.0"
@ -7556,7 +7533,6 @@
"resolved": false, "resolved": false,
"integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
"dev": true, "dev": true,
"optional": true,
"requires": { "requires": {
"minimist": "0.0.8" "minimist": "0.0.8"
} }
@ -7645,8 +7621,7 @@
"version": "1.0.1", "version": "1.0.1",
"resolved": false, "resolved": false,
"integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=",
"dev": true, "dev": true
"optional": true
}, },
"object-assign": { "object-assign": {
"version": "4.1.1", "version": "4.1.1",
@ -7660,7 +7635,6 @@
"resolved": false, "resolved": false,
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
"dev": true, "dev": true,
"optional": true,
"requires": { "requires": {
"wrappy": "1" "wrappy": "1"
} }
@ -7756,8 +7730,7 @@
"version": "5.1.2", "version": "5.1.2",
"resolved": false, "resolved": false,
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
"dev": true, "dev": true
"optional": true
}, },
"safer-buffer": { "safer-buffer": {
"version": "2.1.2", "version": "2.1.2",
@ -7799,7 +7772,6 @@
"resolved": false, "resolved": false,
"integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
"dev": true, "dev": true,
"optional": true,
"requires": { "requires": {
"code-point-at": "^1.0.0", "code-point-at": "^1.0.0",
"is-fullwidth-code-point": "^1.0.0", "is-fullwidth-code-point": "^1.0.0",
@ -7821,7 +7793,6 @@
"resolved": false, "resolved": false,
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
"dev": true, "dev": true,
"optional": true,
"requires": { "requires": {
"ansi-regex": "^2.0.0" "ansi-regex": "^2.0.0"
} }
@ -7870,15 +7841,13 @@
"version": "1.0.2", "version": "1.0.2",
"resolved": false, "resolved": false,
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
"dev": true, "dev": true
"optional": true
}, },
"yallist": { "yallist": {
"version": "3.0.3", "version": "3.0.3",
"resolved": false, "resolved": false,
"integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==", "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==",
"dev": true, "dev": true
"optional": true
} }
} }
}, },
@ -14940,8 +14909,7 @@
"version": "2.1.1", "version": "2.1.1",
"resolved": false, "resolved": false,
"integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
"dev": true, "dev": true
"optional": true
}, },
"aproba": { "aproba": {
"version": "1.2.0", "version": "1.2.0",
@ -14965,15 +14933,13 @@
"version": "1.0.0", "version": "1.0.0",
"resolved": false, "resolved": false,
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
"dev": true, "dev": true
"optional": true
}, },
"brace-expansion": { "brace-expansion": {
"version": "1.1.11", "version": "1.1.11",
"resolved": false, "resolved": false,
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
"dev": true, "dev": true,
"optional": true,
"requires": { "requires": {
"balanced-match": "^1.0.0", "balanced-match": "^1.0.0",
"concat-map": "0.0.1" "concat-map": "0.0.1"
@ -14990,22 +14956,19 @@
"version": "1.1.0", "version": "1.1.0",
"resolved": false, "resolved": false,
"integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=",
"dev": true, "dev": true
"optional": true
}, },
"concat-map": { "concat-map": {
"version": "0.0.1", "version": "0.0.1",
"resolved": false, "resolved": false,
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
"dev": true, "dev": true
"optional": true
}, },
"console-control-strings": { "console-control-strings": {
"version": "1.1.0", "version": "1.1.0",
"resolved": false, "resolved": false,
"integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=",
"dev": true, "dev": true
"optional": true
}, },
"core-util-is": { "core-util-is": {
"version": "1.0.2", "version": "1.0.2",
@ -15136,8 +15099,7 @@
"version": "2.0.3", "version": "2.0.3",
"resolved": false, "resolved": false,
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
"dev": true, "dev": true
"optional": true
}, },
"ini": { "ini": {
"version": "1.3.5", "version": "1.3.5",
@ -15151,7 +15113,6 @@
"resolved": false, "resolved": false,
"integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
"dev": true, "dev": true,
"optional": true,
"requires": { "requires": {
"number-is-nan": "^1.0.0" "number-is-nan": "^1.0.0"
} }
@ -15168,7 +15129,6 @@
"resolved": false, "resolved": false,
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
"dev": true, "dev": true,
"optional": true,
"requires": { "requires": {
"brace-expansion": "^1.1.7" "brace-expansion": "^1.1.7"
} }
@ -15177,15 +15137,13 @@
"version": "0.0.8", "version": "0.0.8",
"resolved": false, "resolved": false,
"integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
"dev": true, "dev": true
"optional": true
}, },
"minipass": { "minipass": {
"version": "2.3.5", "version": "2.3.5",
"resolved": false, "resolved": false,
"integrity": "sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA==", "integrity": "sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA==",
"dev": true, "dev": true,
"optional": true,
"requires": { "requires": {
"safe-buffer": "^5.1.2", "safe-buffer": "^5.1.2",
"yallist": "^3.0.0" "yallist": "^3.0.0"
@ -15206,7 +15164,6 @@
"resolved": false, "resolved": false,
"integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
"dev": true, "dev": true,
"optional": true,
"requires": { "requires": {
"minimist": "0.0.8" "minimist": "0.0.8"
} }
@ -15295,8 +15252,7 @@
"version": "1.0.1", "version": "1.0.1",
"resolved": false, "resolved": false,
"integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=",
"dev": true, "dev": true
"optional": true
}, },
"object-assign": { "object-assign": {
"version": "4.1.1", "version": "4.1.1",
@ -15310,7 +15266,6 @@
"resolved": false, "resolved": false,
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
"dev": true, "dev": true,
"optional": true,
"requires": { "requires": {
"wrappy": "1" "wrappy": "1"
} }
@ -15406,8 +15361,7 @@
"version": "5.1.2", "version": "5.1.2",
"resolved": false, "resolved": false,
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
"dev": true, "dev": true
"optional": true
}, },
"safer-buffer": { "safer-buffer": {
"version": "2.1.2", "version": "2.1.2",
@ -15449,7 +15403,6 @@
"resolved": false, "resolved": false,
"integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
"dev": true, "dev": true,
"optional": true,
"requires": { "requires": {
"code-point-at": "^1.0.0", "code-point-at": "^1.0.0",
"is-fullwidth-code-point": "^1.0.0", "is-fullwidth-code-point": "^1.0.0",
@ -15471,7 +15424,6 @@
"resolved": false, "resolved": false,
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
"dev": true, "dev": true,
"optional": true,
"requires": { "requires": {
"ansi-regex": "^2.0.0" "ansi-regex": "^2.0.0"
} }
@ -15520,15 +15472,13 @@
"version": "1.0.2", "version": "1.0.2",
"resolved": false, "resolved": false,
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
"dev": true, "dev": true
"optional": true
}, },
"yallist": { "yallist": {
"version": "3.0.3", "version": "3.0.3",
"resolved": false, "resolved": false,
"integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==", "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==",
"dev": true, "dev": true
"optional": true
} }
} }
}, },

View File

@ -112,6 +112,7 @@
"cordova-plugin-splashscreen": "5.0.3", "cordova-plugin-splashscreen": "5.0.3",
"cordova-plugin-statusbar": "2.4.3", "cordova-plugin-statusbar": "2.4.3",
"cordova-plugin-whitelist": "1.3.4", "cordova-plugin-whitelist": "1.3.4",
"cordova-plugin-wkuserscript": "git+https://github.com/moodlemobile/cordova-plugin-wkuserscript.git",
"cordova-plugin-wkwebview-cookies": "git+https://github.com/moodlemobile/cordova-plugin-wkwebview-cookies.git", "cordova-plugin-wkwebview-cookies": "git+https://github.com/moodlemobile/cordova-plugin-wkwebview-cookies.git",
"cordova-plugin-zip": "3.1.0", "cordova-plugin-zip": "3.1.0",
"cordova-sqlite-storage": "4.0.0", "cordova-sqlite-storage": "4.0.0",
@ -211,7 +212,8 @@
}, },
"cordova-plugin-wkwebview-cookies": {}, "cordova-plugin-wkwebview-cookies": {},
"cordova-plugin-qrscanner": {}, "cordova-plugin-qrscanner": {},
"cordova-plugin-chooser": {} "cordova-plugin-chooser": {},
"cordova-plugin-wkuserscript": {}
} }
}, },
"main": "desktop/electron.js", "main": "desktop/electron.js",

View File

@ -407,7 +407,7 @@ export class AddonModH5PActivityIndexComponent extends CoreCourseModuleMainActiv
* @return Whether it's an XAPI post statement of the current activity. * @return Whether it's an XAPI post statement of the current activity.
*/ */
protected isCurrentXAPIPost(data: any): boolean { protected isCurrentXAPIPost(data: any): boolean {
if (data.context != 'moodleapp' || data.action != 'xapi_post_statement' || !data.statements) { if (data.environment != 'moodleapp' || data.context != 'h5p' || data.action != 'xapi_post_statement' || !data.statements) {
return false; return false;
} }

View File

@ -0,0 +1,42 @@
// (C) Copyright 2015 Moodle Pty Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
(function () {
var url = location.href;
if (!url.match(/^https?:\/\//i) || !url.match(/\/webservice\/recaptcha\.php/i)) {
// Not the recaptcha script, stop.
return;
}
// Define recaptcha callbacks.
window.recaptchacallback = function(value) {
window.parent.postMessage({
environment: 'moodleapp',
context: 'recaptcha',
action: 'callback',
frameUrl: location.href,
value: value,
}, '*');
};
window.recaptchaexpiredcallback = function() {
window.parent.postMessage({
environment: 'moodleapp',
context: 'recaptcha',
action: 'expired',
frameUrl: location.href,
}, '*');
};
})();

View File

@ -0,0 +1,210 @@
// (C) Copyright 2015 Moodle Pty Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
(function () {
var url = location.href;
if (url.match(/^moodleappfs:\/\/localhost/i) || !url.match(/^[a-z0-9]+:\/\//i)) {
// Same domain as the app, stop.
return;
}
// Redefine window.open.
window.open = function(url, name, specs) {
if (name == '_self') {
// Link should be loaded in the same frame.
location.href = toAbsolute(url);
return;
}
getRootWindow(window).postMessage({
environment: 'moodleapp',
context: 'iframe',
action: 'window_open',
frameUrl: location.href,
url: url,
name: name,
specs: specs,
}, '*');
};
// Handle link clicks.
document.addEventListener('click', (event) => {
if (event.defaultPrevented) {
// Event already prevented by some other code.
return;
}
// Find the link being clicked.
var el = event.target;
while (el && el.tagName !== 'A') {
el = el.parentElement;
}
if (!el || el.treated) {
return;
}
// Add click listener to the link, this way if the iframe has added a listener to the link it will be executed first.
el.treated = true;
el.addEventListener('click', function(event) {
linkClicked(el, event);
});
}, {
capture: true // Use capture to fix this listener not called if the element clicked is too deep in the DOM.
});
/**
* Concatenate two paths, adding a slash between them if needed.
*
* @param leftPath Left path.
* @param rightPath Right path.
* @return Concatenated path.
*/
function concatenatePaths(leftPath, rightPath) {
if (!leftPath) {
return rightPath;
} else if (!rightPath) {
return leftPath;
}
var lastCharLeft = leftPath.slice(-1);
var firstCharRight = rightPath.charAt(0);
if (lastCharLeft === '/' && firstCharRight === '/') {
return leftPath + rightPath.substr(1);
} else if (lastCharLeft !== '/' && firstCharRight !== '/') {
return leftPath + '/' + rightPath;
} else {
return leftPath + rightPath;
}
}
/**
* Get the root window.
*
* @param win Current window to check.
* @return Root window.
*/
function getRootWindow(win) {
if (win.parent === win) {
return win;
}
return getRootWindow(win.parent);
}
/**
* Get the scheme from a URL.
*
* @param url URL to treat.
* @return Scheme, undefined if no scheme found.
*/
function getUrlScheme(url) {
if (!url) {
return;
}
var matches = url.match(/^([a-z][a-z0-9+\-.]*):/);
if (matches && matches[1]) {
return matches[1];
}
}
/**
* Check if a URL is absolute.
*
* @param url URL to treat.
* @return Whether it's absolute.
*/
function isAbsoluteUrl(url) {
return /^[^:]{2,}:\/\//i.test(url);
}
/**
* Check whether a URL scheme belongs to a local file.
*
* @param scheme Scheme to check.
* @return Whether the scheme belongs to a local file.
*/
function isLocalFileUrlScheme(scheme) {
if (scheme) {
scheme = scheme.toLowerCase();
}
return scheme == 'cdvfile' ||
scheme == 'file' ||
scheme == 'filesystem' ||
scheme == 'moodleappfs';
}
/**
* Handle a click on an anchor element.
*
* @param link Anchor element clicked.
* @param event Click event.
*/
function linkClicked(link, event) {
if (event.defaultPrevented) {
// Event already prevented by some other code.
return;
}
var linkScheme = getUrlScheme(link.href);
var pageScheme = getUrlScheme(location.href);
var isTargetSelf = !link.target || link.target == '_self';
if (!link.href || linkScheme == 'javascript') {
// Links with no URL and Javascript links are ignored.
return;
}
event.preventDefault();
if (isTargetSelf && (isLocalFileUrlScheme(linkScheme) || !isLocalFileUrlScheme(pageScheme))) {
// Link should be loaded in the same frame. Don't do it if link is online and frame is local.
location.href = toAbsolute(link.href);
return;
}
getRootWindow(window).postMessage({
environment: 'moodleapp',
context: 'iframe',
action: 'link_clicked',
frameUrl: location.href,
link: {href: link.href, target: link.target},
}, '*');
}
/**
* Convert a URL to an absolute URL if needed using the frame src.
*
* @param url URL to convert.
* @return Absolute URL.
*/
function toAbsolute(url) {
if (isAbsoluteUrl(url)) {
return url;
}
// It's a relative URL, use the frame src to create the full URL.
var pathToDir = location.href.substring(0, location.href.lastIndexOf('/'));
return concatenatePaths(pathToDir, url);
}
})();

View File

@ -1,5 +1,7 @@
<div [class.core-loading-container]="loading" [ngStyle]="{'width': iframeWidth, 'height': iframeHeight}"> <div [class.core-loading-container]="loading || !safeUrl" [ngStyle]="{'width': iframeWidth, 'height': iframeHeight}">
<iframe #iframe [hidden]="loading" class="core-iframe" [ngStyle]="{'width': iframeWidth, 'height': iframeHeight}" [src]="safeUrl" [attr.allowfullscreen]="allowFullscreen ? 'allowfullscreen' : null"></iframe> <!-- Don't add the iframe until the safeUrl is set, adding an iframe with null as src causes the iframe to load the whole app. -->
<iframe #iframe *ngIf="safeUrl" [hidden]="loading" class="core-iframe" [ngStyle]="{'width': iframeWidth, 'height': iframeHeight}" [src]="safeUrl" [attr.allowfullscreen]="allowFullscreen ? 'allowfullscreen' : null"></iframe>
<span class="core-loading-spinner"> <span class="core-loading-spinner">
<ion-spinner *ngIf="loading"></ion-spinner> <ion-spinner *ngIf="loading"></ion-spinner>
</span> </span>

View File

@ -13,7 +13,7 @@
// limitations under the License. // limitations under the License.
import { import {
Component, Input, Output, OnInit, ViewChild, ElementRef, EventEmitter, OnChanges, SimpleChange, Optional Component, Input, Output, ViewChild, ElementRef, EventEmitter, OnChanges, SimpleChange, Optional
} from '@angular/core'; } from '@angular/core';
import { DomSanitizer, SafeResourceUrl } from '@angular/platform-browser'; import { DomSanitizer, SafeResourceUrl } from '@angular/platform-browser';
import { NavController, Platform } from 'ionic-angular'; import { NavController, Platform } from 'ionic-angular';
@ -25,12 +25,13 @@ import { CoreIframeUtilsProvider } from '@providers/utils/iframe';
import { CoreUtilsProvider } from '@providers/utils/utils'; import { CoreUtilsProvider } from '@providers/utils/utils';
import { CoreSplitViewComponent } from '@components/split-view/split-view'; import { CoreSplitViewComponent } from '@components/split-view/split-view';
import { CoreUrl } from '@singletons/url'; import { CoreUrl } from '@singletons/url';
import { WKWebViewCookiesWindow } from 'cordova-plugin-wkwebview-cookies';
@Component({ @Component({
selector: 'core-iframe', selector: 'core-iframe',
templateUrl: 'core-iframe.html' templateUrl: 'core-iframe.html'
}) })
export class CoreIframeComponent implements OnInit, OnChanges { export class CoreIframeComponent implements OnChanges {
@ViewChild('iframe') iframe: ElementRef; @ViewChild('iframe') iframe: ElementRef;
@Input() src: string; @Input() src: string;
@ -43,6 +44,7 @@ export class CoreIframeComponent implements OnInit, OnChanges {
protected logger; protected logger;
protected IFRAME_TIMEOUT = 15000; protected IFRAME_TIMEOUT = 15000;
protected initialized = false;
constructor(logger: CoreLoggerProvider, constructor(logger: CoreLoggerProvider,
protected iframeUtils: CoreIframeUtilsProvider, protected iframeUtils: CoreIframeUtilsProvider,
@ -59,9 +61,15 @@ export class CoreIframeComponent implements OnInit, OnChanges {
} }
/** /**
* Component being initialized. * Init the data.
*/ */
ngOnInit(): void { protected init(): void {
if (this.initialized) {
return;
}
this.initialized = true;
const iframe: HTMLIFrameElement = this.iframe && this.iframe.nativeElement; const iframe: HTMLIFrameElement = this.iframe && this.iframe.nativeElement;
this.iframeWidth = this.domUtils.formatPixelsSize(this.iframeWidth) || '100%'; this.iframeWidth = this.domUtils.formatPixelsSize(this.iframeWidth) || '100%';
@ -101,7 +109,7 @@ export class CoreIframeComponent implements OnInit, OnChanges {
if (this.platform.is('ios') && !this.urlUtils.isLocalFileUrl(url)) { if (this.platform.is('ios') && !this.urlUtils.isLocalFileUrl(url)) {
// Save a "fake" cookie for the iframe's domain to fix a bug in WKWebView. // Save a "fake" cookie for the iframe's domain to fix a bug in WKWebView.
try { try {
const win = <any> window; const win = <WKWebViewCookiesWindow> window;
const urlParts = CoreUrl.parse(url); const urlParts = CoreUrl.parse(url);
await win.WKWebViewCookies.setCookie({ await win.WKWebViewCookies.setCookie({
@ -116,6 +124,11 @@ export class CoreIframeComponent implements OnInit, OnChanges {
} }
this.safeUrl = this.sanitizer.bypassSecurityTrustResourceUrl(CoreFile.instance.convertFileSrc(url)); this.safeUrl = this.sanitizer.bypassSecurityTrustResourceUrl(CoreFile.instance.convertFileSrc(url));
// Now that the URL has been set, initialize the iframe. Wait for the iframe to the added to the DOM.
setTimeout(() => {
this.init();
});
} }
} }
} }

View File

@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
import { Component } from '@angular/core'; import { Component, OnDestroy } from '@angular/core';
import { ViewController, NavParams } from 'ionic-angular'; import { ViewController, NavParams } from 'ionic-angular';
/** /**
@ -22,13 +22,19 @@ import { ViewController, NavParams } from 'ionic-angular';
selector: 'core-recaptcha-modal', selector: 'core-recaptcha-modal',
templateUrl: 'core-recaptchamodal.html' templateUrl: 'core-recaptchamodal.html'
}) })
export class CoreRecaptchaModalComponent { export class CoreRecaptchaModalComponent implements OnDestroy {
expired = false; expired = false;
value = ''; value = '';
src: string; src: string;
protected messageListenerFunction: (event: MessageEvent) => Promise<void>;
constructor(protected viewCtrl: ViewController, params: NavParams) { constructor(protected viewCtrl: ViewController, params: NavParams) {
this.src = params.get('src'); this.src = params.get('src');
// Listen for messages from the iframe.
this.messageListenerFunction = this.onIframeMessage.bind(this);
window.addEventListener('message', this.messageListenerFunction);
} }
/** /**
@ -51,18 +57,63 @@ export class CoreRecaptchaModalComponent {
const contentWindow = iframe && iframe.contentWindow; const contentWindow = iframe && iframe.contentWindow;
if (contentWindow) { if (contentWindow) {
try {
// Set the callbacks we're interested in. // Set the callbacks we're interested in.
contentWindow['recaptchacallback'] = (value): void => { contentWindow['recaptchacallback'] = this.onRecaptchaCallback.bind(this);
contentWindow['recaptchaexpiredcallback'] = this.onRecaptchaExpiredCallback.bind(this);
} catch (error) {
// Cannot access the window.
}
}
}
/**
* Treat an iframe message event.
*
* @param event Event.
* @return Promise resolved when done.
*/
protected async onIframeMessage(event: MessageEvent): Promise<void> {
if (!event.data || event.data.environment != 'moodleapp' || event.data.context != 'recaptcha') {
return;
}
switch (event.data.action) {
case 'callback':
this.onRecaptchaCallback(event.data.value);
break;
case 'expired':
this.onRecaptchaExpiredCallback();
break;
default:
break;
}
}
/**
* Recapcha callback called.
*
* @param value Value received.
*/
protected onRecaptchaCallback(value: any): void {
this.expired = false; this.expired = false;
this.value = value; this.value = value;
this.closeModal(); this.closeModal();
}; }
contentWindow['recaptchaexpiredcallback'] = (): void => { /**
// Verification expired. Check the checkbox again. * Recapcha expired callback called.
*/
protected onRecaptchaExpiredCallback(): void {
this.expired = true; this.expired = true;
this.value = ''; this.value = '';
}; }
}
/**
* Component destroyed.
*/
ngOnDestroy(): void {
window.removeEventListener('message', this.messageListenerFunction);
} }
} }

View File

@ -80,7 +80,8 @@ H5PEmbedCommunicator = (function() {
*/ */
self.post = function(component, statements) { self.post = function(component, statements) {
window.parent.postMessage({ window.parent.postMessage({
context: 'moodleapp', environment: 'moodleapp',
context: 'h5p',
action: 'xapi_post_statement', action: 'xapi_post_statement',
component: component, component: component,
statements: statements, statements: statements,

View File

@ -1229,7 +1229,7 @@ export class CoreFileProvider {
} }
/** /**
* Get the full path to the www folder at runtime. * Get the path to the www folder at runtime based on the WebView URL.
* *
* @return Path. * @return Path.
*/ */
@ -1243,6 +1243,20 @@ export class CoreFileProvider {
return window.location.href; return window.location.href;
} }
/**
* Get the full path to the www folder.
*
* @return Path.
*/
getWWWAbsolutePath(): string {
if (cordova && cordova.file && cordova.file.applicationDirectory) {
return this.textUtils.concatenatePaths(cordova.file.applicationDirectory, 'www');
}
// Cannot use Cordova to get it, use the WebView URL.
return this.getWWWPath();
}
/** /**
* Helper function to call Ionic WebView convertFileSrc only in the needed platforms. * Helper function to call Ionic WebView convertFileSrc only in the needed platforms.
* This is needed to make files work with the Ionic WebView plugin. * This is needed to make files work with the Ionic WebView plugin.

View File

@ -27,6 +27,7 @@ import { CoreUtilsProvider } from './utils';
import { CoreContentLinksHelperProvider } from '@core/contentlinks/providers/helper'; import { CoreContentLinksHelperProvider } from '@core/contentlinks/providers/helper';
import { makeSingleton } from '@singletons/core.singletons'; import { makeSingleton } from '@singletons/core.singletons';
import { CoreUrl } from '@singletons/url'; import { CoreUrl } from '@singletons/url';
import { WKUserScriptWindow, WKUserScriptInjectionTime } from 'cordova-plugin-wkuserscript';
/* /*
* "Utils" service with helper functions for iframes, embed and similar. * "Utils" service with helper functions for iframes, embed and similar.
@ -43,6 +44,27 @@ export class CoreIframeUtilsProvider {
private translate: TranslateService, private network: Network, private zone: NgZone, private config: Config, private translate: TranslateService, private network: Network, private zone: NgZone, private config: Config,
private contentLinksHelper: CoreContentLinksHelperProvider) { private contentLinksHelper: CoreContentLinksHelperProvider) {
this.logger = logger.getInstance('CoreUtilsProvider'); this.logger = logger.getInstance('CoreUtilsProvider');
const win = <WKUserScriptWindow> window;
if (platform.is('ios') && win.WKUserScript) {
platform.ready().then(() => {
// Inject code to the iframes because we cannot access the online ones.
const wwwPath = fileProvider.getWWWAbsolutePath();
const linksPath = textUtils.concatenatePaths(wwwPath, 'assets/js/iframe-treat-links.js');
const recaptchaPath = textUtils.concatenatePaths(wwwPath, 'assets/js/iframe-recaptcha.js');
win.WKUserScript.addScript({id: 'CoreIframeUtilsLinksScript', file: linksPath});
win.WKUserScript.addScript({
id: 'CoreIframeUtilsRecaptchaScript',
file: recaptchaPath,
injectionTime: WKUserScriptInjectionTime.END,
});
// Handle post messages received by iframes.
window.addEventListener('message', this.handleIframeMessage.bind(this));
});
}
} }
/** /**
@ -186,6 +208,30 @@ export class CoreIframeUtilsProvider {
return { window: contentWindow, document: contentDocument }; return { window: contentWindow, document: contentDocument };
} }
/**
* Handle some iframe messages.
*
* @param event Message event.
*/
handleIframeMessage(event: MessageEvent): void {
if (!event.data || event.data.environment != 'moodleapp' || event.data.context != 'iframe') {
return;
}
switch (event.data.action) {
case 'window_open':
this.windowOpen(event.data.url, event.data.name);
break;
case 'link_clicked':
this.linkClicked(event.data.link);
break;
default:
break;
}
}
/** /**
* Redefine the open method in the contentWindow of an element and the sub frames. * Redefine the open method in the contentWindow of an element and the sub frames.
* Please notice that the element should be an iframe, embed or similar. * Please notice that the element should be an iframe, embed or similar.
@ -198,56 +244,10 @@ export class CoreIframeUtilsProvider {
redefineWindowOpen(element: any, contentWindow: Window, contentDocument: Document, navCtrl?: NavController): void { redefineWindowOpen(element: any, contentWindow: Window, contentDocument: Document, navCtrl?: NavController): void {
if (contentWindow) { if (contentWindow) {
// Intercept window.open. // Intercept window.open.
contentWindow.open = (url: string, target: string): Window => { contentWindow.open = (url: string, name: string): Window => {
const scheme = this.urlUtils.getUrlScheme(url); this.windowOpen(url, name, element, navCtrl);
if (!scheme) {
// It's a relative URL, use the frame src to create the full URL.
const src = element.src || element.data;
if (src) {
const dirAndFile = this.fileProvider.getFileAndDirectoryFromPath(src);
if (dirAndFile.directory) {
url = this.textUtils.concatenatePaths(dirAndFile.directory, url);
} else {
this.logger.warn('Cannot get iframe dir path to open relative url', url, element);
return null; return null;
}
} else {
this.logger.warn('Cannot get iframe src to open relative url', url, element);
return null;
}
}
if (target == '_self') {
// Link should be loaded in the same frame.
if (element.tagName.toLowerCase() == 'object') {
element.setAttribute('data', url);
} else {
element.setAttribute('src', url);
}
} else if (this.urlUtils.isLocalFileUrl(url)) {
// It's a local file.
this.utils.openFile(url).catch((error) => {
this.domUtils.showErrorModal(error);
});
} else {
// It's an external link, check if it can be opened in the app.
this.contentLinksHelper.handleLink(url, undefined, navCtrl, true, true).then((treated) => {
if (!treated) {
// Not opened in the app, open with browser. Check if we need to auto-login
if (!this.sitesProvider.isLoggedIn()) {
// Not logged in, cannot auto-login.
this.utils.openInBrowser(url);
} else {
this.sitesProvider.getCurrentSite().openInBrowserWithAutoLoginIfSameSite(url);
}
}
});
}
// We cannot create new Window objects directly, return null which is a valid return value for Window.open().
return null;
}; };
} }
@ -329,21 +329,88 @@ export class CoreIframeUtilsProvider {
// Add click listener to the link, this way if the iframe has added a listener to the link it will be executed first. // Add click listener to the link, this way if the iframe has added a listener to the link it will be executed first.
link.treated = true; link.treated = true;
link.addEventListener('click', this.linkClicked.bind(this, element, link)); link.addEventListener('click', this.linkClicked.bind(this, link, element));
}, { }, {
capture: true // Use capture to fix this listener not called if the element clicked is too deep in the DOM. capture: true // Use capture to fix this listener not called if the element clicked is too deep in the DOM.
}); });
} }
/**
* Handle a window.open called by a frame.
*
* @param url URL passed to window.open.
* @param name Name passed to window.open.
* @param element HTML element of the frame.
* @param navCtrl NavController to use if a link can be opened in the app.
* @return Promise resolved when done.
*/
protected async windowOpen(url: string, name: string, element?: any, navCtrl?: NavController): Promise<void> {
const scheme = this.urlUtils.getUrlScheme(url);
if (!scheme) {
// It's a relative URL, use the frame src to create the full URL.
const src = element && (element.src || element.data);
if (src) {
const dirAndFile = this.fileProvider.getFileAndDirectoryFromPath(src);
if (dirAndFile.directory) {
url = this.textUtils.concatenatePaths(dirAndFile.directory, url);
} else {
this.logger.warn('Cannot get iframe dir path to open relative url', url, element);
return;
}
} else {
this.logger.warn('Cannot get iframe src to open relative url', url, element);
return;
}
}
if (name == '_self') {
// Link should be loaded in the same frame.
if (!element) {
this.logger.warn('Cannot load URL in iframe because the element was not supplied', url);
return;
}
if (element.tagName.toLowerCase() == 'object') {
element.setAttribute('data', url);
} else {
element.setAttribute('src', url);
}
} else if (this.urlUtils.isLocalFileUrl(url)) {
// It's a local file.
try {
await this.utils.openFile(url);
} catch (error) {
this.domUtils.showErrorModal(error);
}
} else {
// It's an external link, check if it can be opened in the app.
const treated = await this.contentLinksHelper.handleLink(url, undefined, navCtrl, true, true);
if (!treated) {
// Not opened in the app, open with browser. Check if we need to auto-login
if (!this.sitesProvider.isLoggedIn()) {
// Not logged in, cannot auto-login.
this.utils.openInBrowser(url);
} else {
await this.sitesProvider.getCurrentSite().openInBrowserWithAutoLoginIfSameSite(url);
}
}
}
}
/** /**
* A link inside a frame was clicked. * A link inside a frame was clicked.
* *
* @param link Data of the link clicked.
* @param element Frame element. * @param element Frame element.
* @param link Link clicked.
* @param event Click event. * @param event Click event.
*/ */
protected linkClicked(element: HTMLFrameElement | HTMLObjectElement, link: HTMLAnchorElement, event: Event): void { protected linkClicked(link: {href: string, target?: string}, element?: HTMLFrameElement | HTMLObjectElement, event?: Event)
if (event.defaultPrevented) { : void {
if (event && event.defaultPrevented) {
// Event already prevented by some other code. // Event already prevented by some other code.
return; return;
} }
@ -356,12 +423,12 @@ export class CoreIframeUtilsProvider {
if (!this.urlUtils.isLocalFileUrlScheme(urlParts.protocol, urlParts.domain)) { if (!this.urlUtils.isLocalFileUrlScheme(urlParts.protocol, urlParts.domain)) {
// Scheme suggests it's an external resource. // Scheme suggests it's an external resource.
event.preventDefault(); event && event.preventDefault();
const frameSrc = (<HTMLFrameElement> element).src || (<HTMLObjectElement> element).data; const frameSrc = element && ((<HTMLFrameElement> element).src || (<HTMLObjectElement> element).data);
// If the frame is not local, check the target to identify how to treat the link. // If the frame is not local, check the target to identify how to treat the link.
if (!this.urlUtils.isLocalFileUrl(frameSrc) && (!link.target || link.target == '_self')) { if (element && !this.urlUtils.isLocalFileUrl(frameSrc) && (!link.target || link.target == '_self')) {
// Load the link inside the frame itself. // Load the link inside the frame itself.
if (element.tagName.toLowerCase() == 'object') { if (element.tagName.toLowerCase() == 'object') {
element.setAttribute('data', link.href); element.setAttribute('data', link.href);
@ -380,13 +447,13 @@ export class CoreIframeUtilsProvider {
} }
} else if (link.target == '_parent' || link.target == '_top' || link.target == '_blank') { } else if (link.target == '_parent' || link.target == '_top' || link.target == '_blank') {
// Opening links with _parent, _top or _blank can break the app. We'll open it in InAppBrowser. // Opening links with _parent, _top or _blank can break the app. We'll open it in InAppBrowser.
event.preventDefault(); event && event.preventDefault();
this.utils.openFile(link.href).catch((error) => { this.utils.openFile(link.href).catch((error) => {
this.domUtils.showErrorModal(error); this.domUtils.showErrorModal(error);
}); });
} else if (this.platform.is('ios') && (!link.target || link.target == '_self')) { } else if (this.platform.is('ios') && (!link.target || link.target == '_self') && element) {
// In cordova ios 4.1.0 links inside iframes stopped working. We'll manually treat them. // In cordova ios 4.1.0 links inside iframes stopped working. We'll manually treat them.
event.preventDefault(); event && event.preventDefault();
if (element.tagName.toLowerCase() == 'object') { if (element.tagName.toLowerCase() == 'object') {
element.setAttribute('data', link.href); element.setAttribute('data', link.href);
} else { } else {