wombat_node_module_style/wombat.js
const WombatInit = require('./wombatInit');
const WombatLocation = require('./wombatLocation')
const {
equals_any,
starts_with,
ends_with,
def_prop,
get_orig_getter,
get_orig_setter,
next_parent
} = require('./utils');
/**
* The Wombat proper class
*/
class Wombat extends WombatInit {
/**
* @param {Window} $wbwindow
* @param {Object} wbinfo
*/
constructor ($wbwindow, wbinfo) {
super($wbwindow, wbinfo);
this.update_location = this.update_location.bind(this);
this.check_location_change = this.check_location_change.bind(this);
this.check_all_locations = this.check_all_locations.bind(this);
}
/**
* @desc Override HTMLBaseElement.{getAttribute,href} and Node.baseURI
*/
override_htmlBaseElem_nodeBaseURI () {
if (!Object.defineProperty) {
return;
}
// <base> element .getAttribute()
var orig_getAttribute = this.$wbwindow.HTMLBaseElement.prototype.getAttribute;
var Wombat = this;
this.$wbwindow.HTMLBaseElement.prototype.getAttribute = function (name) {
var result = orig_getAttribute.call(this, name);
if (name === 'href') {
result = Wombat.extract_orig(result);
}
return result;
};
// <base> element .href
var base_href_get = function () {
return this.getAttribute('href');
};
def_prop(this.$wbwindow.HTMLBaseElement.prototype, 'href', undefined, base_href_get);
// Shared baseURI
this.override_prop_extract(this.$wbwindow.Node.prototype, 'baseURI');
}
/**
* @desc Override XMLHttpRequest.prototype.open in order to
* - rewrite the URL unless Wombat is to not perform rewrites.
* - if the URL begins with "data:" add X-Pywb-Requested-With
*/
override_ajax () {
if (!this.$wbwindow.XMLHttpRequest ||
!this.$wbwindow.XMLHttpRequest.prototype ||
!this.$wbwindow.XMLHttpRequest.prototype.open) {
return;
}
var orig = this.$wbwindow.XMLHttpRequest.prototype.open;
var Wombat = this;
function open_rewritten (method, url, async, user, password) {
if (!this._no_rewrite) {
url = Wombat.rewrite_url(url);
}
// defaults to true
if (async !== false) {
async = true;
}
var result = orig.call(this, method, url, async, user, password);
if (!starts_with(url, 'data:')) {
this.setRequestHeader('X-Pywb-Requested-With', 'XMLHttpRequest');
}
}
this.$wbwindow.XMLHttpRequest.prototype.open = open_rewritten;
// responseURL override
this.override_prop_extract(this.$wbwindow.XMLHttpRequest.prototype, 'responseURL');
}
/**
* @desc Override document.{write,writeln,open}
* - rewrites the html string
* - if the html string is an html element that is contains its own window instance
* initialize a new instance of Wombat associated with the new window
*/
override_document_write () {
if (!this.$wbwindow.DOMParser) {
return;
}
// Write
var orig_doc_write = this.$wbwindow.document.write;
var Wombat = this;
var new_write = function (string) {
var new_buff = Wombat.rewrite_html(string, true);
if (!new_buff) {
return;
}
var res = orig_doc_write.call(this, new_buff);
Wombat.init_new_window_wombat(this.defaultView);
return res;
};
this.$wbwindow.document.write = new_write;
this.$wbwindow.Document.prototype.write = new_write;
// Writeln
var orig_doc_writeln = this.$wbwindow.document.writeln;
var new_writeln = function (string) {
var new_buff = Wombat.rewrite_html(string, true);
if (!new_buff) {
return;
}
var res = orig_doc_writeln.call(this, new_buff);
Wombat.init_new_window_wombat(this.defaultView);
return res;
};
this.$wbwindow.document.writeln = new_writeln;
this.$wbwindow.Document.prototype.writeln = new_writeln;
// Open
var orig_doc_open = this.$wbwindow.document.open;
var new_open = function () {
var res = orig_doc_open.call(this);
Wombat.init_new_window_wombat(this.defaultView);
return res;
};
this.$wbwindow.document.open = new_open;
this.$wbwindow.Document.prototype.open = new_open;
}
/**
* @desc Overrides a function on the history object by name
* @param {string} func_name the name of the history function to override
* @return {function} the rewritten func
*/
override_history_func (func_name) {
if (!this.$wbwindow.history) {
return;
}
var orig_func = this.$wbwindow.history[func_name];
if (!orig_func) {
return;
}
this.$wbwindow.history['_orig_' + func_name] = orig_func;
var me = this;
function rewritten_func (state_obj, title, url) {
url = me.rewrite_url(url);
var abs_url = me.extract_orig(url);
if (!abs_url) {
abs_url = me.$wbwindow.WB_wombat_location.href;
}
if (abs_url &&
(abs_url !== me.$wbwindow.WB_wombat_location.origin && me.$wbwindow.WB_wombat_location.href !== 'about:blank') &&
!starts_with(abs_url, me.$wbwindow.WB_wombat_location.origin + '/')) {
throw new DOMException('Invalid history change: ' + abs_url);
}
if (url === me.$wbwindow.location.href) {
return;
}
orig_func.call(this, state_obj, title, url);
if (me.$wbwindow.__WB_top_frame) {
var message = {
'url': abs_url,
'ts': me.wb_info.timestamp,
'request_ts': me.wb_info.request_ts,
'is_live': me.wb_info.is_live,
'title': title,
'wb_type': func_name
};
me.$wbwindow.__WB_top_frame.postMessage(message, me.wb_info.top_host);
}
}
this.$wbwindow.history[func_name] = rewritten_func;
if (this.$wbwindow.History && this.$wbwindow.History.prototype) {
this.$wbwindow.History.prototype[func_name] = rewritten_func;
}
return rewritten_func;
}
/**
* @desc Overrides a function on the history object by name
* @param {string} func_name
* @return {function} the rewritten func
*/
override_history_nav (func_name) {
if (!this.$wbwindow.history) {
return;
}
// Only useful for framed replay
if (!this.$wbwindow.__WB_top_frame) {
return;
}
var orig_func = this.$wbwindow.history[func_name];
if (!orig_func) {
return;
}
var me = this;
function rewritten_func () {
orig_func.apply(this, arguments);
var message = {
'wb_type': func_name
};
if (func_name === 'go') {
message['param'] = arguments[0];
}
if (me.$wbwindow.__WB_top_frame) {
me.$wbwindow.__WB_top_frame.postMessage(message, me.wb_info.top_host);
}
}
this.$wbwindow.history[func_name] = rewritten_func;
if (this.$wbwindow.History && this.$wbwindow.History.prototype) {
this.$wbwindow.History.prototype[func_name] = rewritten_func;
}
return rewritten_func;
}
/**
*
* @param {Object.prototype} proto
* @param {string} prop the prop name to define a getter for
* @param {?function} cond function that determines if the returned value from the getter
* is the result of {@link WombatRewriter#extract_orig} on the original value
*/
override_prop_extract (proto, prop, cond) {
var orig_getter = get_orig_getter(proto, prop);
var Wombat = this;
if (orig_getter) {
var new_getter = function () {
var res = Wombat.orig_getter.call(this);
if (!cond || cond(this)) {
res = Wombat.extract_orig(res);
}
return res;
};
def_prop(proto, prop, undefined, new_getter);
}
}
/**
* @desc Override window.open in order to
* - rewrite the url of the new window
* - initialize and associate a new instance of Wombat for the new window
* - override the open method of the current replay windows frames
* @todo was init_open_override
*/
override_window_open () {
var orig = this.$wbwindow.open;
if (this.$wbwindow.Window.prototype.open) {
orig = this.$wbwindow.Window.prototype.open;
}
var Wombat = this;
var open_rewritten = function (strUrl, strWindowName, strWindowFeatures) {
strUrl = Wombat.rewrite_url(strUrl, false, '');
var res = orig.call(this, strUrl, strWindowName, strWindowFeatures);
Wombat.init_new_window_wombat(res, strUrl);
return res;
};
this.$wbwindow.open = open_rewritten;
if (this.$wbwindow.Window.prototype.open) {
this.$wbwindow.Window.prototype.open = open_rewritten;
}
for (var i = 0; i < this.$wbwindow.frames.length; i++) {
try {
this.$wbwindow.frames[i].open = open_rewritten;
} catch (e) {
console.log(e);
}
}
}
/**
* @desc Override document.createElement.
* - if the element to be created is an HTML.Form override its action attribute.
* - if the skip parameter of document.createElement is set to a truthy value
* adds _no_rewrite property to the element indicating it is not to be rewritten
*/
override_createElement () {
if (!this.$wbwindow.document.createElement ||
!this.$wbwindow.Document.prototype.createElement) {
return;
}
var orig_createElement = this.$wbwindow.document.createElement;
var Wombat = this;
var createElement_override = function (tagName, skip) {
var created = orig_createElement.call(this, tagName);
if (!created) {
return created;
}
if (skip) {
created._no_rewrite = true;
} else {
// form override
if (created.tagName === 'FORM') {
Wombat.override_attr(created, 'action', '', true);
}
}
return created;
};
this.$wbwindow.Document.prototype.createElement = createElement_override;
this.$wbwindow.document.createElement = createElement_override;
}
/**
* @desc Override the getter/setter of an objects property
* @param {Object} obj the object to apply the override
* @param {string} attr the name of the property
* @param {string} mod
* @param {boolean} default_to_setget
*/
override_attr (obj, attr, mod, default_to_setget) {
var orig_getter = get_orig_getter(obj, attr);
var orig_setter = get_orig_setter(obj, attr);
var Wombat = this;
var setter = function (orig) {
var val;
if (mod === 'cs_' && orig.indexOf('data:text/css') === 0) {
var decoded;
try {
decoded = decodeURIComponent(orig);
} catch (e) {
decoded = orig;
}
if (decoded !== orig) {
val = Wombat.rewrite_style(decoded);
var parts = val.split(',', 2);
val = parts[0] + ',' + encodeURIComponent(parts[1]);
} else {
val = Wombat.rewrite_style(orig);
}
} else {
val = Wombat.rewrite_url(orig, false, mod);
}
if (orig_setter) {
return orig_setter.call(this, val);
} else if (default_to_setget) {
return Wombat.wb_setAttribute.call(this, attr, val);
}
};
var getter = function () {
var res;
if (orig_getter) {
res = orig_getter.call(this);
} else if (default_to_setget) {
res = Wombat.wb_getAttribute.call(this, attr);
}
res = Wombat.extract_orig(res);
return res;
};
def_prop(obj, attr, setter, getter);
}
/**
* @desc Override Attr.{nodeValue,value}
*/
override_attr_props () {
var Wombat = this;
function is_rw_attr (attr) {
if (attr && equals_any(attr.nodeName, Wombat.REWRITE_ATTRS)) {
return true;
}
return false;
}
this.override_prop_extract(this.$wbwindow.Attr.prototype, 'nodeValue', is_rw_attr);
this.override_prop_extract(this.$wbwindow.Attr.prototype, 'value', is_rw_attr);
}
/**
* @desc Override the a style attribute on an object
* @param {Object} obj the style prototype to apply the override to
* @param {string} attr the name of the property to override
* @param {string} prop_name the name of property to get/set
*/
override_style_attr (obj, attr, prop_name) {
var orig_getter = get_orig_getter(obj, attr);
var orig_setter = get_orig_setter(obj, attr);
var Wombat = this;
var setter = function (orig) {
var val = Wombat.rewrite_style(orig);
if (orig_setter) {
orig_setter.call(this, val);
} else {
this.setProperty(prop_name, val);
}
return val;
};
var getter = function () {
if (orig_getter) {
return orig_getter.call(this);
} else {
return this.getPropertyValue(prop_name);
}
};
if ((orig_setter && orig_getter) || prop_name) {
def_prop(obj, attr, setter, getter);
}
}
/**
* @desc
* - Override the href, src attributes of HTML Elements
* - ``HTMLLinkElement.prototype, href, cs_``
* - ``CSSStyleSheet.prototype, href, cs_``
* - ``HTMLImageElement.prototype, src, im_``
* - ``HTMLIFrameElement.prototype, src, if_``
* - ``HTMLScriptElement.prototype, src, js_``
* - ``HTMLVideoElement.prototype, src, oe_``
* - ``HTMLVideoElement.prototype, poster, im_``
* - ``HTMLAudioElement.prototype, src, oe_``
* - ``HTMLAudioElement.prototype, poster, im_``
* - ``HTMLSourceElement.prototype, src, oe_``
* - ``HTMLSourceElement.prototype, srcset, oe_``
* - ``HTMLInputElement.prototype, src, oe_``
* - ``HTMLEmbedElement.prototype, src, oe_``
* - ``HTMLObjectElement.prototype, data, oe_``
* - Override the attribute of CSSStyleDeclaration.prototype if not FireFox otherwise CSS2Properties.prototype
* - ``cssText``
* - ``background, background``
* - ``backgroundImage, background-image``
* - ``listStyle, list-style``
* - ``listStyleImage, list-style-image``
* - ``border, border``
* - ``borderImage, border-image``
* - ``borderImageSource, border-image-source``
*/
attr_overrides () {
this.override_attr(this.$wbwindow.HTMLLinkElement.prototype, 'href', 'cs_');
this.override_attr(this.$wbwindow.CSSStyleSheet.prototype, 'href', 'cs_');
this.override_attr(this.$wbwindow.HTMLImageElement.prototype, 'src', 'im_');
this.override_attr(this.$wbwindow.HTMLIFrameElement.prototype, 'src', 'if_');
this.override_attr(this.$wbwindow.HTMLScriptElement.prototype, 'src', 'js_');
this.override_attr(this.$wbwindow.HTMLVideoElement.prototype, 'src', 'oe_');
this.override_attr(this.$wbwindow.HTMLVideoElement.prototype, 'poster', 'im_');
this.override_attr(this.$wbwindow.HTMLAudioElement.prototype, 'src', 'oe_');
this.override_attr(this.$wbwindow.HTMLAudioElement.prototype, 'poster', 'im_');
this.override_attr(this.$wbwindow.HTMLSourceElement.prototype, 'src', 'oe_');
this.override_attr(this.$wbwindow.HTMLSourceElement.prototype, 'srcset', 'oe_');
this.override_attr(this.$wbwindow.HTMLInputElement.prototype, 'src', 'oe_');
this.override_attr(this.$wbwindow.HTMLEmbedElement.prototype, 'src', 'oe_');
this.override_attr(this.$wbwindow.HTMLObjectElement.prototype, 'data', 'oe_');
this.override_anchor_elem();
var style_proto = this.$wbwindow.CSSStyleDeclaration.prototype;
// For FF
if (this.$wbwindow.CSS2Properties) {
style_proto = this.$wbwindow.CSS2Properties.prototype;
}
this.override_style_attr(style_proto, 'cssText');
this.override_style_attr(style_proto, 'background', 'background');
this.override_style_attr(style_proto, 'backgroundImage', 'background-image');
this.override_style_attr(style_proto, 'listStyle', 'list-style');
this.override_style_attr(style_proto, 'listStyleImage', 'list-style-image');
this.override_style_attr(style_proto, 'border', 'border');
this.override_style_attr(style_proto, 'borderImage', 'border-image');
this.override_style_attr(style_proto, 'borderImageSource', 'border-image-source');
}
/**
* @desc Override the HTMLAnchorElement properties
*/
override_anchor_elem () {
var anchor_orig = {};
var Wombat = this;
function save_prop (prop) {
anchor_orig['get_' + prop] = get_orig_getter(Wombat.$wbwindow.HTMLAnchorElement.prototype, prop);
anchor_orig['set_' + prop] = get_orig_setter(Wombat.$wbwindow.HTMLAnchorElement.prototype, prop);
}
for (var i = 0; i < this.URL_PROPS.length; i++) {
save_prop(this.URL_PROPS[i]);
}
var anchor_setter = function (prop, value) {
var func = anchor_orig['set_' + prop];
if (func) {
return func.call(this, value);
} else {
return '';
}
};
var anchor_getter = function (prop) {
var func = anchor_orig['get_' + prop];
if (func) {
return func.call(this);
} else {
return '';
}
};
this.init_loc_override($wbwindow.HTMLAnchorElement.prototype, anchor_setter, anchor_getter);
this.$wbwindow.HTMLAnchorElement.prototype.toString = function () {
return this.href;
};
}
/**
* @param {HTMLElement|HTMLIFrameElement|HTMLStyleElement} elemtype
* @param {string} prop the property name to override
*/
override_html_assign (elemtype, prop) {
if (!$wbwindow.DOMParser ||
!elemtype ||
!elemtype.prototype) {
return;
}
var Wombat = this;
var obj = elemtype.prototype;
var orig_getter = get_orig_getter(obj, prop);
var orig_setter = get_orig_setter(obj, prop);
if (!orig_setter) {
return;
}
var setter = function (orig) {
var res = orig;
if (!this._no_rewrite) {
// init_iframe_insert_obs(this);
if (this.tagName === 'STYLE') {
res = Wombat.rewrite_style(orig);
} else {
res = Wombat.rewrite_html(orig);
}
}
orig_setter.call(this, res);
};
def_prop(obj, prop, setter, orig_getter);
}
/**
* @desc iframe.contentWindow and iframe.contentDocument overrides to ensure wombat is inited on the iframe $wbwindow
* @param {string} prop
*/
override_iframe_content_access (prop) {
if (!this.$wbwindow.HTMLIFrameElement ||
!this.$wbwindow.HTMLIFrameElement.prototype) {
return;
}
var Wombat = this;
var obj = this.$wbwindow.HTMLIFrameElement.prototype;
var orig_getter = get_orig_getter(obj, prop);
var orig_setter = get_orig_setter(obj, prop);
if (!orig_getter) {
return;
}
var getter = function () {
Wombat.init_iframe_wombat(this);
return orig_getter.call(this);
};
def_prop(obj, prop, orig_setter, getter);
obj['_get_' + prop] = orig_getter;
}
/**
* @desc Override $wbwindow.frames to ensure wombat is inited on them
*/
override_frames_access () {
this.$wbwindow.__wb_frames = this.$wbwindow.frames;
var Wombat = this;
var getter = function () {
for (var i = 0; i < this.__wb_frames.length; i++) {
Wombat.init_new_window_wombat(this.__wb_frames[i]);
}
return this.__wb_frames;
};
def_prop(this.$wbwindow, 'frames', undefined, getter);
def_prop(this.$wbwindow.Window.prototype, 'frames', undefined, getter);
}
/**
* @desc Init wombat on an iframe
* @param {HTMLIFrameElement|HTMLFrameElement} iframe
*/
init_iframe_wombat (iframe) {
var win;
if (iframe._get_contentWindow) {
win = iframe._get_contentWindow.call(iframe);
} else {
win = iframe.contentWindow;
}
try {
if (!win || win === this.$wbwindow || win._skip_wombat || win._wb_wombat) {
return;
}
} catch (e) {
return;
}
// var src = iframe.src;
var src = this.wb_getAttribute.call(iframe, 'src');
this.init_new_window_wombat(win, src);
}
init_new_window_wombat (win, src) {
if (!win || win._wb_wombat) {
return;
}
if (!src || src === '' || src === 'about:blank' || src.indexOf('javascript:') >= 0) {
// win._WBWombat = wombat_internal(win);
// win._wb_wombat = new win._WBWombat(wb_info);
var wombat = new Wombat(win, this.wb_info)
win._wb_wombat = wombat.wombat_init()
} else {
// These should get overriden when content is loaded, but just in case...
// win._WB_wombat_location = win.location;
// win.document.WB_wombat_location = win.document.location;
// win._WB_wombat_top = $wbwindow.WB_wombat_top;
this.init_proto_pm_origin(win);
this.init_postmessage_override();
this.init_messageevent_override();
}
}
/**
* @desc
*/
init_doc_overrides () {
if (!Object.defineProperty) {
return;
}
if (this.$wbwindow.document._wb_override) {
return;
}
var orig_referrer = this.extract_orig(this.$wbwindow.document.referrer);
var domain_info;
if (this.$wbwindow.wbinfo) {
domain_info = this.$wbwindow.wbinfo;
} else {
domain_info = this.wbinfo;
}
domain_info.domain = domain_info.wombat_host;
var domain_setter = function (val) {
if (ends_with(domain_info.wombat_host, val)) {
domain_info.domain = val;
}
};
var domain_getter = function () {
return domain_info.domain;
};
// changing domain disallowed, but set as no-op to avoid errors
def_prop(this.$wbwindow.document, 'domain', domain_setter, domain_getter);
def_prop(this.$wbwindow.document, 'referrer', undefined, function () { return orig_referrer; });
// Cookies
this.init_cookies_override();
// Init mutation observer (for style only)
// init_mutation_obs($wbwindow);
// override href and src attrs
this.attr_overrides();
this.init_form_overrides();
// Attr observers
// if (!wb_opts.skip_attr_observers) {
// init_href_src_obs($wbwindow);
// }
this.$wbwindow.document._wb_override = true;
}
/**
* @desc Override the form action attribute of all forms in the page being replayed
*/
init_form_overrides () {
var Wombat = this;
var do_init_forms = function () {
for (var i = 0; i < Wombat.$wbwindow.document.forms.length; i++) {
var new_action = Wombat.rewrite_url(Wombat.$wbwindow.document.forms[i].action);
if (new_action !== Wombat.$wbwindow.document.forms[i].action) {
Wombat.$wbwindow.document.forms[i].action = new_action;
}
Wombat.override_attr(Wombat.$wbwindow.document.forms[i], 'action', '', true);
}
};
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', do_init_forms);
} else {
do_init_forms();
}
}
/**
* @desc Initialize ``__WB_replay_top, __WB_top_frame, __WB_orig_parent, __WB_orig_parent``
* to the correct values
*/
init_top_frame () {
// proxy mode
if (this.wb_is_proxy) {
this.$wbwindow.__WB_replay_top = this.$wbwindow.top;
this.$wbwindow.__WB_top_frame = undefined;
return;
}
var replay_top = this.$wbwindow;
while ((replay_top.parent !== replay_top) && next_parent(replay_top.parent)) {
replay_top = replay_top.parent;
}
this.$wbwindow.__WB_replay_top = replay_top;
var real_parent = replay_top.__WB_orig_parent || replay_top.parent;
// Check to ensure top frame is different window and directly accessible (later refactor to support postMessage)
// try {
// if ((real_parent == $wbwindow) || !real_parent.wbinfo || !real_parent.wbinfo.is_frame) {
// real_parent = undefined;
// }
// } catch (e) {
// real_parent = undefined;
// }
if (real_parent === this.$wbwindow || !this.wb_info.is_framed) {
real_parent = undefined;
}
if (real_parent) {
this.$wbwindow.__WB_top_frame = real_parent;
this.init_frameElement_override(this.$wbwindow);
} else {
this.$wbwindow.__WB_top_frame = undefined;
}
// Fix .parent only if not embeddable, otherwise leave for accessing embedding window
if (!this.wb_opts.embedded && (replay_top === this.$wbwindow)) {
this.$wbwindow.__WB_orig_parent = this.$wbwindow.parent;
this.$wbwindow.parent = replay_top;
}
}
/**
* @desc Init WombatLocation for the window
* @param {Window} win
*/
init_wombat_loc (win) {
if (!win || (win.WB_wombat_location && win.document.WB_wombat_location)) {
return;
}
// Location
var wombat_location = new WombatLocation(win.location, this);
if (Object.defineProperty) {
var setter = function (value) {
if (this._WB_wombat_location) {
this._WB_wombat_location.href = value;
} else {
this.location = value;
}
};
var getter = function () {
if (this._WB_wombat_location) {
return this._WB_wombat_location;
} else {
return this.location;
}
};
def_prop(win.Object.prototype, 'WB_wombat_location', setter, getter);
this.init_proto_pm_origin(win);
win._WB_wombat_location = wombat_location;
win.document._WB_wombat_location = wombat_location;
} else {
win.WB_wombat_location = wombat_location;
win.document.WB_wombat_location = wombat_location;
// Check quickly after page load
setTimeout(this.check_all_locations, 500);
// Check periodically every few seconds
setInterval(this.check_all_locations, 500);
}
}
/**
* @desc Override Node.{appendChild,insertBefore,replaceChild}
* - if the child is an instanceof window.Element {@link WombatRewriter#rewrite_elem}
* - if the child is style {@link WombatRewriter#rewrite_style}
* - if the child is an iframe {@link WBWombat#init_iframe_wombat}
*/
override_dom_functions () {
if (!this.$wbwindow.Node || !this.$wbwindow.Node.prototype) {
return;
}
var Wombat = this;
function replace_dom_func (funcname) {
var orig = Wombat.$wbwindow.Node.prototype[funcname];
Wombat.$wbwindow.Node.prototype[funcname] = function () {
var child = arguments[0];
if (child) {
if (child instanceof Wombat.$wbwindow.Element) {
Wombat.rewrite_elem(child);
} else if (child instanceof Wombat.$wbwindow.Text) {
if (this.tagName === 'STYLE') {
child.textContent = Wombat.rewrite_style(child.textContent);
}
}
}
var created = orig.apply(this, arguments);
if (created && created.tagName === 'IFRAME') {
Wombat.init_iframe_wombat(created);
}
return created;
};
}
replace_dom_func('appendChild');
replace_dom_func('insertBefore');
replace_dom_func('replaceChild');
}
/**
* @desc setup wombat
* @return {{extract_orig: function,rewrite_url: function,watch_elem: function,
init_new_window_wombat: function,init_paths: function}}
*/
wombat_init () {
this.init_top_frame();
this.init_wombat_loc(this.$wbwindow);
// archival mode: init url-rewriting intercepts
if (!this.wb_is_proxy) {
this.init_wombat_top();
if (this.wb_replay_prefix && this.wb_replay_prefix.indexOf(this.$wbwindow.__WB_replay_top.location.origin) === 0) {
this.wb_rel_prefix = this.wb_replay_prefix.substring(this.$wbwindow.__WB_replay_top.location.origin.length + 1);
} else {
this.wb_rel_prefix = this.wb_replay_prefix;
}
this.wb_rel_prefix_check = this.wb_rel_prefix;
// if ($wbwindow.opener) {
// $wbwindow.opener.WB_wombat_location = copy_location_obj($wbwindow.opener.location);
// }
// Domain
// $wbwindow.document.WB_wombat_domain = wbinfo.wombat_host;
// $wbwindow.document.WB_wombat_referrer = extract_orig($wbwindow.document.referrer);
this.init_doc_overrides();
// History
this.override_history_func('pushState');
this.override_history_func('replaceState');
this.override_history_nav('go');
this.override_history_nav('back');
this.override_history_nav('forward');
// postMessage
// OPT skip
if (!this.wb_opts.skip_postmessage) {
this.init_postmessage_override();
this.init_messageevent_override();
}
this.init_hash_change();
// write
this.override_document_write();
// eval
// this.init_eval_override();
// Ajax
this.override_ajax();
// Fetch
this.init_fetch_rewrite();
// Worker override (experimental)
this.init_web_worker_override();
this.init_service_worker_override();
// innerHTML can be overriden on prototype!
this.override_html_assign(this.$wbwindow.HTMLElement, 'innerHTML');
this.override_html_assign(this.$wbwindow.HTMLIFrameElement, 'srcdoc');
this.override_html_assign(this.$wbwindow.HTMLStyleElement, 'textContent');
// Document.URL override
this.override_prop_extract(this.$wbwindow.Document.prototype, 'URL');
this.override_prop_extract(this.$wbwindow.Document.prototype, 'documentURI');
// Attr nodeValue and value
this.override_attr_props();
// init insertAdjacentHTML() override
this.init_insertAdjacentHTML_override();
// iframe.contentWindow and iframe.contentDocument overrides to
// ensure wombat is inited on the iframe $wbwindow!
this.override_iframe_content_access('contentWindow');
this.override_iframe_content_access('contentDocument');
this.override_frames_access();
// base override
this.override_htmlBaseElem_nodeBaseURI();
// setAttribute
if (!this.wb_opts.skip_setAttribute) {
this.init_setAttribute_override();
this.init_getAttribute_override();
}
// createElement attr override
if (!this.wb_opts.skip_createElement) {
this.override_createElement();
}
// ensure namespace urls are NOT rewritten
this.init_createElementNS_fix();
// Image
// this.init_image_override();
// DOM
// OPT skip
if (!this.wb_opts.skip_dom) {
this.override_dom_functions();
}
// registerProtocolHandler override
this.init_registerPH_override();
// sendBeacon override
this.init_beacon_override();
}
// other overrides
// proxy mode: only using these overrides
// Random
this.init_seeded_random(this.wbinfo.wombat_sec);
// Crypto Random
this.init_crypto_random();
// Date
this.init_date_override(this.wbinfo.wombat_sec);
// open
this.override_window_open();
// disable notifications
this.init_disable_notifications();
// expose functions
var obj = {};
obj.extract_orig = this.extract_orig.bind(this);
obj.rewrite_url = this.rewrite_url.bind(this);
obj.watch_elem = this.watch_elem.bind(this);
obj.init_new_window_wombat = this.init_new_window_wombat.bind(this);
obj.init_paths = this.init_paths.bind(this);
return obj;
}
/**
* @desc Utility functions used by rewriting rules
* @param elem
* @param func
* @return {boolean}
*/
watch_elem (elem, func) {
if (!$wbwindow.MutationObserver) {
return false;
}
var m = new MutationObserver(function (records, observer) {
for (var i = 0; i < records.length; i++) {
var r = records[i];
if (r.type === 'childList') {
for (var j = 0; j < r.addedNodes.length; j++) {
func(r.addedNodes[j]);
}
}
}
});
m.observe(elem, {
childList: true,
subtree: true
});
}
/**
* @desc Update the WombatLocation
* @param {string} req_href
* @param {string} orig_href
* @param {Location} actual_location
* @param {WombatLocation} wombat_loc
*/
update_location (req_href, orig_href, actual_location, wombat_loc) {
if (!req_href) {
return;
}
if (req_href === orig_href) {
// Reset wombat loc to the unrewritten version
//if (wombat_loc) {
// wombat_loc.href = extract_orig(orig_href);
//}
return;
}
var ext_orig = this.extract_orig(orig_href);
var ext_req = this.extract_orig(req_href);
if (!ext_orig || ext_orig === ext_req) {
return;
}
var final_href = this.rewrite_url(req_href);
console.log(actual_location.href + ' -> ' + final_href);
actual_location.href = final_href;
}
/**
* @desc Check if the location has changed
* @param {WombatLocation} wombat_loc
* @param {boolean} is_top
*/
check_location_change (wombat_loc, is_top) {
var locType = (typeof wombat_loc);
var actual_location = (is_top ? this.$wbwindow.__WB_replay_top.location : this.$wbwindow.location);
// String has been assigned to location, so assign it
if (locType === "string") {
this.update_location(wombat_loc, actual_location.href, actual_location);
} else if (locType === "object") {
this.update_location(wombat_loc.href,
wombat_loc._orig_href,
actual_location);
}
}
/**
* @desc check replay-iframe location and replay_top location
* @return {boolean}
*/
check_all_locations () {
if (this.wb_wombat_updating) {
return false;
}
this.wb_wombat_updating = true;
this.check_location_change(this.$wbwindow.WB_wombat_location, false);
// Only check top if its a different $wbwindow
if (this.$wbwindow.WB_wombat_location !== this.$wbwindow.__WB_replay_top.WB_wombat_location) {
this.check_location_change(this.$wbwindow.__WB_replay_top.WB_wombat_location, true);
}
// lochash = $wbwindow.WB_wombat_location.hash;
//
// if (lochash) {
// $wbwindow.location.hash = lochash;
//
// //if ($wbwindow.top.update_wb_url) {
// // $wbwindow.top.location.hash = lochash;
// //}
// }
this.wb_wombat_updating = false;
}
}
module.exports = Wombat