|
@@ -18,7 +18,7 @@
|
|
|
* along with SingleFile. If not, see <http://www.gnu.org/licenses/>.
|
|
* 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 || (() => {
|
|
this.frameTree = this.frameTree || (() => {
|
|
|
|
|
|
|
@@ -28,6 +28,9 @@ this.frameTree = this.frameTree || (() => {
|
|
|
const INIT_RESPONSE_MESSAGE = "initResponse";
|
|
const INIT_RESPONSE_MESSAGE = "initResponse";
|
|
|
const TARGET_ORIGIN = "*";
|
|
const TARGET_ORIGIN = "*";
|
|
|
const TIMEOUT_INIT_REQUEST_MESSAGE = 500;
|
|
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 TOP_WINDOW_ID = "0";
|
|
|
const WINDOW_ID_SEPARATOR = ".";
|
|
const WINDOW_ID_SEPARATOR = ".";
|
|
|
const TOP_WINDOW = window == top;
|
|
const TOP_WINDOW = window == top;
|
|
@@ -128,8 +131,18 @@ this.frameTree = this.frameTree || (() => {
|
|
|
} catch (error) {
|
|
} catch (error) {
|
|
|
/* ignored */
|
|
/* 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 });
|
|
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) {
|
|
function getFrameData(document, window, windowId, options) {
|
|
|
const docData = docHelper.preProcessDoc(document, window, options);
|
|
const docData = docHelper.preProcessDoc(document, window, options);
|
|
|
const content = docHelper.serialize(document);
|
|
const content = docHelper.serialize(document);
|