Kaynağa Gözat

added hook on Image#src

Gildas 7 yıl önce
ebeveyn
işleme
f4e4d38aff
2 değiştirilmiş dosya ile 66 ekleme ve 11 silme
  1. 49 7
      lib/hooks/hooks-frame.js
  2. 17 4
      lib/lazy/content/content-lazy-loader.js

+ 49 - 7
lib/hooks/hooks-frame.js

@@ -24,12 +24,14 @@ this.hooksFrame = this.hooksFrame || (() => {
 
 	const LOAD_DEFERRED_IMAGES_START_EVENT = "single-file-load-deferred-images-start";
 	const LOAD_DEFERRED_IMAGES_END_EVENT = "single-file-load-deferred-images-end";
+	const LOAD_IMAGE_EVENT = "single-file-load-image";
+	const IMAGE_LOADED_EVENT = "single-file-image-loaded";
 	const NEW_FONT_FACE_EVENT = "single-file-new-font-face";
 	const fontFaces = [];
 
 	if (document instanceof HTMLDocument) {
 		const scriptElement = document.createElement("script");
-		scriptElement.textContent = `(${hook.toString()})()`;
+		scriptElement.textContent = `(${hook.toString()})(${JSON.stringify({ LOAD_DEFERRED_IMAGES_START_EVENT, LOAD_DEFERRED_IMAGES_END_EVENT, LOAD_IMAGE_EVENT, IMAGE_LOADED_EVENT, NEW_FONT_FACE_EVENT })})`;
 		(document.documentElement || document).appendChild(scriptElement);
 		scriptElement.remove();
 		addEventListener(NEW_FONT_FACE_EVENT, event => fontFaces.push(event.detail));
@@ -38,13 +40,19 @@ this.hooksFrame = this.hooksFrame || (() => {
 	return {
 		getFontsData: () => fontFaces,
 		loadDeferredImagesStart: () => dispatchEvent(new CustomEvent(LOAD_DEFERRED_IMAGES_START_EVENT)),
-		loadDeferredImagesEnd: () => dispatchEvent(new CustomEvent(LOAD_DEFERRED_IMAGES_END_EVENT))
+		loadDeferredImagesEnd: () => dispatchEvent(new CustomEvent(LOAD_DEFERRED_IMAGES_END_EVENT)),
+		LOAD_IMAGE_EVENT,
+		IMAGE_LOADED_EVENT
 	};
 
-	function hook() {
-		const LOAD_DEFERRED_IMAGES_START_EVENT = "single-file-load-deferred-images-start";
-		const LOAD_DEFERRED_IMAGES_END_EVENT = "single-file-load-deferred-images-end";
-		const NEW_FONT_FACE_EVENT = "single-file-new-font-face";
+	function hook(constants) {
+		const {
+			LOAD_DEFERRED_IMAGES_START_EVENT,
+			LOAD_DEFERRED_IMAGES_END_EVENT,
+			LOAD_IMAGE_EVENT,
+			IMAGE_LOADED_EVENT,
+			NEW_FONT_FACE_EVENT
+		} = constants;
 		const FONT_STYLE_PROPERTIES = {
 			family: "font-family",
 			style: "font-style",
@@ -89,6 +97,33 @@ this.hooksFrame = this.hooksFrame || (() => {
 			window._singleFile_localStorage = window.localStorage;
 			window.__defineGetter__("localStorage", () => { throw new Error("localStorage temporary blocked by SingleFile"); });
 			document.__defineGetter__("cookie", () => { throw new Error("document.cookie temporary blocked by SingleFile"); });
+			const Image = window.Image;
+			window._singleFileImage = window.Image;
+			window.__defineGetter__("Image", function () {
+				return function () {
+					const image = new Image(...arguments);
+					const result = new Image(...arguments);
+					result.__defineSetter__("src", function (value) {
+						dispatchEvent(new CustomEvent(LOAD_IMAGE_EVENT));
+						image.src = value;
+					});
+					result.__defineGetter__("src", function () {
+						return image.src;
+					});
+					result.__defineSetter__("srcset", function (value) {
+						dispatchEvent(new CustomEvent(LOAD_IMAGE_EVENT));
+						image.srcset = value;
+					});
+					result.__defineGetter__("srcset", function () {
+						return image.srcset;
+					});
+					image.onload = image.onprogress = image.onloadend = image.onerror = event => {
+						dispatchEvent(new CustomEvent(IMAGE_LOADED_EVENT));
+						result.dispatchEvent(new UIEvent(event.type, event));
+					};
+					return result;
+				};
+			});
 			dispatchEvent(new UIEvent("resize"));
 			dispatchEvent(new UIEvent("scroll"));
 			const docBoundingRect = document.documentElement.getBoundingClientRect();
@@ -117,6 +152,7 @@ this.hooksFrame = this.hooksFrame || (() => {
 			delete document.documentElement.clientWidth;
 			delete screen.height;
 			delete screen.width;
+			delete document.cookie;
 			if (window._singleFile_getBoundingClientRect) {
 				Element.prototype.getBoundingClientRect = window._singleFile_getBoundingClientRect;
 				window.innerHeight = window._singleFile_innerHeight;
@@ -124,11 +160,17 @@ this.hooksFrame = this.hooksFrame || (() => {
 				delete window._singleFile_getBoundingClientRect;
 				delete window._singleFile_innerHeight;
 				delete window._singleFile_innerWidth;
-				delete document.cookie;
+			}
+			if (window._singleFile_localStorage) {
 				delete window.localStorage;
 				window.localStorage = window._singleFile_localStorage;
 				delete window._singleFile_localStorage;
 			}
+			if (window._singleFileImage) {
+				delete window.Image;
+				window.Image = window._singleFileImage;
+				delete window._singleFileImage;
+			}
 			dispatchEvent(new UIEvent("resize"));
 			dispatchEvent(new UIEvent("scroll"));
 		});

+ 17 - 4
lib/lazy/content/content-lazy-loader.js

@@ -18,7 +18,7 @@
  *   along with SingleFile.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-/* global browser, document, MutationObserver, setTimeout, clearTimeout, hooksFrame */
+/* global browser, document, MutationObserver, setTimeout, clearTimeout, hooksFrame, addEventListener, removeEventListener */
 
 this.lazyLoader = this.lazyLoader || (() => {
 
@@ -33,7 +33,7 @@ this.lazyLoader = this.lazyLoader || (() => {
 			let timeoutId, srcAttributeChanged;
 			setAsyncTimeout(() => {
 				clearAsyncTimeout(timeoutId);
-				lazyLoadEnd(idleTimeoutId, observer, resolve);
+				lazyLoadEnd(idleTimeoutId, observer, cleanupAndResolve);
 			}, options.loadDeferredImagesMaxIdleTime * 5);
 			const observer = new MutationObserver(async mutations => {
 				mutations = mutations.filter(mutation => mutation.type == ATTRIBUTES_MUTATION_TYPE);
@@ -48,7 +48,7 @@ this.lazyLoader = this.lazyLoader || (() => {
 					});
 					if (updated.length) {
 						srcAttributeChanged = true;
-						timeoutId = await deferLazyLoadEnd(timeoutId, idleTimeoutId, observer, options, resolve);
+						timeoutId = await deferLazyLoadEnd(timeoutId, idleTimeoutId, observer, options, cleanupAndResolve);
 					}
 				}
 			});
@@ -56,12 +56,25 @@ this.lazyLoader = this.lazyLoader || (() => {
 			const idleTimeoutId = await setAsyncTimeout(() => {
 				if (!srcAttributeChanged) {
 					clearAsyncTimeout(timeoutId);
-					lazyLoadEnd(idleTimeoutId, observer, resolve);
+					lazyLoadEnd(idleTimeoutId, observer, cleanupAndResolve);
 				}
 			}, options.loadDeferredImagesMaxIdleTime * 1.2);
 			if (typeof hooksFrame != "undefined") {
 				hooksFrame.loadDeferredImagesStart();
 			}
+			addEventListener(hooksFrame.LOAD_IMAGE_EVENT, onImageEvent);
+			addEventListener(hooksFrame.IMAGE_LOADED_EVENT, onImageEvent);
+
+			async function onImageEvent() {
+				timeoutId = await deferLazyLoadEnd(timeoutId, idleTimeoutId, observer, options, cleanupAndResolve);
+			}
+
+			function cleanupAndResolve(value) {
+				observer.disconnect();
+				removeEventListener(hooksFrame.LOAD_IMAGE_EVENT, onImageEvent);
+				removeEventListener(hooksFrame.IMAGE_LOADED_EVENT, onImageEvent);
+				resolve(value);
+			}
 		});
 	}