// (C) Copyright 2015 Martin Dougiamas
//
// 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.
import { Injectable, NgZone } from '@angular/core';
import { Platform } from 'ionic-angular';
import { TranslateService } from '@ngx-translate/core';
import { Network } from '@ionic-native/network';
import { CoreAppProvider } from '../app';
import { CoreFileProvider } from '../file';
import { CoreLoggerProvider } from '../logger';
import { CoreSitesProvider } from '../sites';
import { CoreDomUtilsProvider } from './dom';
import { CoreTextUtilsProvider } from './text';
import { CoreUrlUtilsProvider } from './url';
import { CoreUtilsProvider } from './utils';
/*
* "Utils" service with helper functions for iframes, embed and similar.
*/
@Injectable()
export class CoreIframeUtilsProvider {
static FRAME_TAGS = ['iframe', 'frame', 'object', 'embed'];
protected logger;
constructor(logger: CoreLoggerProvider, private fileProvider: CoreFileProvider, private sitesProvider: CoreSitesProvider,
private urlUtils: CoreUrlUtilsProvider, private textUtils: CoreTextUtilsProvider, private utils: CoreUtilsProvider,
private domUtils: CoreDomUtilsProvider, private platform: Platform, private appProvider: CoreAppProvider,
private translate: TranslateService, private network: Network, private zone: NgZone) {
this.logger = logger.getInstance('CoreUtilsProvider');
}
/**
* Check if a frame uses an online URL but the app is offline. If it does, the iframe is hidden and a warning is shown.
*
* @param {any} element The frame to check (iframe, embed, ...).
* @param {boolean} [isSubframe] Whether it's a frame inside another frame.
* @return {boolean} True if frame is online and the app is offline, false otherwise.
*/
checkOnlineFrameInOffline(element: any, isSubframe?: boolean): boolean {
const src = element.src || element.data;
if (src && src.match(/^https?:\/\//i) && !this.appProvider.isOnline()) {
if (element.classList.contains('core-iframe-offline-disabled')) {
// Iframe already hidden, stop.
return true;
}
// The frame has an online URL but the app is offline. Show a warning.
const div = document.createElement('div');
div.setAttribute('text-center', '');
div.setAttribute('padding', '');
div.classList.add('core-iframe-offline-warning');
div.innerHTML = (isSubframe ? '' : this.domUtils.getConnectionWarningIconHtml()) +
'
';
element.parentElement.insertBefore(div, element);
// Add a class to specify that the iframe is hidden.
element.classList.add('core-iframe-offline-disabled');
if (isSubframe) {
// We cannot apply CSS styles in subframes, just hide the iframe.
element.style.display = 'none';
}
// If the network changes, check it again.
const subscription = this.network.onConnect().subscribe(() => {
// Execute the callback in the Angular zone, so change detection doesn't stop working.
this.zone.run(() => {
if (!this.checkOnlineFrameInOffline(element, isSubframe)) {
// Now the app is online, no need to check connection again.
subscription.unsubscribe();
}
});
});
return true;
} else if (element.classList.contains('core-iframe-offline-disabled')) {
// Reload the frame.
element.src = element.src;
element.data = element.data;
// Remove the warning and show the iframe
this.domUtils.removeElement(element.parentElement, 'div.core-iframe-offline-warning');
element.classList.remove('core-iframe-offline-disabled');
if (isSubframe) {
element.style.display = '';
}
}
return false;
}
/**
* Given an element, return the content window and document.
* Please notice that the element should be an iframe, embed or similar.
*
* @param {any} element Element to treat (iframe, embed, ...).
* @return {{ window: Window, document: Document }} Window and Document.
*/
getContentWindowAndDocument(element: any): { window: Window, document: Document } {
let contentWindow: Window = element.contentWindow,
contentDocument: Document;
try {
contentDocument = element.contentDocument || (contentWindow && contentWindow.document);
} catch (ex) {
// Ignore errors.
}
if (!contentWindow && contentDocument) {
// It's probably an