Просмотр исходного кода

added fallback implementation to retrieve frame contents

Gildas 7 лет назад
Родитель
Сommit
3005a2a261
2 измененных файлов с 77 добавлено и 2 удалено
  1. 76 2
      lib/single-file/frame-tree.js
  2. 1 0
      manifest.json

+ 76 - 2
lib/single-file/frame-tree.js

@@ -18,7 +18,7 @@
  *   along with SingleFile.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-/* global window, top, document, addEventListener, docHelper, timeout, MessageChannel */
+/* global window, top, document, addEventListener, docHelper, timeout, MessageChannel, superFetch, fetch, TextDecoder, DOMParser */
 
 this.frameTree = this.frameTree || (() => {
 
@@ -28,6 +28,9 @@ this.frameTree = this.frameTree || (() => {
 	const INIT_RESPONSE_MESSAGE = "initResponse";
 	const TARGET_ORIGIN = "*";
 	const TIMEOUT_INIT_REQUEST_MESSAGE = 500;
+	const REGEXP_SIMPLE_QUOTES_STRING = /^'(.*?)'$/;
+	const REGEXP_DOUBLE_QUOTES_STRING = /^"(.*?)"$/;
+	const PREFIX_VALID_FRAME_URL = /^https?:\/\//;
 	const TOP_WINDOW_ID = "0";
 	const WINDOW_ID_SEPARATOR = ".";
 	const TOP_WINDOW = window == top;
@@ -128,8 +131,18 @@ this.frameTree = this.frameTree || (() => {
 				} catch (error) {
 					/* ignored */
 				}
+				timeout.set(async () => {
+					let content;
+					if (frameElement.src && frameElement.src.match(PREFIX_VALID_FRAME_URL)) {
+						content = await getFrameContent(frameElement.src, parentWindowId, options);
+					}
+					if (content) {
+						sendInitResponse({ framesData: [{ windowId, processed: true, content }], sessionId });
+					} else {
+						sendInitResponse({ framesData: [{ windowId, processed: true, timeout: true }], sessionId });
+					}
+				}, TIMEOUT_INIT_REQUEST_MESSAGE);
 			}
-			timeout.set(() => sendInitResponse({ framesData: [{ windowId, processed: true, timeout: true }], sessionId }), TIMEOUT_INIT_REQUEST_MESSAGE);
 		});
 		sendInitResponse({ framesData, sessionId });
 	}
@@ -170,6 +183,67 @@ this.frameTree = this.frameTree || (() => {
 		}
 	}
 
+	async function getFrameContent(frameUrl, parentWindowId, options) {
+		let frameContent;
+		try {
+			frameContent = await ((typeof superFetch !== "undefined" && superFetch.fetch) || fetch)(frameUrl);
+		} catch (error) {
+			/* ignored */
+		}
+		if (frameContent.status >= 400 && superFetch.hostFetch) {
+			try {
+				frameContent = await superFetch.hostFetch(frameUrl);
+			} catch (error) {
+				/* ignored */
+			}
+		}
+		if (frameContent) {
+			const contentType = frameContent.headers && frameContent.headers.get("content-type");
+			let charSet, mimeType;
+			if (contentType) {
+				const matchContentType = contentType.toLowerCase().split(";");
+				mimeType = matchContentType[0].trim();
+				if (mimeType.indexOf("/") <= 0) {
+					mimeType = "text/html";
+				}
+				const charSetValue = matchContentType[1] && matchContentType[1].trim();
+				if (charSetValue) {
+					const matchCharSet = charSetValue.match(/^charset=(.*)/);
+					if (matchCharSet) {
+						charSet = removeQuotes(matchCharSet[1]);
+					}
+				}
+			}
+			const buffer = await frameContent.arrayBuffer();
+			let doc;
+			try {
+				const content = (new TextDecoder(charSet)).decode(buffer);
+				const domParser = new DOMParser();
+				doc = domParser.parseFromString(content, mimeType);
+			} catch (error) {
+				/* ignored */
+			}
+			if (doc) {
+				const frameElements = doc.documentElement.querySelectorAll(FRAMES_CSS_SELECTOR);
+				frameElements.forEach((frameElement, frameIndex) => {
+					const windowId = parentWindowId + WINDOW_ID_SEPARATOR + frameIndex;
+					frameElement.setAttribute(docHelper.windowIdAttributeName(options.sessionId), windowId);
+				});
+				return docHelper.serialize(doc);
+			}
+		}
+	}
+
+	function removeQuotes(string) {
+		string = string.toLowerCase().trim();
+		if (string.match(REGEXP_SIMPLE_QUOTES_STRING)) {
+			string = string.replace(REGEXP_SIMPLE_QUOTES_STRING, "$1");
+		} else {
+			string = string.replace(REGEXP_DOUBLE_QUOTES_STRING, "$1");
+		}
+		return string.trim();
+	}
+
 	function getFrameData(document, window, windowId, options) {
 		const docData = docHelper.preProcessDoc(document, window, options);
 		const content = docHelper.serialize(document);

+ 1 - 0
manifest.json

@@ -21,6 +21,7 @@
 				"extension/index.js",
 				"lib/single-file/doc-helper.js",
 				"lib/single-file/timeout.js",
+				"lib/fetch/content/fetch.js",
 				"lib/single-file/frame-tree.js",
 				"extension/core/content/content-frame.js"
 			],