From aa0a0195678b87aa86b6ddc729459819420e40dd Mon Sep 17 00:00:00 2001 From: Ilya Kreymer Date: Sat, 30 Sep 2017 21:09:38 -0700 Subject: [PATCH] Frame insert refactor (#246) refactor frame/head insert templates: ContentFrame: - content iframe inited with new ContentFrame() which creates iframe - wb_frame.js: contains ContentFrame system for initing, updating, closing content frame for replayed content. - wb_frame.js: supports 'app_prefix' and 'content_prefix' or default 'prefix' for replay content - window.location.hash passed added to init url. - frame insert and head insert: simplify, remove 'wbrequest' - frame insert: global wbinfo object no longer needed in top frame, each ContentFrame self-contained. - wombat.js: next_parent() check does not assume wbinfo is present in top frame - vidrw.js: only init if wbinfo is present Banner: - wb.js no longer needed, frame check/redirect folded into wombat.js - default banner self-contained in default_banner.js/default_banner.css, handles both frame and frameless case - rename wb.css -> default_banner.css - banner html passed in as 'banner_html' variable to be optionally included, supports per collection banner html. - templateview: BaseInsertView can accept an option 'banner view', used by HeadInsertView and TopFrameView Tests: - tests: test_auto_colls uses shared app to test dynamic changes, testing both frame and non-frame access, added per-collection banner html check. --- pywb/apps/rewriterapp.py | 6 +- pywb/apps/test/test_rewriter.py | 6 +- pywb/rewrite/templateview.py | 69 ++--- pywb/static/{wb.css => default_banner.css} | 2 +- pywb/static/default_banner.js | 104 ++++--- pywb/static/vidrw.js | 6 +- pywb/static/wb.js | 176 ------------ pywb/static/wb_frame.js | 306 +++++++++------------ pywb/static/wombat.js | 53 +++- pywb/templates/banner.html | 4 +- pywb/templates/frame_insert.html | 27 +- pywb/templates/head_insert.html | 57 ++-- tests/test_auto_colls.py | 76 ++--- tests/test_integration.py | 35 +-- tests/test_live_rewriter.py | 5 +- tests/test_memento.py | 6 +- tests/test_root_coll.py | 4 +- 17 files changed, 401 insertions(+), 541 deletions(-) rename pywb/static/{wb.css => default_banner.css} (98%) delete mode 100644 pywb/static/wb.js diff --git a/pywb/apps/rewriterapp.py b/pywb/apps/rewriterapp.py index 13fb9875..25c6e2f7 100644 --- a/pywb/apps/rewriterapp.py +++ b/pywb/apps/rewriterapp.py @@ -71,13 +71,15 @@ class RewriterApp(object): self.jinja_env = jinja_env + self.banner_view = BaseInsertView(self.jinja_env, self._html_templ('banner_html')) + self.head_insert_view = HeadInsertView(self.jinja_env, self._html_templ('head_insert_html'), - self._html_templ('banner_html')) + self.banner_view) self.frame_insert_view = TopFrameView(self.jinja_env, self._html_templ('frame_insert_html'), - self._html_templ('banner_html')) + self.banner_view) self.error_view = BaseInsertView(self.jinja_env, self._html_templ('error_html')) self.not_found_view = BaseInsertView(self.jinja_env, self._html_templ('not_found_html')) diff --git a/pywb/apps/test/test_rewriter.py b/pywb/apps/test/test_rewriter.py index 834dc161..e77a7842 100644 --- a/pywb/apps/test/test_rewriter.py +++ b/pywb/apps/test/test_rewriter.py @@ -28,15 +28,13 @@ class TestRewriterApp(FakeRedisTests, BaseTestClass): assert '"http://localhost:80/live/mp_/http://www.iana.org/domains/example"' in resp.text - assert 'wbinfo.url = "http://example.com/"' + assert '"http://example.com/"' def test_top_frame(self): resp = self.testapp.get('/live/http://example.com/') resp.charset = 'utf-8' - assert '"http://localhost:80/live/mp_/http://example.com/"' in resp.text - - assert 'wbinfo.capture_url = "http://example.com/"' in resp.text + assert '"http://example.com/"' in resp.text #def test_cookie_track_1(self): # resp = self.testapp.get('/live/mp_/https://twitter.com/') diff --git a/pywb/rewrite/templateview.py b/pywb/rewrite/templateview.py index 4a7973b8..2d94fe89 100644 --- a/pywb/rewrite/templateview.py +++ b/pywb/rewrite/templateview.py @@ -109,10 +109,10 @@ class JinjaEnv(object): # ============================================================================ class BaseInsertView(object): - def __init__(self, jenv, insert_file, banner_file=''): + def __init__(self, jenv, insert_file, banner_view=None): self.jenv = jenv self.insert_file = insert_file - self.banner_file = banner_file + self.banner_view = banner_view def render_to_string(self, env, **kwargs): template = None @@ -134,6 +134,7 @@ class BaseInsertView(object): params = env.get('webrec.template_params') if params: kwargs.update(params) + kwargs['env'] = env return template.render(**kwargs) @@ -149,27 +150,25 @@ class HeadInsertView(BaseInsertView): coll='', include_ts=True): - url = wb_url.get_url() - - include_wombat = not wb_url.is_banner_only - - wbrequest = {'host_prefix': host_prefix, - 'wb_prefix': wb_prefix, - 'wb_url': wb_url, - 'coll': coll, - 'env': env, - 'options': {'is_framed': is_framed}, - 'rewrite_opts': {} - } + params = {'host_prefix': host_prefix, + 'wb_prefix': wb_prefix, + 'wb_url': wb_url, + 'coll': coll, + 'is_framed': 'true' if is_framed else 'false', + 'top_url': top_url, + } def make_head_insert(rule, cdx): - return (self.render_to_string(env, wbrequest=wbrequest, - cdx=cdx, - top_url=top_url, - include_ts=include_ts, - include_wombat=include_wombat, - banner_html=self.banner_file, - rule=rule)) + params['wombat_ts'] = cdx['timestamp'] if include_ts else '' + params['wombat_sec'] = timestamp_to_sec(cdx['timestamp']) + params['is_live'] = 'true' if cdx.get('is_live') else 'false' + + if self.banner_view: + banner_html = self.banner_view.render_to_string(env, cdx=cdx, **params) + params['banner_html'] = banner_html + + return self.render_to_string(env, cdx=cdx, **params) + return make_head_insert @@ -198,25 +197,27 @@ class TopFrameView(BaseInsertView): else: iframe_url = wb_prefix + embed_url - wbrequest = {'host_prefix': host_prefix, - 'wb_prefix': wb_prefix, - 'wb_url': wb_url, - 'coll': coll, + params = {'host_prefix': host_prefix, + 'wb_prefix': wb_prefix, + 'wb_url': wb_url, + 'coll': coll, - 'options': {'frame_mod': frame_mod, - 'replay_mod': replay_mod}, - } + 'options': {'frame_mod': frame_mod, + 'replay_mod': replay_mod}, - params = dict(embed_url=embed_url, - iframe_url=iframe_url, - wbrequest=wbrequest, - timestamp=timestamp, - url=wb_url.get_url(), - banner_html=self.banner_file) + 'embed_url': embed_url, + 'iframe_url': iframe_url, + 'timestamp': timestamp, + 'url': wb_url.get_url() + } if extra_params: params.update(extra_params) + if self.banner_view: + banner_html = self.banner_view.render_to_string(env, **params) + params['banner_html'] = banner_html + return self.render_to_string(env, **params) diff --git a/pywb/static/wb.css b/pywb/static/default_banner.css similarity index 98% rename from pywb/static/wb.css rename to pywb/static/default_banner.css index b285cfa6..0806cd91 100644 --- a/pywb/static/wb.css +++ b/pywb/static/default_banner.css @@ -28,7 +28,7 @@ height: 40px !important; } -.wb_iframe_div +#wb_iframe_div { position: absolute; width: 100%; diff --git a/pywb/static/default_banner.js b/pywb/static/default_banner.js index 3f24942f..5b7d189e 100644 --- a/pywb/static/default_banner.js +++ b/pywb/static/default_banner.js @@ -19,43 +19,85 @@ This file is part of pywb, https://github.com/ikreymer/pywb */ // Creates the default pywb banner. -// Override this function/script to create a different type of banner +(function() { + function ts_to_date(ts, is_gmt) { + if (!ts) { + return ""; + } -_wb_js.create_banner_element = function(banner_id) -{ + if (ts.length < 14) { + ts += "00000000000000".substr(ts.length); + } - var banner = document.createElement("wb_div", true); - banner.setAttribute("id", banner_id); - banner.setAttribute("lang", "en"); + var datestr = (ts.substring(0, 4) + "-" + + ts.substring(4, 6) + "-" + + ts.substring(6, 8) + "T" + + ts.substring(8, 10) + ":" + + ts.substring(10, 12) + ":" + + ts.substring(12, 14) + "-00:00"); - var text; + var date = new Date(datestr); - if (wbinfo.is_frame) { - text = _wb_js.banner_labels.LOADING_MSG; - } else if (wbinfo.is_live) { - text = _wb_js.banner_labels.LIVE_MSG; - } else { - text = _wb_js.banner_labels.REPLAY_MSG; - } - - text = "" + text + ""; - - var capture_str = ""; - if (wbinfo && wbinfo.timestamp) { - capture_str = _wb_js.ts_to_date(wbinfo.timestamp, true); + if (is_gmt) { + return date.toGMTString(); + } else { + return date.toLocaleString(); + } } - text += "" + capture_str + ""; + function init(bid) { + var banner = document.createElement("wb_div", true); - if (wbinfo.proxy_magic && wbinfo.url) { - var select_url = wbinfo.proxy_magic + "/" + wbinfo.url; - var query_url = wbinfo.proxy_magic + "/*/" + wbinfo.url; - text += ' All Capture Times'; - text += '
' - text += 'From collection "' + wbinfo.coll + '" All Collections'; + banner.setAttribute("id", bid); + banner.setAttribute("lang", "en"); + + var text = "Loading..."; + + banner.innerHTML = text; + document.body.insertBefore(banner, document.body.firstChild); } - - banner.innerHTML = text; - document.body.insertBefore(banner, document.body.firstChild); -} + + function set_banner(url, ts, is_live) { + var capture_str; + + if (!ts) { + return; + } + + if (is_live) { + capture_str = "This is a live page from "; + } else { + capture_str = "This is an archived page from "; + } + + capture_str += ts_to_date(ts, true); + document.querySelector("#_wb_capture_info").innerHTML = capture_str; + } + + if (window.top != window) { + return; + } + + window.addEventListener("load", function() { + if (window.wbinfo) { + init("_wb_plain_banner"); + + set_banner(window.wbinfo.url, + window.wbinfo.timestamp, + window.wbinfo.is_live); + } else { + init("_wb_frame_top_banner"); + + window.addEventListener("message", function(event) { + var state = event.data; + if (state.wb_type) { + set_banner(state.url, state.ts, state.is_live); + } + }); + } + }); + +})(); + + diff --git a/pywb/static/vidrw.js b/pywb/static/vidrw.js index a17d0e32..0366bd1d 100644 --- a/pywb/static/vidrw.js +++ b/pywb/static/vidrw.js @@ -34,7 +34,7 @@ if (window.location.hash) { if (_pywbvid == "html" || _pywbvid == "flash") { var YT_W_E_RX = /^(https?:\/\/.*youtube.com)\/(watch|embed).*$/; - if (wbinfo.url.match(YT_W_E_RX)) { + if (window.wbinfo && window.wbinfo.url.match(YT_W_E_RX)) { // special case: prevent yt player from being inited Object.defineProperty(window, 'yt', {writeable: false}); Object.defineProperty(window, 'ytplayer', {writeable: false}); @@ -45,6 +45,10 @@ if (window.location.hash) { __wbvidrw = (function() { + if (!window.wbinfo) { + return; + } + var checked_embeds = false; var FLASH_PLAYER = wbinfo.static_prefix + "/flowplayer/flowplayer-3.2.18.swf"; diff --git a/pywb/static/wb.js b/pywb/static/wb.js deleted file mode 100644 index ecaeee91..00000000 --- a/pywb/static/wb.js +++ /dev/null @@ -1,176 +0,0 @@ -/* -Copyright(c) 2013-2014 Ilya Kreymer. Released under the GNU General Public License. - -This file is part of pywb, https://github.com/ikreymer/pywb - - pywb is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - pywb is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with pywb. If not, see . - */ - -function __WbJsInit() { - -var bid = undefined; - - -function init_banner() { - var PLAIN_BANNER_ID = "_wb_plain_banner"; - var FRAME_BANNER_ID = "_wb_frame_top_banner"; - - if (!window.wbinfo || (window.__WB_replay_top && window != window.__WB_replay_top)) { - return; - } - - if (!bid) { - if (wbinfo.is_frame) { - bid = FRAME_BANNER_ID; - } else { - bid = PLAIN_BANNER_ID; - } - } - - if (!document || !document.body) { - return; - } - - if (document.getElementById(bid) != null) { - return; - } - - var res = _wb_js.create_banner_element(bid); - if (res) { - bid = res; - } -} - -this.banner_labels = {LOADING_MSG: "Loading...", - REPLAY_MSG: "This is an archived page from ", - LIVE_MSG: "This is a live page loaded on "}; - -this.create_banner_element = function() { - // No banner by default - return null; -} - -this.ts_to_date = function(ts, is_gmt) -{ - if (!ts) { - return ""; - } - - if (ts.length < 14) { - ts += "00000000000000".substr(ts.length); - } - - var datestr = (ts.substring(0, 4) + "-" + - ts.substring(4, 6) + "-" + - ts.substring(6, 8) + "T" + - ts.substring(8, 10) + ":" + - ts.substring(10, 12) + ":" + - ts.substring(12, 14) + "-00:00"); - - var date = new Date(datestr); - if (is_gmt) { - return date.toGMTString(); - } else { - return date.toLocaleString(); - } -} - -function add_event(name, func, object) { - if (object.addEventListener) { - object.addEventListener(name, func); - return true; - } else if (object.attachEvent) { - object.attachEvent("on" + name, func); - return true; - } else { - return false; - } -} - -function remove_event(name, func, object) { - if (object.removeEventListener) { - object.removeEventListener(name, func); - return true; - } else if (object.detachEvent) { - object.detachEvent("on" + name, func); - return true; - } else { - return false; - } -} - -function notify_top(event) { - if (!window.__WB_top_frame) { - return; - } - - if (!window.WB_wombat_location) { - return; - } - - if (typeof(window.WB_wombat_location.href) != "string") { - return; - } - - var message = { - "url": window.WB_wombat_location.href, - "ts": wbinfo.timestamp, - "request_ts": wbinfo.request_ts, - "is_live": wbinfo.is_live, - "title": document ? document.title : "", - "readyState": document.readyState, - "wb_type": "load", - } - - window.__WB_top_frame.postMessage(message, "*"); - - //remove_event("readystatechange", notify_top, document); -} - -this.load = function() { - if (window._wb_js_inited) { - return; - } - - window._wb_js_inited = true; - - // Non-Framed Replay OR top frame for framed replay! - if (window.wbinfo && !window.__WB_top_frame) { - if (wbinfo.is_framed && wbinfo.mod != "bn_") { - var hash = window.location.hash; - - var loc = window.location.href.replace(window.location.hash, ""); - loc = decodeURI(loc); - - var top_url = wbinfo.top_url; - - if (wbinfo.top_url && (loc != decodeURI(wbinfo.top_url))) { - // Auto-redirect to top frame - window.location.replace(wbinfo.top_url + hash); - return; - } - } - // Init Banner (no frame or top frame) - add_event("readystatechange", init_banner, document); - - // Framed Replay - } else if (window.__WB_top_frame) { - add_event("readystatechange", notify_top, document); - } -} - -}; - - -_wb_js = new __WbJsInit(); diff --git a/pywb/static/wb_frame.js b/pywb/static/wb_frame.js index 23616d6f..fa7b9808 100644 --- a/pywb/static/wb_frame.js +++ b/pywb/static/wb_frame.js @@ -17,100 +17,80 @@ This file is part of pywb, https://github.com/ikreymer/pywb along with pywb. If not, see . */ -var LIVE_COOKIE_REGEX = /pywb.timestamp=([\d]{1,14})/; -var TS_REGEX = /\/([\d]{1,14})(?:\w+_)?\/(?:\w+[:])?\/\//; +function ContentFrame(content_info) { + this.last_inner_hash = window.location.hash; + this.last_url = content_info.url; + this.last_ts = content_info.request_ts; -//var curr_state = {}; - -var IFRAME_ID = "replay_iframe"; - -var last_inner_hash = undefined; - -function make_url(url, ts, mod, prefix) -{ - if (ts || mod) { - mod += "/"; - } - - prefix = prefix || wbinfo.prefix; - - if (ts) { - return prefix + ts + mod + url; - } else { - return prefix + mod + url; - } -} - -function pop_state(state) { - set_state(state); -} - -function extract_ts(url) -{ - var result = url.match(TS_REGEX); - if (!result) { - return ""; - } - - return result[1]; -} - -function extract_replay_url(url) { - var inx = url.indexOf("/http:"); - if (inx < 0) { - inx = url.indexOf("/https:"); - if (inx < 0) { - return ""; - } - } - return url.substring(inx + 1); -} - -function set_state(state) { - var capture_info = document.getElementById("_wb_capture_info"); - if (capture_info) { - capture_info.innerHTML = state.capture_str; - } - - var label = document.getElementById("_wb_label"); - if (label && window._wb_js) { - if (state.is_live) { - label.innerHTML = _wb_js.banner_labels.LIVE_MSG; + this.init_iframe = function() { + if (typeof(content_info.iframe) === "string") { + this.iframe = document.querySelector(content_info.iframe); } else { - label.innerHTML = _wb_js.banner_labels.REPLAY_MSG; + this.iframe = content_info.iframe; + } + + if (!this.iframe) { + console.warn("no iframe found " + content_info.iframe + " found"); + return; + } + + this.extract_prefix(); + + this.load_url(content_info.url, content_info.request_ts); + } + + this.extract_prefix = function() { + this.app_prefix = content_info.app_prefix || content_info.prefix; + this.content_prefix = content_info.content_prefix || content_info.prefix; + + if (this.app_prefix && this.content_prefix) { + return; + } + + var inx = window.location.href.indexOf(content_info.url); + + if (inx < 0) { + inx = window.location.href.indexOf("/http") + 1; + if (inx <= 0) { + inx = window.location.href.indexOf("///") + 1; + if (inx <= 0) { + console.warn("No Prefix Found!"); + } + } + } + + this.prefix = window.location.href.substr(0, inx); + + this.app_prefix = this.app_prefix || this.prefix; + this.content_prefix = this.content_prefix || this.prefix; + } + + + this.make_url = function(url, ts, content_url) { + var mod, prefix; + + if (content_url) { + mod = "mp_"; + prefix = this.content_prefix; + } else { + mod = ""; + prefix = this.app_prefix; + } + + if (ts || mod) { + mod += "/"; + } + + if (ts) { + return prefix + ts + mod + url; + } else { + return prefix + mod + url; } } - //curr_state = state; -} - -window.onpopstate = function(event) { - var state = event.state; - - if (state) { - pop_state(state); - } -} - -function extract_ts_cookie(value) { - var result = value.match(LIVE_COOKIE_REGEX); - if (result) { - return result[1]; - } else { - return ""; - } -} - - -function init_pm(frame) { - if (!frame) { - return; - } - - var frame_win = frame.contentWindow; - - window.addEventListener("message", function(event) { + this.handle_event = function(event) { + var frame_win = this.iframe.contentWindow; if (event.source == window.parent) { // Pass to replay frame frame_win.postMessage(event.data, "*"); @@ -118,101 +98,79 @@ function init_pm(frame) { // Check if iframe url change message if (typeof(event.data) == "object" && event.data["wb_type"]) { - handle_message(event.data); + this.handle_message(event.data); } else { // Pass to parent window.parent.postMessage(event.data, "*"); } } - }); + } + + this.handle_message = function(state) { + var type = state.wb_type; + + if (type == "load" || type == "replace-url") { + this.set_url(state); + } else if (type == "hashchange") { + this.inner_hash_changed(state); + } + } + + this.set_url = function(state) { + if (state.url && (state.url != this.last_url || state.request_ts != this.last_ts)) { + var new_url = this.make_url(state.url, state.request_ts, false); + + window.history.replaceState(state, "", new_url); + + this.last_url = state.url; + this.last_ts = state.request_ts; + } + } + + this.load_url = function(newUrl, newTs) { + this.iframe.src = this.make_url(newUrl, newTs, true); + } + + this.inner_hash_changed = function(state) { + if (window.location.hash != state.hash) { + window.location.hash = state.hash; + } + this.last_inner_hash = state.hash; + } + + this.outer_hash_changed = function(event) { + if (window.location.hash == this.last_inner_hash) { + return; + } + + if (this.iframe) { + var message = {"wb_type": "outer_hashchange", "hash": window.location.hash} + + this.iframe.contentWindow.postMessage(message, "*", undefined, true); + } + } + + this.close = function() { + window.removeEventListener("hashchange", this.outer_hash_changed); + window.removeEventListener("message", this.handle_event); + } + + // bind event callbacks + this.outer_hash_changed = this.outer_hash_changed.bind(this); + this.handle_event = this.handle_event.bind(this); + + window.addEventListener("hashchange", this.outer_hash_changed, false); + window.addEventListener("message", this.handle_event); + + if (document.readyState === "complete") { + this.init_iframe(); + } else { + document.addEventListener("DOMContentLoaded", this.init_iframe.bind(this), { once: true }); + } window.__WB_pmw = function(win) { this.pm_source = win; return this; } } - - -function handle_message(state) { - var type = state.wb_type; - - if (type == "load" || type == "replace-url") { - update_wb_url(state); - } else if (type == "hashchange") { - inner_hash_changed(state); - } -} - - -function update_wb_url(state) { - if (window._wb_js) { - state['capture_str'] = _wb_js.ts_to_date(state.ts, true); - } - - if (!state.url) { - return; - } - - var canon_url = make_url(state.url, state.request_ts, "", wbinfo.outer_prefix); - - window.history.replaceState(state, "", canon_url); - - set_state(state); -} - -function inner_hash_changed(state) { - if (window.location.hash != state.hash) { - window.location.hash = state.hash; - } - last_inner_hash = state.hash; -} - -function outer_hash_changed(event) { - if (window.location.hash == last_inner_hash) { - return; - } - - var frame = document.getElementById(IFRAME_ID); - - if (frame) { - var message = {"wb_type": "outer_hashchange", "hash": window.location.hash} - - frame.contentWindow.postMessage(message, "*", undefined, true); - } -} - -function init_hash_connect() { - var frame = document.getElementById(IFRAME_ID); - - if (!frame) { - return; - } - - if (window.location.hash) { - var curr_url = wbinfo.capture_url + window.location.hash; - - frame.src = make_url(curr_url, wbinfo.request_ts, wbinfo.replay_mod); - - last_inner_hash = window.location.hash; - //frame.location.href = make_url(curr_url, wbinfo.request_ts, wbinfo.replay_mod); - //frame.location.hash = window.location.hash; - } - - if ("onhashchange" in window) { - window.addEventListener("hashchange", outer_hash_changed, false); - } - - // Init Post Message connect - init_pm(frame); -} - -document.addEventListener("DOMContentLoaded", init_hash_connect); - -// Load Banner -if (window._wb_js) { - _wb_js.load(); -} - - - diff --git a/pywb/static/wombat.js b/pywb/static/wombat.js index 97309c82..7391ac0f 100644 --- a/pywb/static/wombat.js +++ b/pywb/static/wombat.js @@ -3077,9 +3077,59 @@ var _WBWombat = function($wbwindow, wbinfo) { return res; } + if (wbinfo.is_framed && wbinfo.mod != "bn_") { + init_frame_notify(); + } + return obj; } + function init_frame_notify() { + function notify_top(event) { + if (!window.__WB_top_frame) { + var hash = window.location.hash; + + //var loc = window.location.href.replace(window.location.hash, ""); + //loc = decodeURI(loc); + + //if (wbinfo.top_url && (loc != decodeURI(wbinfo.top_url))) { + // Auto-redirect to top frame + window.location.replace(wbinfo.top_url + hash); + //} + + return; + } + + if (!window.WB_wombat_location) { + return; + } + + if (typeof(window.WB_wombat_location.href) != "string") { + return; + } + + var message = { + "url": window.WB_wombat_location.href, + "ts": wbinfo.timestamp, + "request_ts": wbinfo.request_ts, + "is_live": wbinfo.is_live, + "title": document ? document.title : "", + "readyState": document.readyState, + "wb_type": "load" + } + + window.__WB_top_frame.postMessage(message, wbinfo.top_host); + } + + if (document.readyState == "complete") { + notify_top(); + } else if (window.addEventListener) { + document.addEventListener("readystatechange", notify_top); + } else if (window.attachEvent) { + document.attachEvent("onreadystatechange", notify_top); + } + } + function init_top_frame($wbwindow) { // proxy mode if (wb_is_proxy) { @@ -3099,9 +3149,8 @@ var _WBWombat = function($wbwindow, wbinfo) { return (win._wb_wombat != undefined); } else { // otherwise, ensure that it is not a top container frame - return !win.wbinfo.is_frame; + return win.wbinfo.is_framed } - } catch (e) { return false; } diff --git a/pywb/templates/banner.html b/pywb/templates/banner.html index f987041e..f1a300cc 100644 --- a/pywb/templates/banner.html +++ b/pywb/templates/banner.html @@ -1,4 +1,4 @@ - - + + diff --git a/pywb/templates/frame_insert.html b/pywb/templates/frame_insert.html index 84e84f31..b8fbe1bb 100644 --- a/pywb/templates/frame_insert.html +++ b/pywb/templates/frame_insert.html @@ -12,26 +12,21 @@ html, body } - - - - + -{% include banner_html ignore missing %} +{{ banner_html }} - -
- +
+
+ diff --git a/pywb/templates/head_insert.html b/pywb/templates/head_insert.html index 56cebc8b..d32ff310 100644 --- a/pywb/templates/head_insert.html +++ b/pywb/templates/head_insert.html @@ -1,55 +1,40 @@ + + wbinfo.is_framed = {{ is_framed }}; + wbinfo.is_live = {{ is_live }}; + wbinfo.coll = "{{ coll }}"; + wbinfo.proxy_magic = "{{ env.pywb_proxy_magic }}"; + wbinfo.static_prefix = "{{ host_prefix }}/{{ static_path }}"; -{% if rule.js_rewrite_location != 'urls' and include_wombat %} - - -{% endif %} - - -{% include banner_html ignore missing %} - - - - -{# - -{% if wbrequest.options.is_framed %} - -{% endif %} -#} +{{ banner_html }} diff --git a/tests/test_auto_colls.py b/tests/test_auto_colls.py index ef138fe5..7299c121 100644 --- a/tests/test_auto_colls.py +++ b/tests/test_auto_colls.py @@ -1,4 +1,4 @@ -from .base_config_test import CollsDirMixin +from .base_config_test import BaseConfigTest, CollsDirMixin, fmod import os import tempfile @@ -38,7 +38,11 @@ AUTOINDEX_FILE = 'autoindex.cdxj' #============================================================================= -class TestManagedColls(CollsDirMixin, BaseTestClass): +class TestManagedColls(CollsDirMixin, BaseConfigTest): + @classmethod + def setup_class(cls): + super(TestManagedColls, cls).setup_class('config_test.yaml') + def _check_dirs(self, base, dirlist): for dir_ in dirlist: assert os.path.isdir(os.path.join(base, dir_)) @@ -46,11 +50,6 @@ class TestManagedColls(CollsDirMixin, BaseTestClass): def _get_sample_warc(self, name): return os.path.join(get_test_dir(), 'warcs', name) - def _create_app(self): - config_file = 'config_test.yaml' - config_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), config_file) - self.testapp = webtest.TestApp(FrontEndApp(config_file=config_file)) - @patch('pywb.apps.cli.BaseCli.run_gevent', lambda *args, **kwargs: None) def test_run_cli(self): """ test new wayback cli interface @@ -89,8 +88,8 @@ class TestManagedColls(CollsDirMixin, BaseTestClass): main(['add', 'test', warc1]) - self._create_app() - resp = self.testapp.get('/test/20140103030321/http://example.com?example=1') + def test_add_warcs_replay(self, fmod): + resp = self.get('/test/20140103030321{0}/http://example.com/?example=1', fmod) assert resp.status_int == 200 def test_another_coll(self): @@ -102,8 +101,8 @@ class TestManagedColls(CollsDirMixin, BaseTestClass): main(['add', 'foo', warc1]) - self._create_app() - resp = self.testapp.get('/foo/20140103030321/http://example.com?example=1') + def test_another_coll_replay(self, fmod): + resp = self.get('/foo/20140103030321{0}/http://example.com/?example=1', fmod) assert resp.status_int == 200 def test_add_more_warcs(self): @@ -121,9 +120,9 @@ class TestManagedColls(CollsDirMixin, BaseTestClass): with raises(IOError): main(['add', 'test', 'non-existent-file.warc.gz']) + def test_add_more_warcs_replay(self, fmod): # check new cdx - self._create_app() - resp = self.testapp.get('/test/20140126200624/http://www.iana.org/') + resp = self.get('/test/20140126200624{0}/http://www.iana.org/', fmod) assert resp.status_int == 200 def test_add_custom_nested_warcs(self): @@ -165,11 +164,11 @@ class TestManagedColls(CollsDirMixin, BaseTestClass): assert '334' in nested_cdx_index assert 'A/iana.warc.gz' in nested_cdx_index - self._create_app() - resp = self.testapp.get('/nested/20140126200624/http://www.iana.org/') + def test_nested_replay(self, fmod): + resp = self.get('/nested/20140126200624{0}/http://www.iana.org/', fmod) assert resp.status_int == 200 - resp = self.testapp.get('/nested/20140103030321/http://example.com?example=1') + resp = self.get('/nested/20140103030321{0}/http://example.com/?example=1', fmod) assert resp.status_int == 200 def test_merge_vs_reindex_equality(self): @@ -202,7 +201,6 @@ class TestManagedColls(CollsDirMixin, BaseTestClass): with open(a_static, 'w+b') as fh: fh.write(b'/* Some JS File */') - self._create_app() resp = self.testapp.get('/static/_/test/abc.js') assert resp.status_int == 200 assert resp.content_type == 'application/javascript' @@ -217,7 +215,6 @@ class TestManagedColls(CollsDirMixin, BaseTestClass): with open(a_static, 'w+b') as fh: fh.write(b'/* Some CSS File */') - self._create_app() resp = self.testapp.get('/static/foo.css') assert resp.status_int == 200 assert resp.content_type == 'text/css' @@ -230,7 +227,6 @@ class TestManagedColls(CollsDirMixin, BaseTestClass): """ main(['metadata', 'foo', '--set', 'title=Collection Title']) - self._create_app() resp = self.testapp.get('/') assert resp.status_int == 200 assert resp.content_type == 'text/html' @@ -242,7 +238,6 @@ class TestManagedColls(CollsDirMixin, BaseTestClass): resp.charset = 'utf-8' assert '(Collection Title)' in resp.text - def test_other_metadata_search_page(self): main(['metadata', 'foo', '--set', 'desc=Some Description Text', @@ -251,7 +246,6 @@ class TestManagedColls(CollsDirMixin, BaseTestClass): with raises(ValueError): main(['metadata', 'foo', '--set', 'name_only']) - self._create_app() resp = self.testapp.get('/foo/') resp.charset = 'utf-8' assert resp.status_int == 200 @@ -268,18 +262,32 @@ class TestManagedColls(CollsDirMixin, BaseTestClass): def test_custom_template_search(self): """ Test manually added custom search template search.html """ - a_static = os.path.join(self.root_dir, COLLECTIONS, 'test', 'templates', 'search.html') + custom_search = os.path.join(self.root_dir, COLLECTIONS, 'test', + 'templates', 'search.html') - with open(a_static, 'w+b') as fh: + with open(custom_search, 'w+b') as fh: fh.write(b'pywb custom search page') - self._create_app() resp = self.testapp.get('/test/') resp.charset = 'utf-8' assert resp.status_int == 200 assert resp.content_type == 'text/html' assert 'pywb custom search page' in resp.text + def test_add_custom_banner(self): + """ Test adding custom banner.html per-collection template + """ + + banner_file = os.path.join(self.root_dir, COLLECTIONS, 'test', + 'templates', 'banner.html') + + with open(banner_file, 'w+b') as fh: + fh.write(b'
Custom Banner Here!
') + + def test_add_custom_banner_replay(self, fmod): + resp = self.get('/test/20140103030321/http://example.com/?example=1', fmod) + assert '
Custom Banner Here!
' in resp.text + def test_more_custom_templates(self): """ Test custom templates and metadata @@ -287,7 +295,7 @@ class TestManagedColls(CollsDirMixin, BaseTestClass): Add custom metadata and test its presence in custom search page """ custom_search = os.path.join(self.root_dir, COLLECTIONS, 'test', - 'templates', 'search.html') + 'templates', 'search.html') # add metadata main(['metadata', 'test', '--set', 'some=value']) @@ -296,7 +304,9 @@ class TestManagedColls(CollsDirMixin, BaseTestClass): fh.write(b'overriden search page: ') fh.write(b'{{ metadata | tojson }}\n') - self._create_app() + # force clear of jinja env cache to reload + self.app.rewriterapp.jinja_env.jinja_env.cache = {} + resp = self.testapp.get('/test/') resp.charset = 'utf-8' assert resp.status_int == 200 @@ -304,7 +314,8 @@ class TestManagedColls(CollsDirMixin, BaseTestClass): assert 'overriden search page: ' in resp.text assert '"some": "value"' in resp.text - resp = self.testapp.get('/test/20140103030321/http://example.com?example=1') + def test_more_custom_templates_replay(self, fmod): + resp = self.get('/test/20140103030321{0}/http://example.com/?example=1', fmod) assert resp.status_int == 200 def test_add_default_coll_templates(self): @@ -334,7 +345,6 @@ class TestManagedColls(CollsDirMixin, BaseTestClass): fh.seek(0) fh.write(buf) - self._create_app() resp = self.testapp.get('/') resp.charset = 'utf-8' assert resp.content_type == 'text/html' @@ -379,8 +389,6 @@ class TestManagedColls(CollsDirMixin, BaseTestClass): """ shutil.rmtree(os.path.join(self.root_dir, COLLECTIONS, 'foo', 'templates')) - self._create_app() - resp = self.testapp.get('/foo/') resp.charset = 'utf-8' assert resp.status_int == 200 @@ -559,7 +567,6 @@ class TestManagedColls(CollsDirMixin, BaseTestClass): # No Statics -- ignorable shutil.rmtree(os.path.join(colls, 'foo', 'static')) - self._create_app() # No WARCS warcs_path = os.path.join(colls, 'foo', ARCHIVE_DIR) @@ -572,16 +579,10 @@ class TestManagedColls(CollsDirMixin, BaseTestClass): cdx_path = os.path.join(colls, 'foo', INDEX_DIR) shutil.rmtree(cdx_path) - #with raises(Exception): - # self._create_app() - # CDX a file not a dir with open(cdx_path, 'w+b') as fh: fh.write(b'foo\n') - #with raises(Exception): - # self._create_app() - shutil.rmtree(colls) # No Collections to list @@ -589,7 +590,6 @@ class TestManagedColls(CollsDirMixin, BaseTestClass): main(['list']) # No Collections - self._create_app() resp = self.testapp.get('/test/', status=404) assert resp.status_int == 404 diff --git a/tests/test_integration.py b/tests/test_integration.py index faec4196..5d30519b 100644 --- a/tests/test_integration.py +++ b/tests/test_integration.py @@ -74,15 +74,16 @@ class TestWbIntegration(BaseConfigTest): def test_replay_top_frame(self): resp = self.testapp.get('/pywb/20140127171238/http://www.iana.org/') - assert '