diff --git a/src/assets/js/iframe-treat-links.js b/src/assets/js/iframe-treat-links.js index 6582397ec..09210cd0a 100644 --- a/src/assets/js/iframe-treat-links.js +++ b/src/assets/js/iframe-treat-links.js @@ -21,12 +21,11 @@ } // Redefine window.open. + const originalWindowOpen = window.open; window.open = function(url, name, specs) { if (name == '_self') { - // Link should be loaded in the same frame. - location.href = toAbsolute(url); - - return; + // Link will be opened in the same frame, no need to treat it. + return originalWindowOpen(url, name, specs); } getRootWindow(window).postMessage({ @@ -34,7 +33,7 @@ context: 'iframe', action: 'window_open', frameUrl: location.href, - url: url, + url: toAbsolute(url), name: name, specs: specs, }, '*'); @@ -164,24 +163,19 @@ return; } - const linkScheme = getUrlScheme(link.href); - const pageScheme = getUrlScheme(location.href); - const isTargetSelf = !link.target || link.target == '_self'; + 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; + } - if (!link.href || linkScheme == 'javascript') { + if (!link.href || getUrlScheme(link.href) == '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', diff --git a/src/core/services/utils/iframe.ts b/src/core/services/utils/iframe.ts index 710568119..327fa6a62 100644 --- a/src/core/services/utils/iframe.ts +++ b/src/core/services/utils/iframe.ts @@ -18,7 +18,6 @@ import { WKWebViewCookiesWindow } from 'cordova-plugin-wkwebview-cookies'; import { CoreNetwork } from '@services/network'; import { CoreFile } from '@services/file'; -import { CoreFileHelper } from '@services/file-helper'; import { CoreSites } from '@services/sites'; import { CoreDomUtils } from '@services/utils/dom'; import { CoreUrl } from '@singletons/url'; @@ -313,7 +312,13 @@ export class CoreIframeUtilsProvider { ): void { if (contentWindow) { // Intercept window.open. + const originalWindowOpen = contentWindow.open; 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); // 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. 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. }); @@ -447,26 +452,11 @@ export class CoreIframeUtilsProvider { } } - 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 { - 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); - } + 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. * * @param link Link clicked, or data of the link clicked. - * @param element Frame element. * @param event Click event. * @returns Promise resolved when done. */ protected async linkClicked( link: CoreIframeHTMLAnchorElement | {href: string; target?: string; originalHref?: string}, - element?: CoreFrameElement, event?: Event, ): Promise { if (event && event.defaultPrevented) { @@ -488,69 +476,24 @@ export class CoreIframeUtilsProvider { 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 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. return; } - if (urlParts.protocol && !CoreUrl.isLocalFileUrlScheme(urlParts.protocol, urlParts.domain || '')) { - // Scheme suggests it's an external resource. - event && event.preventDefault(); - - const frameSrc = element && (( element).src || ( element).data); - - // 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); - } + try { + event?.preventDefault(); + await CoreWindow.open(link.href, link.target); + } catch (error) { + CoreDomUtils.showErrorModal(error); } } diff --git a/src/core/singletons/window.ts b/src/core/singletons/window.ts index d3d08ac29..c8d0668aa 100644 --- a/src/core/singletons/window.ts +++ b/src/core/singletons/window.ts @@ -67,7 +67,7 @@ export class CoreWindow { } else { let treated = false; - if (name != '_system') { + if (name !== '_system') { // Check if it can be opened in the app. treated = await CoreContentLinksHelper.handleLink(url, undefined, true, true); }