1
0
mirror of https://github.com/webrecorder/pywb.git synced 2025-03-15 00:03:28 +01:00

LocalStorage/SessionStorage Overrides (#235)

* client-side rewrite: Custom LocalStorage/SessionStorage override:
- custom, in-mem only objects for localStorage and sessionStorage to avoid polluting browser storage, using Proxy if available to allow accessors
- storage event listeners tracked in addEventListener override, called directly with custom StorageEvent.
- storage event listener wrapped in SameOriginListener() to prevent notifying listeners from different origins

* addEventListener fix: prevents duplicate additions for wrapped listeners, for both message and storage
This commit is contained in:
Ilya Kreymer 2017-09-06 23:14:48 -07:00 committed by GitHub
parent 31dbbc4f05
commit 5a0867fed9

View File

@ -18,7 +18,7 @@ This file is part of pywb, https://github.com/ikreymer/pywb
*/
//============================================
// Wombat JS-Rewriting Library v2.45
// Wombat JS-Rewriting Library v2.46
//============================================
@ -47,6 +47,8 @@ var _WBWombat = function($wbwindow, wbinfo) {
var wb_is_proxy = false;
var storage_listeners = {};
//============================================
function is_host_url(str) {
// Good guess that's its a hostname
@ -2082,6 +2084,17 @@ var _WBWombat = function($wbwindow, wbinfo) {
$wbwindow.Window.prototype.postMessage = postmessage_rewritten;
function SameOriginListener(orig_listener, win) {
function listen(event) {
if (window != win) {
return;
}
return orig_listener(event);
}
return {"listen": listen};
}
function WrappedListener(orig_listener, win) {
function listen(event) {
@ -2133,16 +2146,28 @@ var _WBWombat = function($wbwindow, wbinfo) {
var _orig_removeEventListener = $wbwindow.removeEventListener;
var addEventListener_rewritten = function(type, listener, useCapture) {
var obj = proxy_to_obj(this);
if (type == "message") {
var wrapped_listener = new WrappedListener(listener, this);
var wrapped = listen_map[listener];
if (!wrapped) {
wrapped = new WrappedListener(listener, this).listen;
listen_map[listener] = wrapped;
}
listen_map[listener] = wrapped_listener;
listener = wrapped;
return _orig_addEventListener.call(obj, type, wrapped_listener.listen, useCapture);
return _orig_addEventListener.call(obj, type, listener, useCapture);
} else if (type == "storage") {
var wrapped = storage_listeners[listener];
if (!wrapped) {
wrapped = new SameOriginListener(listener, this).listen;
storage_listeners[listener] = wrapped;
}
listener = wrapped;
} else {
return _orig_addEventListener.call(obj, type, listener, useCapture);
}
@ -2151,54 +2176,64 @@ var _WBWombat = function($wbwindow, wbinfo) {
$wbwindow.addEventListener = addEventListener_rewritten;
// REMOVE
var removeEventListener_rewritten = function(type, listener, useCapture) {
var obj = proxy_to_obj(this);
if (type == "message") {
var wrapped_listener = listen_map[listener];
if (!wrapped_listener) {
if (!listen_map[listener]) {
return;
}
listener = listen_map[listener];
delete listen_map[listener];
return _orig_removeEventListener.call(obj, type, wrapped_listener.listen, useCapture);
} else {
return _orig_removeEventListener.call(obj, type, listener, useCapture);
} else if (type == "storage") {
if (!storage_listeners[listener]) {
return;
}
listener = storage_listeners[listener];
delete storage_listeners[listener];
}
return _orig_removeEventListener.call(obj, type, listener, useCapture);
}
$wbwindow.removeEventListener = removeEventListener_rewritten;
}
//============================================
function addEventOverride(attr, event_proto)
{
if (!event_proto) {
event_proto = $wbwindow.MessageEvent.prototype;
}
var orig_getter = get_orig_getter(event_proto, attr);
if (!orig_getter) {
return;
}
function getter() {
if (this["_" + attr] != undefined) {
return this["_" + attr];
}
return orig_getter.call(this);
}
def_prop(event_proto, attr, undefined, getter);
}
//============================================
function init_messageevent_override($wbwindow) {
if (!$wbwindow.MessageEvent || $wbwindow.MessageEvent.prototype.__extended) {
return;
}
function addMEOverride(attr)
{
var orig_getter = get_orig_getter($wbwindow.MessageEvent.prototype, attr);
if (!orig_getter) {
return;
}
function getter() {
if (this["_" + attr] != undefined) {
return this["_" + attr];
}
return orig_getter.call(this);
}
def_prop($wbwindow.MessageEvent.prototype, attr, undefined, getter);
}
addMEOverride("target");
addMEOverride("srcElement");
addMEOverride("currentTarget");
addMEOverride("eventPhase");
addMEOverride("path");
addEventOverride("target");
addEventOverride("srcElement");
addEventOverride("currentTarget");
addEventOverride("eventPhase");
addEventOverride("path");
override_prop_to_proxy($wbwindow.MessageEvent.prototype, "source");
@ -2556,6 +2591,108 @@ var _WBWombat = function($wbwindow, wbinfo) {
}
}
//============================================
function init_storage_override() {
var CustomStorage = function() {
function fire_event(store, key, old_val, new_val) {
var sevent = new StorageEvent("storage", {
"key": key,
"newValue": new_val,
"oldValue": old_val,
"url": $wbwindow.WB_wombat_location.href,
});
sevent._storageArea = store;
for (var list in storage_listeners) {
storage_listeners[list](sevent);
}
}
this.data = {}
this.getItem = function(name) {
return this.data.hasOwnProperty(name) ? this.data[name] : null;
}
this.setItem = function(name, value) {
name = String(name);
//if (name.length > 1000) {
// name = name.substr(0, 1000);
//}
value = String(value);
var old_val = this.getItem(name);
this.data[name] = value;
fire_event(this, name, old_val, value);
}
this.removeItem = function(name) {
var old_val = this.getItem(name);
res = delete this.data[name];
fire_event(this, name, old_val, null);
return res;
}
this.clear = function() {
this.data = {};
fire_event(this, null, null, null);
}
this.key = function(n) {
var keys = Object.keys(this.data);
if (typeof(n) === "number" && n >= 0 && n < keys.length) {
return keys[n];
} else {
return null;
}
}
Object.defineProperty(this, "length", {"get": function() {
return Object.keys(this.data).length;
}});
}
addEventOverride("storageArea", $wbwindow.StorageEvent.prototype);
var local = new CustomStorage();
var session = new CustomStorage();
if ($wbwindow.Proxy) {
function wrap_proxy(obj) {
return new $wbwindow.Proxy(obj, {
get: function(target, prop) {
if (prop in target) {
return target[prop];
}
return target.getItem(prop);
},
set: function(target, prop, value) {
if (target.hasOwnProperty(prop)) {
return false;
}
target.setItem(prop, value);
return true;
},
getOwnPropertyDescriptor: function(target, prop) {
var descriptor = Object.getOwnPropertyDescriptor(target, prop);
return descriptor;
}
});
}
local = wrap_proxy(local);
session = wrap_proxy(session);
}
def_prop($wbwindow, "localStorage", undefined, function() { return local; });
def_prop($wbwindow, "sessionStorage", undefined, function() { return session; });
}
//============================================
function get_final_url(use_rel, mod, url) {
var prefix = use_rel ? wb_rel_prefix : wb_abs_prefix;
@ -2939,6 +3076,9 @@ var _WBWombat = function($wbwindow, wbinfo) {
// disable notifications
init_disable_notifications();
// custom storage
init_storage_override();
// add window and document obj proxies, if available
init_window_obj_proxy($wbwindow);
init_document_obj_proxy($wbwindow.document);