mirror of
git://source.winehq.org/git/wine.git
synced 2024-09-01 14:47:44 +00:00
mshtml: Send StorageEvents to iframe windows properly.
Signed-off-by: Gabriel Ivăncescu <gabrielopcode@gmail.com>
This commit is contained in:
parent
e64ab65a5e
commit
2297623f01
|
@ -197,6 +197,8 @@ static inline HTMLStorage *impl_from_IHTMLStorage(IHTMLStorage *iface)
|
||||||
return CONTAINING_RECORD(iface, HTMLStorage, IHTMLStorage_iface);
|
return CONTAINING_RECORD(iface, HTMLStorage, IHTMLStorage_iface);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static HRESULT build_session_origin(IUri*,BSTR,BSTR*);
|
||||||
|
|
||||||
struct storage_event_task {
|
struct storage_event_task {
|
||||||
task_t header;
|
task_t header;
|
||||||
HTMLInnerWindow *window;
|
HTMLInnerWindow *window;
|
||||||
|
@ -226,22 +228,22 @@ static void storage_event_destr(task_t *_task)
|
||||||
IHTMLWindow2_Release(&task->window->base.IHTMLWindow2_iface);
|
IHTMLWindow2_Release(&task->window->base.IHTMLWindow2_iface);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Takes ownership of old_value */
|
struct send_storage_event_ctx {
|
||||||
static HRESULT send_storage_event(HTMLStorage *storage, BSTR key, BSTR old_value, BSTR new_value)
|
HTMLInnerWindow *skip_window;
|
||||||
|
const WCHAR *origin;
|
||||||
|
UINT origin_len;
|
||||||
|
BSTR key;
|
||||||
|
BSTR old_value;
|
||||||
|
BSTR new_value;
|
||||||
|
};
|
||||||
|
|
||||||
|
static HRESULT push_storage_event_task(struct send_storage_event_ctx *ctx, HTMLInnerWindow *window, BOOL commit)
|
||||||
{
|
{
|
||||||
HTMLInnerWindow *window = storage->window;
|
|
||||||
BOOL local = !!storage->filename;
|
|
||||||
struct storage_event_task *task;
|
struct storage_event_task *task;
|
||||||
DOMEvent *event;
|
DOMEvent *event;
|
||||||
HRESULT hres;
|
HRESULT hres;
|
||||||
|
|
||||||
if(!window) {
|
hres = create_storage_event(window->doc, ctx->key, ctx->old_value, ctx->new_value, commit, &event);
|
||||||
SysFreeString(old_value);
|
|
||||||
return S_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
hres = create_storage_event(window->doc, key, old_value, new_value, local, &event);
|
|
||||||
SysFreeString(old_value);
|
|
||||||
if(FAILED(hres))
|
if(FAILED(hres))
|
||||||
return hres;
|
return hres;
|
||||||
|
|
||||||
|
@ -256,6 +258,102 @@ static HRESULT send_storage_event(HTMLStorage *storage, BSTR key, BSTR old_value
|
||||||
return push_task(&task->header, storage_event_proc, storage_event_destr, window->task_magic);
|
return push_task(&task->header, storage_event_proc, storage_event_destr, window->task_magic);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static HRESULT send_storage_event_impl(struct send_storage_event_ctx *ctx, HTMLInnerWindow *window)
|
||||||
|
{
|
||||||
|
HTMLOuterWindow *child;
|
||||||
|
const WCHAR *origin;
|
||||||
|
UINT origin_len;
|
||||||
|
BOOL matches;
|
||||||
|
HRESULT hres;
|
||||||
|
BSTR bstr;
|
||||||
|
|
||||||
|
if(!window)
|
||||||
|
return S_OK;
|
||||||
|
|
||||||
|
LIST_FOR_EACH_ENTRY(child, &window->children, HTMLOuterWindow, sibling_entry) {
|
||||||
|
hres = send_storage_event_impl(ctx, child->base.inner_window);
|
||||||
|
if(FAILED(hres))
|
||||||
|
return hres;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(window == ctx->skip_window)
|
||||||
|
return S_OK;
|
||||||
|
|
||||||
|
/* Try it quick from session storage first, if available */
|
||||||
|
if(window->session_storage) {
|
||||||
|
HTMLStorage *storage = impl_from_IHTMLStorage(window->session_storage);
|
||||||
|
origin = storage->session_storage->origin;
|
||||||
|
origin_len = ctx->skip_window ? wcslen(origin) : storage->session_storage->origin_len;
|
||||||
|
bstr = NULL;
|
||||||
|
}else {
|
||||||
|
hres = IUri_GetHost(window->base.outer_window->uri, &bstr);
|
||||||
|
if(hres != S_OK) {
|
||||||
|
if(SUCCEEDED(hres))
|
||||||
|
SysFreeString(bstr);
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
if(ctx->skip_window)
|
||||||
|
_wcslwr(bstr);
|
||||||
|
else {
|
||||||
|
BSTR tmp = bstr;
|
||||||
|
hres = build_session_origin(window->base.outer_window->uri, tmp, &bstr);
|
||||||
|
SysFreeString(tmp);
|
||||||
|
if(hres != S_OK) {
|
||||||
|
if(SUCCEEDED(hres))
|
||||||
|
SysFreeString(bstr);
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
origin = bstr;
|
||||||
|
origin_len = SysStringLen(bstr);
|
||||||
|
}
|
||||||
|
|
||||||
|
matches = (origin_len == ctx->origin_len && !memcmp(origin, ctx->origin, origin_len * sizeof(WCHAR)));
|
||||||
|
SysFreeString(bstr);
|
||||||
|
|
||||||
|
return matches ? push_storage_event_task(ctx, window, FALSE) : S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Takes ownership of old_value */
|
||||||
|
static HRESULT send_storage_event(HTMLStorage *storage, BSTR key, BSTR old_value, BSTR new_value)
|
||||||
|
{
|
||||||
|
HTMLInnerWindow *window = storage->window;
|
||||||
|
struct send_storage_event_ctx ctx;
|
||||||
|
HTMLOuterWindow *top_window;
|
||||||
|
BSTR hostname = NULL;
|
||||||
|
HRESULT hres = S_OK;
|
||||||
|
|
||||||
|
if(!window)
|
||||||
|
goto done;
|
||||||
|
get_top_window(window->base.outer_window, &top_window);
|
||||||
|
|
||||||
|
ctx.key = key;
|
||||||
|
ctx.old_value = old_value;
|
||||||
|
ctx.new_value = new_value;
|
||||||
|
if(!storage->filename) {
|
||||||
|
ctx.origin = storage->session_storage->origin;
|
||||||
|
ctx.origin_len = storage->session_storage->origin_len;
|
||||||
|
ctx.skip_window = NULL;
|
||||||
|
}else {
|
||||||
|
hres = IUri_GetHost(window->base.outer_window->uri, &hostname);
|
||||||
|
if(hres != S_OK)
|
||||||
|
goto done;
|
||||||
|
_wcslwr(hostname);
|
||||||
|
ctx.origin = hostname;
|
||||||
|
ctx.origin_len = SysStringLen(hostname);
|
||||||
|
ctx.skip_window = top_window->base.inner_window; /* localStorage on native skips top window */
|
||||||
|
}
|
||||||
|
hres = send_storage_event_impl(&ctx, top_window->base.inner_window);
|
||||||
|
|
||||||
|
if(ctx.skip_window && hres == S_OK)
|
||||||
|
hres = push_storage_event_task(&ctx, window, TRUE);
|
||||||
|
|
||||||
|
done:
|
||||||
|
SysFreeString(hostname);
|
||||||
|
SysFreeString(old_value);
|
||||||
|
return hres;
|
||||||
|
}
|
||||||
|
|
||||||
static HRESULT WINAPI HTMLStorage_QueryInterface(IHTMLStorage *iface, REFIID riid, void **ppv)
|
static HRESULT WINAPI HTMLStorage_QueryInterface(IHTMLStorage *iface, REFIID riid, void **ppv)
|
||||||
{
|
{
|
||||||
HTMLStorage *This = impl_from_IHTMLStorage(iface);
|
HTMLStorage *This = impl_from_IHTMLStorage(iface);
|
||||||
|
|
|
@ -1253,6 +1253,130 @@ sync_test("storage", function() {
|
||||||
sessionStorage.clear();
|
sessionStorage.clear();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
async_test("storage events", function() {
|
||||||
|
var iframe = document.createElement("iframe"), iframe2 = document.createElement("iframe");
|
||||||
|
var local = false, storage, storage2, v = document.documentMode, i = 0;
|
||||||
|
|
||||||
|
var tests = [
|
||||||
|
function() {
|
||||||
|
expect();
|
||||||
|
storage.removeItem("foobar");
|
||||||
|
},
|
||||||
|
function() {
|
||||||
|
expect(0, "foobar", "", "test");
|
||||||
|
storage.setItem("foobar", "test");
|
||||||
|
},
|
||||||
|
function() {
|
||||||
|
expect(1, "foobar", "test", "TEST", true);
|
||||||
|
storage2.setItem("foobar", "TEST");
|
||||||
|
},
|
||||||
|
function() {
|
||||||
|
expect(0, "foobar", "TEST", "");
|
||||||
|
storage.removeItem("foobar");
|
||||||
|
},
|
||||||
|
function() {
|
||||||
|
expect(1, "winetest", "", "WineTest");
|
||||||
|
storage2.setItem("winetest", "WineTest");
|
||||||
|
},
|
||||||
|
function() {
|
||||||
|
expect(0, "", "", "");
|
||||||
|
storage.clear();
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
function next() {
|
||||||
|
if(++i < tests.length)
|
||||||
|
tests[i]();
|
||||||
|
else if(local)
|
||||||
|
next_test();
|
||||||
|
else {
|
||||||
|
// w10pro64 testbot VM throws WININET_E_INTERNAL_ERROR for some reason
|
||||||
|
storage = null, storage2 = null;
|
||||||
|
try {
|
||||||
|
storage = window.localStorage, storage2 = iframe.contentWindow.localStorage;
|
||||||
|
}catch(e) {
|
||||||
|
ok(e.number === 0x72ee4 - 0x80000000, "localStorage threw " + e.number + ": " + e);
|
||||||
|
}
|
||||||
|
if(!storage || !storage2) {
|
||||||
|
win_skip("localStorage is buggy and not available, skipping");
|
||||||
|
next_test();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
i = 0, local = true;
|
||||||
|
|
||||||
|
if(!storage.length)
|
||||||
|
setTimeout(function() { tests[0](); });
|
||||||
|
else {
|
||||||
|
// Get rid of any entries first, since native doesn't update immediately
|
||||||
|
var w = [ window, iframe.contentWindow ];
|
||||||
|
for(var j = 0; j < w.length; j++)
|
||||||
|
w[j].onstorage = w[j].document.onstorage = w[j].document.onstoragecommit = null;
|
||||||
|
document.onstoragecommit = function() {
|
||||||
|
if(!storage.length)
|
||||||
|
setTimeout(function() { tests[0](); });
|
||||||
|
else
|
||||||
|
storage.clear();
|
||||||
|
};
|
||||||
|
storage.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function test_event(e, key, oldValue, newValue) {
|
||||||
|
if(v < 9) {
|
||||||
|
ok(e === undefined, "event not undefined in legacy mode: " + e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var s = Object.prototype.toString.call(e);
|
||||||
|
todo_wine.
|
||||||
|
ok(s === "[object StorageEvent]", "Object.toString = " + s);
|
||||||
|
ok(e.key === key, "key = " + e.key + ", expected " + key);
|
||||||
|
ok(e.oldValue === oldValue, "oldValue = " + e.oldValue + ", expected " + oldValue);
|
||||||
|
ok(e.newValue === newValue, "newValue = " + e.newValue + ", expected " + newValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
function expect(idx, key, oldValue, newValue, quirk) {
|
||||||
|
var window2 = iframe.contentWindow, document2 = window2.document;
|
||||||
|
window.onstorage = function() { ok(false, "window.onstorage called"); };
|
||||||
|
document.onstorage = function() { ok(false, "doc.onstorage called"); };
|
||||||
|
document.onstoragecommit = function() { ok(false, "doc.onstoragecommit called"); };
|
||||||
|
window2.onstorage = function() { ok(false, "iframe window.onstorage called"); };
|
||||||
|
document2.onstorage = function() { ok(false, "iframe doc.onstorage called"); };
|
||||||
|
document2.onstoragecommit = function() { ok(false, "iframe doc.onstoragecommit called"); };
|
||||||
|
|
||||||
|
if(idx === undefined) {
|
||||||
|
setTimeout(function() { next(); });
|
||||||
|
}else {
|
||||||
|
// Native sometimes calls this for some reason
|
||||||
|
if(local && quirk) document.onstoragecommit = null;
|
||||||
|
|
||||||
|
(v < 9 ? document2 : window2)["onstorage"] = function(e) {
|
||||||
|
(local && idx ? document2 : (local || v < 9 ? document : window))[local ? "onstoragecommit" : "onstorage"] = function(e) {
|
||||||
|
test_event(e, local ? "" : key, local ? "" : oldValue, local ? "" : newValue);
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
test_event(e, key, oldValue, newValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
iframe.onload = function() {
|
||||||
|
iframe2.onload = function() {
|
||||||
|
var w = iframe2.contentWindow;
|
||||||
|
w.onstorage = function() { ok(false, "about:blank window.onstorage called"); };
|
||||||
|
w.document.onstorage = function() { ok(false, "about:blank document.onstorage called"); };
|
||||||
|
w.document.onstoragecommit = function() { ok(false, "about:blank document.onstoragecommit called"); };
|
||||||
|
|
||||||
|
storage = window.sessionStorage, storage2 = iframe.contentWindow.sessionStorage;
|
||||||
|
tests[0]();
|
||||||
|
};
|
||||||
|
iframe2.src = "about:blank";
|
||||||
|
document.body.appendChild(iframe2);
|
||||||
|
};
|
||||||
|
iframe.src = "blank.html";
|
||||||
|
document.body.appendChild(iframe);
|
||||||
|
});
|
||||||
|
|
||||||
sync_test("elem_attr", function() {
|
sync_test("elem_attr", function() {
|
||||||
var v = document.documentMode;
|
var v = document.documentMode;
|
||||||
var elem = document.createElement("div"), r;
|
var elem = document.createElement("div"), r;
|
||||||
|
|
Loading…
Reference in a new issue