diff --git a/package-lock.json b/package-lock.json index 2b42a6992..7623fa790 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3773,7 +3773,7 @@ "from": "git+https://github.com/moodlemobile/cordova-plugin-wkuserscript.git" }, "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" }, "cordova-plugin-zip": { diff --git a/src/assets/js/iframe-recaptcha.js b/src/assets/js/iframe-recaptcha.js new file mode 100644 index 000000000..f1ff5e843 --- /dev/null +++ b/src/assets/js/iframe-recaptcha.js @@ -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, + }, '*'); + }; +})(); \ No newline at end of file diff --git a/src/components/recaptcha/recaptchamodal.ts b/src/components/recaptcha/recaptchamodal.ts index 1280e569f..2cff04c18 100644 --- a/src/components/recaptcha/recaptchamodal.ts +++ b/src/components/recaptcha/recaptchamodal.ts @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { Component } from '@angular/core'; +import { Component, OnDestroy } from '@angular/core'; import { ViewController, NavParams } from 'ionic-angular'; /** @@ -22,13 +22,19 @@ import { ViewController, NavParams } from 'ionic-angular'; selector: 'core-recaptcha-modal', templateUrl: 'core-recaptchamodal.html' }) -export class CoreRecaptchaModalComponent { +export class CoreRecaptchaModalComponent implements OnDestroy { expired = false; value = ''; src: string; + protected messageListenerFunction: (event: MessageEvent) => Promise; + constructor(protected viewCtrl: ViewController, params: NavParams) { 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; if (contentWindow) { - // Set the callbacks we're interested in. - contentWindow['recaptchacallback'] = (value): void => { - this.expired = false; - this.value = value; - this.closeModal(); - }; - - contentWindow['recaptchaexpiredcallback'] = (): void => { - // Verification expired. Check the checkbox again. - this.expired = true; - this.value = ''; - }; + try { + // Set the callbacks we're interested in. + 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 { + 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.value = value; + this.closeModal(); + } + + /** + * Recapcha expired callback called. + */ + protected onRecaptchaExpiredCallback(): void { + this.expired = true; + this.value = ''; + } + + /** + * Component destroyed. + */ + ngOnDestroy(): void { + window.removeEventListener('message', this.messageListenerFunction); + } } diff --git a/src/providers/utils/iframe.ts b/src/providers/utils/iframe.ts index e477e7401..be188bbb1 100644 --- a/src/providers/utils/iframe.ts +++ b/src/providers/utils/iframe.ts @@ -27,7 +27,7 @@ import { CoreUtilsProvider } from './utils'; import { CoreContentLinksHelperProvider } from '@core/contentlinks/providers/helper'; import { makeSingleton } from '@singletons/core.singletons'; import { CoreUrl } from '@singletons/url'; -import { WKUserScriptWindow } from 'cordova-plugin-wkuserscript'; +import { WKUserScriptWindow, WKUserScriptInjectionTime } from 'cordova-plugin-wkuserscript'; /* * "Utils" service with helper functions for iframes, embed and similar. @@ -50,8 +50,16 @@ export class CoreIframeUtilsProvider { if (platform.is('ios') && win.WKUserScript) { platform.ready().then(() => { // Inject code to the iframes because we cannot access the online ones. - const path = textUtils.concatenatePaths(fileProvider.getWWWAbsolutePath(), 'assets/js/iframe-treat-links.js'); - win.WKUserScript.addScript({id: 'CoreIframeUtilsScript', file: path}); + 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));