Skip to content

Commit 52852c2

Browse files
committed
MOBILE-3401 recaptcha: Fix recaptcha callbacks in iOS
1 parent 678f2e6 commit 52852c2

File tree

4 files changed

+118
-17
lines changed

4 files changed

+118
-17
lines changed

package-lock.json

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/assets/js/iframe-recaptcha.js

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// (C) Copyright 2015 Moodle Pty Ltd.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
(function () {
16+
var url = location.href;
17+
18+
if (!url.match(/^https?:\/\//i) || !url.match(/\/webservice\/recaptcha\.php/i)) {
19+
// Not the recaptcha script, stop.
20+
return;
21+
}
22+
23+
// Define recaptcha callbacks.
24+
window.recaptchacallback = function(value) {
25+
window.parent.postMessage({
26+
environment: 'moodleapp',
27+
context: 'recaptcha',
28+
action: 'callback',
29+
frameUrl: location.href,
30+
value: value,
31+
}, '*');
32+
};
33+
34+
window.recaptchaexpiredcallback = function() {
35+
window.parent.postMessage({
36+
environment: 'moodleapp',
37+
context: 'recaptcha',
38+
action: 'expired',
39+
frameUrl: location.href,
40+
}, '*');
41+
};
42+
})();

src/components/recaptcha/recaptchamodal.ts

Lines changed: 64 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15-
import { Component } from '@angular/core';
15+
import { Component, OnDestroy } from '@angular/core';
1616
import { ViewController, NavParams } from 'ionic-angular';
1717

1818
/**
@@ -22,13 +22,19 @@ import { ViewController, NavParams } from 'ionic-angular';
2222
selector: 'core-recaptcha-modal',
2323
templateUrl: 'core-recaptchamodal.html'
2424
})
25-
export class CoreRecaptchaModalComponent {
25+
export class CoreRecaptchaModalComponent implements OnDestroy {
2626
expired = false;
2727
value = '';
2828
src: string;
2929

30+
protected messageListenerFunction: (event: MessageEvent) => Promise<void>;
31+
3032
constructor(protected viewCtrl: ViewController, params: NavParams) {
3133
this.src = params.get('src');
34+
35+
// Listen for messages from the iframe.
36+
this.messageListenerFunction = this.onIframeMessage.bind(this);
37+
window.addEventListener('message', this.messageListenerFunction);
3238
}
3339

3440
/**
@@ -51,18 +57,63 @@ export class CoreRecaptchaModalComponent {
5157
const contentWindow = iframe && iframe.contentWindow;
5258

5359
if (contentWindow) {
54-
// Set the callbacks we're interested in.
55-
contentWindow['recaptchacallback'] = (value): void => {
56-
this.expired = false;
57-
this.value = value;
58-
this.closeModal();
59-
};
60+
try {
61+
// Set the callbacks we're interested in.
62+
contentWindow['recaptchacallback'] = this.onRecaptchaCallback.bind(this);
63+
contentWindow['recaptchaexpiredcallback'] = this.onRecaptchaExpiredCallback.bind(this);
64+
} catch (error) {
65+
// Cannot access the window.
66+
}
67+
}
68+
}
69+
70+
/**
71+
* Treat an iframe message event.
72+
*
73+
* @param event Event.
74+
* @return Promise resolved when done.
75+
*/
76+
protected async onIframeMessage(event: MessageEvent): Promise<void> {
77+
if (!event.data || event.data.environment != 'moodleapp' || event.data.context != 'recaptcha') {
78+
return;
79+
}
6080

61-
contentWindow['recaptchaexpiredcallback'] = (): void => {
62-
// Verification expired. Check the checkbox again.
63-
this.expired = true;
64-
this.value = '';
65-
};
81+
switch (event.data.action) {
82+
case 'callback':
83+
this.onRecaptchaCallback(event.data.value);
84+
break;
85+
case 'expired':
86+
this.onRecaptchaExpiredCallback();
87+
break;
88+
89+
default:
90+
break;
6691
}
6792
}
93+
94+
/**
95+
* Recapcha callback called.
96+
*
97+
* @param value Value received.
98+
*/
99+
protected onRecaptchaCallback(value: any): void {
100+
this.expired = false;
101+
this.value = value;
102+
this.closeModal();
103+
}
104+
105+
/**
106+
* Recapcha expired callback called.
107+
*/
108+
protected onRecaptchaExpiredCallback(): void {
109+
this.expired = true;
110+
this.value = '';
111+
}
112+
113+
/**
114+
* Component destroyed.
115+
*/
116+
ngOnDestroy(): void {
117+
window.removeEventListener('message', this.messageListenerFunction);
118+
}
68119
}

src/providers/utils/iframe.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ import { CoreUtilsProvider } from './utils';
2727
import { CoreContentLinksHelperProvider } from '@core/contentlinks/providers/helper';
2828
import { makeSingleton } from '@singletons/core.singletons';
2929
import { CoreUrl } from '@singletons/url';
30-
import { WKUserScriptWindow } from 'cordova-plugin-wkuserscript';
30+
import { WKUserScriptWindow, WKUserScriptInjectionTime } from 'cordova-plugin-wkuserscript';
3131

3232
/*
3333
* "Utils" service with helper functions for iframes, embed and similar.
@@ -50,8 +50,16 @@ export class CoreIframeUtilsProvider {
5050
if (platform.is('ios') && win.WKUserScript) {
5151
platform.ready().then(() => {
5252
// Inject code to the iframes because we cannot access the online ones.
53-
const path = textUtils.concatenatePaths(fileProvider.getWWWAbsolutePath(), 'assets/js/iframe-treat-links.js');
54-
win.WKUserScript.addScript({id: 'CoreIframeUtilsScript', file: path});
53+
const wwwPath = fileProvider.getWWWAbsolutePath();
54+
const linksPath = textUtils.concatenatePaths(wwwPath, 'assets/js/iframe-treat-links.js');
55+
const recaptchaPath = textUtils.concatenatePaths(wwwPath, 'assets/js/iframe-recaptcha.js');
56+
57+
win.WKUserScript.addScript({id: 'CoreIframeUtilsLinksScript', file: linksPath});
58+
win.WKUserScript.addScript({
59+
id: 'CoreIframeUtilsRecaptchaScript',
60+
file: recaptchaPath,
61+
injectionTime: WKUserScriptInjectionTime.END,
62+
});
5563

5664
// Handle post messages received by iframes.
5765
window.addEventListener('message', this.handleIframeMessage.bind(this));

0 commit comments

Comments
 (0)