MOBILE-3401 ios: Fix iframe links and window.open in iOS
parent
c6dfa40351
commit
678f2e666a
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
})();
|
|
@ -25,6 +25,7 @@ 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',
|
||||||
|
@ -108,7 +109,7 @@ export class CoreIframeComponent implements 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({
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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 } 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,19 @@ 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 path = textUtils.concatenatePaths(fileProvider.getWWWAbsolutePath(), 'assets/js/iframe-treat-links.js');
|
||||||
|
win.WKUserScript.addScript({id: 'CoreIframeUtilsScript', file: path});
|
||||||
|
|
||||||
|
// Handle post messages received by iframes.
|
||||||
|
window.addEventListener('message', this.handleIframeMessage.bind(this));
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -186,6 +200,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,55 +236,9 @@ 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;
|
|
||||||
}
|
|
||||||
} 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;
|
return null;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -329,21 +321,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 +415,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 +439,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 {
|
||||||
|
|
Loading…
Reference in New Issue