diff --git a/src/server/response.cpp b/src/server/response.cpp index df0517e92..ddda140ac 100644 --- a/src/server/response.cpp +++ b/src/server/response.cpp @@ -321,17 +321,6 @@ void print_response_info(int retCode, MHD_Response* response) } -void ContentResponse::inject_externallinks_blocker() -{ - kainjow::mustache::data data; - data.set("root", m_root); - auto script_tag = render_template(RESOURCE::templates::external_blocker_part_html, data); - m_content = prependToFirstOccurence( - m_content, - "", - script_tag); -} - void ContentResponse::inject_root_link(){ m_content = prependToFirstOccurence( m_content, @@ -369,10 +358,6 @@ ContentResponse::create_mhd_response(const RequestContext& request) { if (contentDecorationAllowed()) { inject_root_link(); - - if (m_blockExternalLinks) { - inject_externallinks_blocker(); - } } const bool isCompressed = can_compress(request) && compress(m_content); diff --git a/src/server/response.h b/src/server/response.h index ff78ff0a7..a19079b64 100644 --- a/src/server/response.h +++ b/src/server/response.h @@ -103,7 +103,6 @@ class ContentResponse : public Response { private: MHD_Response* create_mhd_response(const RequestContext& request); - void inject_externallinks_blocker(); void inject_root_link(); bool can_compress(const RequestContext& request) const; bool contentDecorationAllowed() const; diff --git a/static/resources_list.txt b/static/resources_list.txt index 6696343f8..5e1b30f73 100644 --- a/static/resources_list.txt +++ b/static/resources_list.txt @@ -12,7 +12,6 @@ skin/taskbar.css skin/index.css skin/fonts/Poppins.ttf skin/fonts/Roboto.ttf -skin/block_external.js skin/search_results.css skin/blank.html skin/viewer.js @@ -23,7 +22,6 @@ templates/error.html templates/error.xml templates/index.html templates/suggestion.json -templates/external_blocker_part.html templates/captured_external.html templates/catalog_entries.xml templates/catalog_v2_root.xml diff --git a/static/skin/block_external.js b/static/skin/block_external.js deleted file mode 100644 index 6bc32baec..000000000 --- a/static/skin/block_external.js +++ /dev/null @@ -1,74 +0,0 @@ -const root = document.querySelector( `link[type='root']` ).getAttribute("href"); -// `block_path` variable used by openzim/warc2zim to detect whether URL blocking is enabled or not -var block_path = `${root}/catch/external`; -// called only on external links -function capture_event(e, target) { target.setAttribute("href", encodeURI(block_path + "?source=" + target.href)); } - -// called on all link clicks. filters external and call capture_event -function on_click_event(e) { - var target = findParent("a", e.target); - if (target !== null && "href" in target) { - var href = target.href; - if (window.location.pathname.indexOf(block_path) == 0) // already in catch page - return; - if (href.indexOf(window.location.origin) == 0) - return; - if (href.substr(0, 2) == "//") - return capture_event(e, target); - if (href.substr(0, 5) == "http:") - return capture_event(e, target); - if (href.substr(0, 6) == "https:") - return capture_event(e, target); - return; - } -} - -// script entrypoint (called on document ready) -function run() { live('a', 'click', on_click_event); } - -// find first parent with tagname -function findParent(tagname, el) { - while (el) { - if ((el.nodeName || el.tagName).toLowerCase() === tagname.toLowerCase()) { - return el; - } - el = el.parentNode; - } - return null; -} - -// matches polyfill -this.Element && function(ElementPrototype) { - ElementPrototype.matches = ElementPrototype.matches || - ElementPrototype.matchesSelector || - ElementPrototype.webkitMatchesSelector || - ElementPrototype.msMatchesSelector || - function(selector) { - var node = this, nodes = (node.parentNode || node.document).querySelectorAll(selector), i = -1; - while (nodes[++i] && nodes[i] != node); - return !!nodes[i]; - } -}(Element.prototype); - -// helper for enabling IE 8 event bindings -function addEvent(el, type, handler) { - if (el.attachEvent) el.attachEvent('on'+type, handler); else el.addEventListener(type, handler); -} - -// live binding helper using matchesSelector -function live(selector, event, callback, context) { - addEvent(context || document, event, function(e) { - var found, el = e.target || e.srcElement; - while (el && el.matches && el !== context && !(found = el.matches(selector))) el = el.parentElement; - if (found) callback.call(el, e); - }); -} - -// in case the document is already rendered -if (document.readyState!='loading') run(); -// modern browsers -else if (document.addEventListener) document.addEventListener('DOMContentLoaded', run); -// IE <= 8 -else document.attachEvent('onreadystatechange', function(){ - if (document.readyState=='complete') run(); -}); diff --git a/static/skin/viewer.js b/static/skin/viewer.js index 971c21220..021c05e0f 100644 --- a/static/skin/viewer.js +++ b/static/skin/viewer.js @@ -179,6 +179,88 @@ function handle_content_url_change() { updateCurrentBookIfNeeded(newHash); }; +//////////////////////////////////////////////////////////////////////////////// +// External link blocking +//////////////////////////////////////////////////////////////////////////////// + +function matchingAncestorElement(el, context, selector) { + while (el && el.matches && el !== context) { + if ( el.matches(selector) ) + return el; + el = el.parentElement; + } + return null; +} + +const block_path = `${root}/catch/external`; + +function blockLink(target) { + const encodedHref = encodeURIComponent(target.href); + target.setAttribute("href", block_path + "?source=" + encodedHref); + target.setAttribute("target", "_top"); +} + +function isExternalUrl(url) { + if ( url.startsWith(window.location.origin) ) + return false; + + return url.startsWith("//") + || url.startsWith("http:") + || url.startsWith("https:"); +} + +function onClickEvent(e) { + const iframeDocument = contentIframe.contentDocument; + const target = matchingAncestorElement(e.target, iframeDocument, "a"); + if (target !== null && "href" in target) { + if ( isExternalUrl(target.href) ) + return blockLink(target); + } +} + +// helper for enabling IE 8 event bindings +function addEventHandler(el, eventType, handler) { + if (el.attachEvent) + el.attachEvent('on'+eventType, handler); + else + el.addEventListener(eventType, handler); +} + +function setupEventHandler(context, selector, eventType, callback) { + addEventHandler(context, eventType, function(e) { + const eventElement = e.target || e.srcElement; + const el = matchingAncestorElement(eventElement, context, selector); + if (el) + callback.call(el, e); + }); +} + +// matches polyfill +this.Element && function(ElementPrototype) { + ElementPrototype.matches = ElementPrototype.matches || + ElementPrototype.matchesSelector || + ElementPrototype.webkitMatchesSelector || + ElementPrototype.msMatchesSelector || + function(selector) { + var node = this, nodes = (node.parentNode || node.document).querySelectorAll(selector), i = -1; + while (nodes[++i] && nodes[i] != node); + return !!nodes[i]; + } +}(Element.prototype); + +function setup_external_link_blocker() { + setupEventHandler(contentIframe.contentDocument, 'a', 'click', onClickEvent); +} + +//////////////////////////////////////////////////////////////////////////////// +// End of external link blocking +//////////////////////////////////////////////////////////////////////////////// + +function on_content_load() { + handle_content_url_change(); + setup_external_link_blocker(); +} + window.onresize = handle_visual_viewport_change; window.onhashchange = handle_location_hash_change; diff --git a/static/templates/external_blocker_part.html b/static/templates/external_blocker_part.html deleted file mode 100644 index 6bb0a0c21..000000000 --- a/static/templates/external_blocker_part.html +++ /dev/null @@ -1 +0,0 @@ - diff --git a/static/viewer.html b/static/viewer.html index 7948cd17c..51667ffc2 100644 --- a/static/viewer.html +++ b/static/viewer.html @@ -56,7 +56,7 @@ diff --git a/test/server.cpp b/test/server.cpp index 234890d2a..874b6cf36 100644 --- a/test/server.cpp +++ b/test/server.cpp @@ -46,7 +46,6 @@ const ResourceCollection resources200Compressible{ { WITH_ETAG, "/ROOT/skin/autoComplete.min.js" }, { WITH_ETAG, "/ROOT/skin/css/autoComplete.css" }, { WITH_ETAG, "/ROOT/skin/taskbar.css" }, - { WITH_ETAG, "/ROOT/skin/block_external.js" }, { NO_ETAG, "/ROOT/catalog/search" },