MOBILE-3098 iframe: Open in app links inside iframes

main
Dani Palou 2019-07-16 08:22:58 +02:00
parent ea7caaed20
commit 8d64fea2af
4 changed files with 43 additions and 26 deletions

View File

@ -12,11 +12,15 @@
// 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, Input, Output, OnInit, ViewChild, ElementRef, EventEmitter, OnChanges, SimpleChange } from '@angular/core'; import {
Component, Input, Output, OnInit, ViewChild, ElementRef, EventEmitter, OnChanges, SimpleChange, Optional
} from '@angular/core';
import { DomSanitizer, SafeResourceUrl } from '@angular/platform-browser'; import { DomSanitizer, SafeResourceUrl } from '@angular/platform-browser';
import { NavController } from 'ionic-angular';
import { CoreLoggerProvider } from '@providers/logger'; import { CoreLoggerProvider } from '@providers/logger';
import { CoreDomUtilsProvider } from '@providers/utils/dom'; import { CoreDomUtilsProvider } from '@providers/utils/dom';
import { CoreIframeUtilsProvider } from '@providers/utils/iframe'; import { CoreIframeUtilsProvider } from '@providers/utils/iframe';
import { CoreSplitViewComponent } from '@components/split-view/split-view';
/** /**
*/ */
@ -38,7 +42,9 @@ export class CoreIframeComponent implements OnInit, OnChanges {
protected IFRAME_TIMEOUT = 15000; protected IFRAME_TIMEOUT = 15000;
constructor(logger: CoreLoggerProvider, private iframeUtils: CoreIframeUtilsProvider, private domUtils: CoreDomUtilsProvider, constructor(logger: CoreLoggerProvider, private iframeUtils: CoreIframeUtilsProvider, private domUtils: CoreDomUtilsProvider,
private sanitizer: DomSanitizer) { private sanitizer: DomSanitizer, private navCtrl: NavController,
@Optional() private svComponent: CoreSplitViewComponent) {
this.logger = logger.getInstance('CoreIframe'); this.logger = logger.getInstance('CoreIframe');
this.loaded = new EventEmitter<HTMLIFrameElement>(); this.loaded = new EventEmitter<HTMLIFrameElement>();
} }
@ -55,7 +61,8 @@ export class CoreIframeComponent implements OnInit, OnChanges {
// Show loading only with external URLs. // Show loading only with external URLs.
this.loading = !this.src || !!this.src.match(/^https?:\/\//i); this.loading = !this.src || !!this.src.match(/^https?:\/\//i);
this.iframeUtils.treatFrame(iframe); const navCtrl = this.svComponent ? this.svComponent.getMasterNav() : this.navCtrl;
this.iframeUtils.treatFrame(iframe, false, navCtrl);
if (this.loading) { if (this.loading) {
iframe.addEventListener('load', () => { iframe.addEventListener('load', () => {

View File

@ -27,7 +27,7 @@ import { CoreBlockDelegate } from '@core/block/providers/delegate';
}) })
export class CoreSitePluginsBlockComponent extends CoreBlockBaseComponent implements OnChanges { export class CoreSitePluginsBlockComponent extends CoreBlockBaseComponent implements OnChanges {
@Input() block: any; @Input() block: any;
@Input() contextLevel: number; @Input() contextLevel: string;
@Input() instanceId: number; @Input() instanceId: number;
@ViewChild(CoreSitePluginsPluginContentComponent) content: CoreSitePluginsPluginContentComponent; @ViewChild(CoreSitePluginsPluginContentComponent) content: CoreSitePluginsPluginContentComponent;

View File

@ -352,7 +352,8 @@ export class CoreFormatTextDirective implements OnChanges {
this.utils.isTrueOrOne(this.singleLine), undefined, this.highlight); this.utils.isTrueOrOne(this.singleLine), undefined, this.highlight);
}).then((formatted) => { }).then((formatted) => {
const div = document.createElement('div'), const div = document.createElement('div'),
canTreatVimeo = site && site.isVersionGreaterEqualThan(['3.3.4', '3.4']); canTreatVimeo = site && site.isVersionGreaterEqualThan(['3.3.4', '3.4']),
navCtrl = this.svComponent ? this.svComponent.getMasterNav() : this.navCtrl;
let images, let images,
anchors, anchors,
audios, audios,
@ -405,12 +406,12 @@ export class CoreFormatTextDirective implements OnChanges {
}); });
videos.forEach((video) => { videos.forEach((video) => {
this.treatVideoFilters(video); this.treatVideoFilters(video, navCtrl);
this.treatMedia(video); this.treatMedia(video);
}); });
iframes.forEach((iframe) => { iframes.forEach((iframe) => {
this.treatIframe(iframe, site, canTreatVimeo); this.treatIframe(iframe, site, canTreatVimeo, navCtrl);
}); });
// Handle buttons with inner links. // Handle buttons with inner links.
@ -439,7 +440,7 @@ export class CoreFormatTextDirective implements OnChanges {
// Handle all kind of frames. // Handle all kind of frames.
frames.forEach((frame: any) => { frames.forEach((frame: any) => {
this.iframeUtils.treatFrame(frame); this.iframeUtils.treatFrame(frame, false, navCtrl);
}); });
this.domUtils.handleBootstrapTooltips(div); this.domUtils.handleBootstrapTooltips(div);
@ -508,8 +509,9 @@ export class CoreFormatTextDirective implements OnChanges {
* Treat video filters. Currently only treating youtube video using video JS. * Treat video filters. Currently only treating youtube video using video JS.
* *
* @param {HTMLElement} el Video element. * @param {HTMLElement} el Video element.
* @param {NavController} navCtrl NavController to use.
*/ */
protected treatVideoFilters(video: HTMLElement): void { protected treatVideoFilters(video: HTMLElement, navCtrl: NavController): void {
// Treat Video JS Youtube video links and translate them to iframes. // Treat Video JS Youtube video links and translate them to iframes.
if (!video.classList.contains('video-js')) { if (!video.classList.contains('video-js')) {
return; return;
@ -534,7 +536,7 @@ export class CoreFormatTextDirective implements OnChanges {
// Replace video tag by the iframe. // Replace video tag by the iframe.
video.parentNode.replaceChild(iframe, video); video.parentNode.replaceChild(iframe, video);
this.iframeUtils.treatFrame(iframe); this.iframeUtils.treatFrame(iframe, false, navCtrl);
} }
/** /**
@ -571,8 +573,9 @@ export class CoreFormatTextDirective implements OnChanges {
* @param {HTMLIFrameElement} iframe Iframe to treat. * @param {HTMLIFrameElement} iframe Iframe to treat.
* @param {CoreSite} site Site instance. * @param {CoreSite} site Site instance.
* @param {boolean} canTreatVimeo Whether Vimeo videos can be treated in the site. * @param {boolean} canTreatVimeo Whether Vimeo videos can be treated in the site.
* @param {NavController} navCtrl NavController to use.
*/ */
protected treatIframe(iframe: HTMLIFrameElement, site: CoreSite, canTreatVimeo: boolean): void { protected treatIframe(iframe: HTMLIFrameElement, site: CoreSite, canTreatVimeo: boolean, navCtrl: NavController): void {
const src = iframe.src, const src = iframe.src,
currentSite = this.sitesProvider.getCurrentSite(); currentSite = this.sitesProvider.getCurrentSite();
@ -583,7 +586,7 @@ export class CoreFormatTextDirective implements OnChanges {
currentSite.getAutoLoginUrl(src, false).then((finalUrl) => { currentSite.getAutoLoginUrl(src, false).then((finalUrl) => {
iframe.src = finalUrl; iframe.src = finalUrl;
this.iframeUtils.treatFrame(iframe); this.iframeUtils.treatFrame(iframe, false, navCtrl);
}); });
return; return;
@ -644,7 +647,7 @@ export class CoreFormatTextDirective implements OnChanges {
} }
} }
this.iframeUtils.treatFrame(iframe); this.iframeUtils.treatFrame(iframe, false, navCtrl);
} }
/** /**

View File

@ -13,7 +13,7 @@
// limitations under the License. // limitations under the License.
import { Injectable, NgZone } from '@angular/core'; import { Injectable, NgZone } from '@angular/core';
import { Config, Platform } from 'ionic-angular'; import { Config, Platform, NavController } from 'ionic-angular';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { Network } from '@ionic-native/network'; import { Network } from '@ionic-native/network';
import { CoreAppProvider } from '../app'; import { CoreAppProvider } from '../app';
@ -191,8 +191,9 @@ export class CoreIframeUtilsProvider {
* @param {any} element Element to treat (iframe, embed, ...). * @param {any} element Element to treat (iframe, embed, ...).
* @param {Window} contentWindow The window of the element contents. * @param {Window} contentWindow The window of the element contents.
* @param {Document} contentDocument The document of the element contents. * @param {Document} contentDocument The document of the element contents.
* @param {NavController} [navCtrl] NavController to use if a link can be opened in the app.
*/ */
redefineWindowOpen(element: any, contentWindow: Window, contentDocument: Document): 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, target: string): Window => {
@ -229,7 +230,10 @@ export class CoreIframeUtilsProvider {
this.domUtils.showErrorModal(error); this.domUtils.showErrorModal(error);
}); });
} else { } else {
// It's an external link, we will open with browser. Check if we need to auto-login. // 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()) { if (!this.sitesProvider.isLoggedIn()) {
// Not logged in, cannot auto-login. // Not logged in, cannot auto-login.
this.utils.openInBrowser(url); this.utils.openInBrowser(url);
@ -237,6 +241,8 @@ export class CoreIframeUtilsProvider {
this.sitesProvider.getCurrentSite().openInBrowserWithAutoLoginIfSameSite(url); this.sitesProvider.getCurrentSite().openInBrowserWithAutoLoginIfSameSite(url);
} }
} }
});
}
// We cannot create new Window objects directly, return null which is a valid return value for Window.open(). // We cannot create new Window objects directly, return null which is a valid return value for Window.open().
return null; return null;
@ -248,7 +254,7 @@ export class CoreIframeUtilsProvider {
CoreIframeUtilsProvider.FRAME_TAGS.forEach((tag) => { CoreIframeUtilsProvider.FRAME_TAGS.forEach((tag) => {
const elements = Array.from(contentDocument.querySelectorAll(tag)); const elements = Array.from(contentDocument.querySelectorAll(tag));
elements.forEach((subElement) => { elements.forEach((subElement) => {
this.treatFrame(subElement, true); this.treatFrame(subElement, true, navCtrl);
}); });
}); });
} }
@ -260,14 +266,15 @@ export class CoreIframeUtilsProvider {
* *
* @param {any} element Element to treat (iframe, embed, ...). * @param {any} element Element to treat (iframe, embed, ...).
* @param {boolean} [isSubframe] Whether it's a frame inside another frame. * @param {boolean} [isSubframe] Whether it's a frame inside another frame.
* @param {NavController} [navCtrl] NavController to use if a link can be opened in the app.
*/ */
treatFrame(element: any, isSubframe?: boolean): void { treatFrame(element: any, isSubframe?: boolean, navCtrl?: NavController): void {
if (element) { if (element) {
this.checkOnlineFrameInOffline(element, isSubframe); this.checkOnlineFrameInOffline(element, isSubframe);
let winAndDoc = this.getContentWindowAndDocument(element); let winAndDoc = this.getContentWindowAndDocument(element);
// Redefine window.open in this element and sub frames, it might have been loaded already. // Redefine window.open in this element and sub frames, it might have been loaded already.
this.redefineWindowOpen(element, winAndDoc.window, winAndDoc.document); this.redefineWindowOpen(element, winAndDoc.window, winAndDoc.document, navCtrl);
// Treat links. // Treat links.
this.treatFrameLinks(element, winAndDoc.document); this.treatFrameLinks(element, winAndDoc.document);
@ -276,7 +283,7 @@ export class CoreIframeUtilsProvider {
// Element loaded, redefine window.open and treat links again. // Element loaded, redefine window.open and treat links again.
winAndDoc = this.getContentWindowAndDocument(element); winAndDoc = this.getContentWindowAndDocument(element);
this.redefineWindowOpen(element, winAndDoc.window, winAndDoc.document); this.redefineWindowOpen(element, winAndDoc.window, winAndDoc.document, navCtrl);
this.treatFrameLinks(element, winAndDoc.document); this.treatFrameLinks(element, winAndDoc.document);
if (winAndDoc.window) { if (winAndDoc.window) {