├── .gitignore ├── .github └── FUNDING.yml ├── static ├── untitled.mp4 ├── style.css ├── setup.html └── rocket.svg ├── Makefile ├── options.html ├── README.md ├── LICENSE ├── manifest.json ├── js ├── options.js ├── instant_page.min.js ├── background.js └── lazy_images.js └── icons ├── rocket_da.svg └── rocket.svg /.gitignore: -------------------------------------------------------------------------------- 1 | faster_pageload.zip 2 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: [simonfrey] 2 | custom: ["https://paypal.me/51edpo"] 3 | -------------------------------------------------------------------------------- /static/untitled.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonfrey/faster-pageload-web-extensions/HEAD/static/untitled.mp4 -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: store 2 | 3 | store: 4 | @echo "Building..." 5 | @zip faster_pageload.zip * -r 6 | @echo "Build completed." 7 | 8 | clean: 9 | @echo "Started cleaning...." 10 | @rm faster_pageload.zip 11 | @echo "Finished cleaning" 12 | -------------------------------------------------------------------------------- /static/style.css: -------------------------------------------------------------------------------- 1 | body{ 2 | max-width: 1200px; 3 | margin:auto; 4 | padding:1em; 5 | font-size: 1.2em; 6 | } 7 | 8 | h1,h2{ 9 | text-align: center; 10 | } 11 | h1 img{ 12 | height:1.5em; 13 | } 14 | 15 | ol{ 16 | font-size: 1.5em; 17 | } 18 | 19 | div{ 20 | border:solid 5px rebeccapurple; 21 | padding:1em; 22 | } -------------------------------------------------------------------------------- /options.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 |
18 |
19 |
20 |
Note, has to be in the format: www.example.com, example2.com
21 |
22 |
23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /static/setup.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Faster Pageload post install instructions 4 | 5 | 6 | 7 | 8 |

Please follow this instructions

9 |

As in Firefox preloading is deactivated by default, you have to activate it.

10 |

Three easy steps

11 |
    15 |
  1. Open about:config 16 |
  2. Search for network.dns.disablePrefetchFromHTTPS and set it to false 17 |
  3. Search for network.predictor.enable-prefetch and set it to true 18 |
19 |
20 |

If there are problems coming up, do not hesitate to ask: fasterpageload@simon-frey.eu

21 | 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Fast Pageload uses preloading to make your browsing experience feel more fluent and the web overall faster. 2 | This is archived by just in time preloading pages your are quite likely to visit. Studies show, that when you hover over a link there is a 50% chance that you will click it. Faster Pageload starts loading the page when you hover and thereby when you actually click (on average 400ms later) the page is already loaded to a big portion. 3 | There is no technical effect, as the pages are still loaded normally but for your as human it will feel faster, as the pages builds up instantly after you clicked. 4 | 5 | For further speed improvements we lazy load images on the web pages. So only the images you really see are loaded. 6 | 7 | Give it a go and feel the magic. 8 | 9 | Credits: 10 | Addon Icon by Freepik (https://www.flaticon.com/authors/freepik) 11 | Instant.page script that does the actual preloading is by Alexandre Dieulot (https://dieulot.fr/) and licensed under the MIT license (https://opensource.org/licenses/MIT) 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Simon Frey 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "manifest_version": 2, 3 | "name": "Faster Pageload", 4 | "version": "1.8.5", 5 | "author": "Simon Frey", 6 | "developer": { 7 | "name": "Simon Frey", 8 | "url": "https://simon-frey.eu/" 9 | }, 10 | "description": "Load webpages faster by preloading subsequent pages via the instant.page script. Install the plugin and you will feel how your browser becomes faster.", 11 | "homepage_url": "https://github.com/simonfrey/faster-pageload-web-extensions", 12 | 13 | "icons": { 14 | "48": "icons/rocket.svg", 15 | "96": "icons/rocket.svg" 16 | }, 17 | "options_ui": { 18 | "page": "options.html" 19 | }, 20 | 21 | "background": { 22 | "scripts": ["js/background.js"] 23 | }, 24 | "browser_specific_settings": { 25 | "gecko": { 26 | "id": "{73c0257e-a620-4c48-a2a8-2a9e8481a0d4}" 27 | } 28 | }, 29 | 30 | "web_accessible_resources": ["static/*"], 31 | "browser_action": { 32 | "default_icon": "icons/rocket.svg", 33 | "default_title": "Faster Pageload" 34 | }, 35 | 36 | "permissions": ["storage", 37 | "webRequest", 38 | "", 39 | "webRequestBlocking"], 40 | "content_scripts": [ 41 | { 42 | "matches": [""], 43 | "js": ["js/instant_page.min.js", "js/lazy_images.js"] 44 | } 45 | ] 46 | } 47 | -------------------------------------------------------------------------------- /js/options.js: -------------------------------------------------------------------------------- 1 | var preloadViewportElement = document.getElementById("preloadViewport"); 2 | var imgLazyLoadElement = document.getElementById("imgLazyLoad"); 3 | var blacklistSitesElement = document.getElementById("blacklistSites"); 4 | 5 | function saveOptions(e) { 6 | e.preventDefault(); 7 | browser.storage.sync.set({ 8 | preloadViewport: preloadViewportElement.checked, 9 | imgLazyLoad: imgLazyLoadElement.checked, 10 | blacklistSites: blacklistSitesElement.value.replace(/\s+/g, '').toLowerCase(), 11 | }); 12 | 13 | browser.runtime.reload(); 14 | } 15 | 16 | function restoreOptions() { 17 | function setPreloadViewport(result) { 18 | preloadViewportElement.checked = result.preloadViewport || false; 19 | } 20 | function setImgLazyLoad(result) { 21 | imgLazyLoadElement.checked = result.imgLazyLoad || false; 22 | } 23 | function setBlacklistSites(result) { 24 | blacklistSitesElement.value = result.blacklistSites || ""; 25 | } 26 | 27 | function onError(error) { 28 | alert(`[faster pageload plugin] local storage error: ${error}`); 29 | } 30 | 31 | 32 | browser.storage.sync.get("preloadViewport").then(setPreloadViewport, onError); 33 | browser.storage.sync.get("imgLazyLoad").then(setImgLazyLoad, onError); 34 | browser.storage.sync.get("blacklistSites").then(setBlacklistSites, onError); 35 | 36 | } 37 | 38 | document.addEventListener("DOMContentLoaded", restoreOptions); 39 | document.querySelector("form").addEventListener("submit", saveOptions); 40 | -------------------------------------------------------------------------------- /js/instant_page.min.js: -------------------------------------------------------------------------------- 1 | /*! instant.page v5.1.0 - (C) 2019-2020 Alexandre Dieulot - https://instant.page/license */ 2 | let t,e;const n=new Set,o=document.createElement("link"),i=o.relList&&o.relList.supports&&o.relList.supports("prefetch")&&window.IntersectionObserver&&"isIntersecting"in IntersectionObserverEntry.prototype,s="instantAllowQueryString"in document.body.dataset,a="instantAllowExternalLinks"in document.body.dataset,r="instantWhitelist"in document.body.dataset,c="instantMousedownShortcut"in document.body.dataset,d=1111;let l=65,u=!1,f=!1,m=!1;if("instantIntensity"in document.body.dataset){const t=document.body.dataset.instantIntensity;if("mousedown"==t.substr(0,"mousedown".length))u=!0,"mousedown-only"==t&&(f=!0);else if("viewport"==t.substr(0,"viewport".length))navigator.connection&&(navigator.connection.saveData||navigator.connection.effectiveType&&navigator.connection.effectiveType.includes("2g"))||("viewport"==t?document.documentElement.clientWidth*document.documentElement.clientHeight<45e4&&(m=!0):"viewport-all"==t&&(m=!0));else{const e=parseInt(t);isNaN(e)||(l=e)}}if(i){const n={capture:!0,passive:!0};if(f||document.addEventListener("touchstart",function(t){e=performance.now();const n=t.target.closest("a");if(!h(n))return;v(n.href)},n),u?c||document.addEventListener("mousedown",function(t){const e=t.target.closest("a");if(!h(e))return;v(e.href)},n):document.addEventListener("mouseover",function(n){if(performance.now()-e{v(o.href),t=void 0},l)},n),c&&document.addEventListener("mousedown",function(t){if(performance.now()-e1||t.metaKey||t.ctrlKey)return;if(!n)return;n.addEventListener("click",function(t){1337!=t.detail&&t.preventDefault()},{capture:!0,passive:!1,once:!0});const o=new MouseEvent("click",{view:window,bubbles:!0,cancelable:!1,detail:1337});n.dispatchEvent(o)},n),m){let t;(t=window.requestIdleCallback?t=>{requestIdleCallback(t,{timeout:1500})}:t=>{t()})(()=>{const t=new IntersectionObserver(e=>{e.forEach(e=>{if(e.isIntersecting){const n=e.target;t.unobserve(n),v(n.href)}})});document.querySelectorAll("a").forEach(e=>{h(e)&&t.observe(e)})})}}function p(e){e.relatedTarget&&e.target.closest("a")==e.relatedTarget.closest("a")||t&&(clearTimeout(t),t=void 0)}function h(t){if(t&&t.href&&(!r||"instant"in t.dataset)&&(a||t.origin==location.origin||"instant"in t.dataset)&&["http:","https:"].includes(t.protocol)&&("http:"!=t.protocol||"https:"!=location.protocol)&&(s||!t.search||"instant"in t.dataset)&&!(t.hash&&t.pathname+t.search==location.pathname+location.search||"noInstant"in t.dataset))return!0}function v(t){if(n.has(t))return;const e=document.createElement("link");e.rel="prefetch",e.href=t,document.head.appendChild(e),n.add(t)} -------------------------------------------------------------------------------- /js/background.js: -------------------------------------------------------------------------------- 1 | function handleInstalled(details) { 2 | if (details.reason == "install") { 3 | browser.tabs.create({ 4 | url: chrome.runtime.getURL("static/setup.html") 5 | }); 6 | } 7 | } 8 | 9 | browser.runtime.onInstalled.addListener(handleInstalled); 10 | 11 | 12 | // Set active icon 13 | function SetIcon(active) { 14 | if (active) { 15 | chrome.browserAction.setIcon({ path: "icons/rocket.svg" }); 16 | } else { 17 | chrome.browserAction.setIcon({ path: "icons/rocket_da.svg" }); 18 | } 19 | } 20 | 21 | chrome.browserAction.onClicked.addListener(async function () { 22 | var storeData = await browser.storage.sync.get([ 23 | "active", 24 | ]); 25 | if (storeData.active == undefined) { 26 | storeData.active = true; 27 | } 28 | await browser.storage.sync.set({ 29 | active: !storeData.active, 30 | }); 31 | SetIcon(!storeData.active); 32 | }); 33 | 34 | function onError(error) { 35 | console.log(`faster pageload plugin: local storage error: ${error}`); 36 | } 37 | function setInitIcon(storeData) { 38 | SetIcon(storeData.active); 39 | } 40 | browser.storage.sync.get("active").then(setInitIcon, onError); 41 | 42 | 43 | 44 | 45 | 46 | // 47 | // Real lazy loading 48 | 49 | function timeout(ms) { 50 | return new Promise(resolve => setTimeout(resolve, ms)); 51 | } 52 | 53 | 54 | function onError(error) { 55 | alert(`faster pageload plugin: local storage error: ${error}`); 56 | } 57 | 58 | function setImgLazyLoad(result) { 59 | if (result.active == false) { 60 | console.log("[faster pageload plugin] Inactive: Do not lazy load images") 61 | return 62 | } 63 | 64 | if (/*result.imgLazyLoad == undefined ||*/ result.imgLazyLoad == true) { 65 | var lazyDone = false; 66 | var ourLazyLoad = false; 67 | 68 | 69 | function cancel(requestDetails) { 70 | if (!lazyDone && requestDetails.documentUrl == requestDetails.originUrl) { 71 | return new Promise(async (resolve, reject) => { 72 | for (k = 0; k < 30; k++) { 73 | await timeout(500); 74 | if (ourLazyLoad) { 75 | resolve({ cancel: true }); 76 | console.log("CANCEL: ", requestDetails) 77 | 78 | // console.log("CANCEL: " + requestDetails.url) 79 | return 80 | }; 81 | if (!ourLazyLoad && lazyDone) { 82 | resolve({}); 83 | } 84 | } 85 | resolve({}); 86 | 87 | }) 88 | 89 | } 90 | } 91 | browser.webRequest.onBeforeRequest.addListener( 92 | cancel, 93 | { urls: [""], types: ["image"] }, 94 | ["blocking"] 95 | ); 96 | browser.runtime.onMessage.addListener((request, sender, sendResponse) => { 97 | if (request.lazyDone != undefined) lazyDone = true; 98 | if (request.ourLazyLoad != undefined) ourLazyLoad = request.ourLazyLoad; 99 | console.log("LAZY DONE:", lazyDone) 100 | console.log("OUR LAZY:", ourLazyLoad) 101 | }); 102 | function handleUpdated(tabId, changeInfo, tabInfo) { 103 | console.log("Changed status: ", changeInfo.status); 104 | if (changeInfo.status == "loading") { 105 | lazyDone = false; 106 | ourLazyLoad = false; 107 | } 108 | } 109 | browser.tabs.onUpdated.addListener(handleUpdated); 110 | } 111 | } 112 | browser.storage.sync.get(["imgLazyLoad", "active"]).then(setImgLazyLoad, onError); 113 | -------------------------------------------------------------------------------- /icons/rocket_da.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 7 | 9 | 12 | 14 | 18 | 21 | 23 | 26 | 28 | 30 | 32 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /js/lazy_images.js: -------------------------------------------------------------------------------- 1 | 2 | function IsImageOk(img) { 3 | // During the onload event, IE correctly identifies any images that 4 | // weren't downloaded as not complete. Others should too. Gecko-based 5 | // browsers act like NS4 in that they report this incorrectly. 6 | if (!img.complete) { 7 | return false; 8 | } 9 | 10 | // However, they do have two very useful properties: naturalWidth and 11 | // naturalHeight. These give the true size of the image. If it failed 12 | // to load, either of these should be zero. 13 | if (typeof img.naturalWidth != "undefined" && img.naturalWidth == 0) { 14 | return false; 15 | } 16 | 17 | // No other way of checking: assume it's ok. 18 | return true; 19 | } 20 | 21 | function setImgLazyLoad(result) { 22 | if (result.active == false) { 23 | console.log("[faster pageload plugin] Inactive: Do not lazy load images") 24 | return 25 | } 26 | 27 | /*This first check if blacklistSites is empty, if not then checks if the current web domain is in the blacklist which has been split into an array*/ 28 | if (result.blacklistSites !== "" && result.blacklistSites.split(",").includes(window.location.host)) { 29 | console.log("[faster pageload plugin] Site Blacklisted: Do not load instant.page script") 30 | return 31 | } 32 | 33 | if (/*result.imgLazyLoad == undefined ||*/ result.imgLazyLoad == true) { 34 | const config = { 35 | root: null, // avoiding 'root' or setting it to 'null' sets it to default value: viewport 36 | rootMargin: "0px", 37 | threshold: 0.5 38 | }; 39 | 40 | // register the config object with an instance 41 | // of intersectionObserver 42 | var obst = new IntersectionObserver(function (entries, self) { 43 | // iterate over each entry 44 | entries.forEach(entry => { 45 | // process just the images that are intersecting. 46 | // isIntersecting is a property exposed by the interface 47 | if (entry.isIntersecting) { 48 | // custom function that copies the path to the img 49 | // from data-src to src 50 | entry.target.src = entry.target.getAttribute( 51 | "data-src-fasterpageload" 52 | ); 53 | entry.target.removeAttribute("data-src-fasterpageload"); 54 | console.log( 55 | "[faster pageload plugin] Lazy loaded image: ", 56 | entry.target.src 57 | ); 58 | // the image is now in place, stop watching 59 | self.unobserve(entry.target); 60 | } 61 | }); 62 | }, config); 63 | 64 | var imgElements = document.querySelectorAll("img"); 65 | console.log("[faster pageload plugin] Lazyload " + imgElements.length + " images"); 66 | 67 | var foundSrcs = {} 68 | var foundTimes = 0 69 | var ourLazy = true; 70 | 71 | for (var i = imgElements.length - 1; i >= 0; i--) { 72 | var imgElem = imgElements[i]; 73 | 74 | 75 | // if ( 76 | // imgElem.src != undefined && 77 | // imgElem.src != "" && 78 | // Object.keys(imgElem.dataset).length == 0 && 79 | // imgElem.src.includes("data:") == false 80 | // ) { 81 | 82 | if(foundSrcs[imgElem.src]== undefined){ 83 | foundSrcs[imgElem.src] = 1; 84 | } 85 | foundSrcs[imgElem.src]++; 86 | 87 | console.log("FOUND: ",foundSrcs[imgElem.src], imgElem.src,imgElem.loading) 88 | 89 | if (imgElem.loading == "lazy"){ 90 | console.log("Found already lazy") 91 | ourLazy = false; 92 | break 93 | } 94 | 95 | if (foundSrcs[imgElem.src] >= 5) { 96 | console.log("[faster pageload plugin] Unknown lazy load plugin found. Remove lazy load elements: ", imgElements.length - 1 - i) 97 | // we have apparently an unknown lazy load plugin on the site. Reset all images 98 | for (var j = imgElements.length - 1; j >= i; j--) { 99 | var remElem = imgElements[j]; 100 | remElem.src = remElem.getAttribute( 101 | "data-src-fasterpageload" 102 | ); 103 | remElem.removeAttribute("data-src-fasterpageload"); 104 | obst.unobserve(remElem); 105 | } 106 | ourLazy = false; 107 | break 108 | 109 | } else { 110 | 111 | 112 | 113 | 114 | imgElem.setAttribute("data-src-fasterpageload", imgElem.src); 115 | imgElem.src = chrome.runtime.getURL("static/rocket.svg"); 116 | obst.observe(imgElem); 117 | } 118 | // } 119 | 120 | } 121 | 122 | 123 | 124 | browser.runtime.sendMessage({ 125 | lazyDone: true, 126 | ourLazyLoad: ourLazy 127 | }); 128 | } 129 | 130 | } 131 | 132 | function onError(error) { 133 | alert(`faster pageload plugin: local storage error: ${error}`); 134 | } 135 | 136 | browser.storage.sync.get(["blacklistSites", "imgLazyLoad", "active"]).then(setImgLazyLoad, onError); 137 | -------------------------------------------------------------------------------- /icons/rocket.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 8 | 9 | 11 | 14 | 15 | 17 | 19 | 21 | 24 | 26 | 30 | 33 | 35 | 38 | 40 | 42 | 44 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /static/rocket.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 8 | 9 | 11 | 14 | 15 | 17 | 19 | 21 | 24 | 26 | 30 | 33 | 35 | 38 | 40 | 42 | 44 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | --------------------------------------------------------------------------------