MOBILE-4018 iframe: Make open iframe links more consistent

main
Dani Palou 2025-01-08 13:02:35 +01:00
parent 0af6865703
commit 02df883a03
3 changed files with 35 additions and 98 deletions

View File

@ -21,12 +21,11 @@
} }
// Redefine window.open. // Redefine window.open.
const originalWindowOpen = window.open;
window.open = function(url, name, specs) { window.open = function(url, name, specs) {
if (name == '_self') { if (name == '_self') {
// Link should be loaded in the same frame. // Link will be opened in the same frame, no need to treat it.
location.href = toAbsolute(url); return originalWindowOpen(url, name, specs);
return;
} }
getRootWindow(window).postMessage({ getRootWindow(window).postMessage({
@ -34,7 +33,7 @@
context: 'iframe', context: 'iframe',
action: 'window_open', action: 'window_open',
frameUrl: location.href, frameUrl: location.href,
url: url, url: toAbsolute(url),
name: name, name: name,
specs: specs, specs: specs,
}, '*'); }, '*');
@ -164,24 +163,19 @@
return; return;
} }
const linkScheme = getUrlScheme(link.href); if (!link.target || link.target == '_self') {
const pageScheme = getUrlScheme(location.href); // Link needs to be opened in the same iframe. This is already handled properly, we don't need to do anything else.
const isTargetSelf = !link.target || link.target == '_self'; // Links opened in the same iframe won't be captured by the app.
return;
}
if (!link.href || linkScheme == 'javascript') { if (!link.href || getUrlScheme(link.href) == 'javascript') {
// Links with no URL and Javascript links are ignored. // Links with no URL and Javascript links are ignored.
return; return;
} }
event.preventDefault(); 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({ getRootWindow(window).postMessage({
environment: 'moodleapp', environment: 'moodleapp',
context: 'iframe', context: 'iframe',

View File

@ -18,7 +18,6 @@ import { WKWebViewCookiesWindow } from 'cordova-plugin-wkwebview-cookies';
import { CoreNetwork } from '@services/network'; import { CoreNetwork } from '@services/network';
import { CoreFile } from '@services/file'; import { CoreFile } from '@services/file';
import { CoreFileHelper } from '@services/file-helper';
import { CoreSites } from '@services/sites'; import { CoreSites } from '@services/sites';
import { CoreDomUtils } from '@services/utils/dom'; import { CoreDomUtils } from '@services/utils/dom';
import { CoreUrl } from '@singletons/url'; import { CoreUrl } from '@singletons/url';
@ -313,7 +312,13 @@ export class CoreIframeUtilsProvider {
): void { ): void {
if (contentWindow) { if (contentWindow) {
// Intercept window.open. // Intercept window.open.
const originalWindowOpen = contentWindow.open;
contentWindow.open = (url: string, name: string) => { contentWindow.open = (url: string, name: string) => {
if (name === '_self') {
// Link will be opened in the same frame, no need to treat it.
return originalWindowOpen(url, name);
}
this.windowOpen(url, name, element); this.windowOpen(url, name, element);
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
@ -410,7 +415,7 @@ 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', event => this.linkClicked(link, element, event)); link.addEventListener('click', event => this.linkClicked(link, event));
}, { }, {
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.
}); });
@ -447,26 +452,11 @@ export class CoreIframeUtilsProvider {
} }
} }
if (name == '_self') { try {
// Link should be loaded in the same frame. // It's an external link or a local file, check if it can be opened in the app.
if (!element) { await CoreWindow.open(url, name);
this.logger.warn('Cannot load URL in iframe because the element was not supplied', url); } catch (error) {
CoreDomUtils.showErrorModal(error);
return;
}
if (element.tagName.toLowerCase() === 'object') {
element.setAttribute('data', url);
} else {
element.setAttribute('src', url);
}
} else {
try {
// It's an external link or a local file, check if it can be opened in the app.
await CoreWindow.open(url, name);
} catch (error) {
CoreDomUtils.showErrorModal(error);
}
} }
} }
@ -474,13 +464,11 @@ export class CoreIframeUtilsProvider {
* A link inside a frame was clicked. * A link inside a frame was clicked.
* *
* @param link Link clicked, or data of the link clicked. * @param link Link clicked, or data of the link clicked.
* @param element Frame element.
* @param event Click event. * @param event Click event.
* @returns Promise resolved when done. * @returns Promise resolved when done.
*/ */
protected async linkClicked( protected async linkClicked(
link: CoreIframeHTMLAnchorElement | {href: string; target?: string; originalHref?: string}, link: CoreIframeHTMLAnchorElement | {href: string; target?: string; originalHref?: string},
element?: CoreFrameElement,
event?: Event, event?: Event,
): Promise<void> { ): Promise<void> {
if (event && event.defaultPrevented) { if (event && event.defaultPrevented) {
@ -488,69 +476,24 @@ export class CoreIframeUtilsProvider {
return; return;
} }
if (!link.target || link.target === '_self') {
// Link needs to be opened in the same iframe. This is already handled properly, we don't need to do anything else.
// Links opened in the same iframe won't be captured by the app.
return;
}
const urlParts = CoreUrl.parse(link.href); const urlParts = CoreUrl.parse(link.href);
const originalHref = 'getAttribute' in link ? link.getAttribute('href') : link.originalHref; const originalHref = 'getAttribute' in link ? link.getAttribute('href') : link.originalHref;
if (!link.href || !originalHref || originalHref == '#' || !urlParts || urlParts.protocol === 'javascript') { if (!link.href || !originalHref || originalHref === '#' || !urlParts || urlParts.protocol === 'javascript') {
// Links with no URL and Javascript links are ignored. // Links with no URL and Javascript links are ignored.
return; return;
} }
if (urlParts.protocol && !CoreUrl.isLocalFileUrlScheme(urlParts.protocol, urlParts.domain || '')) { try {
// Scheme suggests it's an external resource. event?.preventDefault();
event && event.preventDefault(); await CoreWindow.open(link.href, link.target);
} catch (error) {
const frameSrc = element && ((<HTMLIFrameElement> element).src || (<HTMLObjectElement> element).data); CoreDomUtils.showErrorModal(error);
// If the frame is not local, check the target to identify how to treat the link.
if (
element &&
frameSrc &&
!CoreUrl.isLocalFileUrl(frameSrc) &&
(!link.target || link.target == '_self')
) {
// Load the link inside the frame itself.
if (element.tagName.toLowerCase() === 'object') {
element.setAttribute('data', link.href);
} else {
element.setAttribute('src', link.href);
}
return;
}
// The frame is local or the link needs to be opened in a new window. Open in browser.
if (!CoreSites.isLoggedIn()) {
CoreOpener.openInBrowser(link.href);
} else {
await CoreSites.getCurrentSite()?.openInBrowserWithAutoLogin(link.href);
}
} 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.
event && event.preventDefault();
const filename = link.href.substring(link.href.lastIndexOf('/') + 1);
if (!CoreFileHelper.isOpenableInApp({ filename })) {
try {
await CoreFileHelper.showConfirmOpenUnsupportedFile(false, { filename });
} catch (error) {
return; // Cancelled, stop.
}
}
try {
await CoreOpener.openFile(link.href);
} catch (error) {
CoreDomUtils.showErrorModal(error);
}
} else if (CorePlatform.isIOS() && (!link.target || link.target == '_self') && element) {
// In cordova ios 4.1.0 links inside iframes stopped working. We'll manually treat them.
event && event.preventDefault();
if (element.tagName.toLowerCase() === 'object') {
element.setAttribute('data', link.href);
} else {
element.setAttribute('src', link.href);
}
} }
} }

View File

@ -68,7 +68,7 @@ export class CoreWindow {
} else { } else {
let treated = false; let treated = false;
if (name != '_system') { if (name !== '_system') {
// Check if it can be opened in the app. // Check if it can be opened in the app.
treated = await CoreContentLinksHelper.handleLink(url, undefined, true, true); treated = await CoreContentLinksHelper.handleLink(url, undefined, true, true);
} }