mshtml: Send StorageEvents to iframe windows properly.

Signed-off-by: Gabriel Ivăncescu <gabrielopcode@gmail.com>
This commit is contained in:
Gabriel Ivăncescu 2022-09-13 19:57:37 +03:00 committed by Alexandre Julliard
parent e64ab65a5e
commit 2297623f01
2 changed files with 233 additions and 11 deletions

View file

@ -197,6 +197,8 @@ static inline HTMLStorage *impl_from_IHTMLStorage(IHTMLStorage *iface)
return CONTAINING_RECORD(iface, HTMLStorage, IHTMLStorage_iface);
}
static HRESULT build_session_origin(IUri*,BSTR,BSTR*);
struct storage_event_task {
task_t header;
HTMLInnerWindow *window;
@ -226,22 +228,22 @@ static void storage_event_destr(task_t *_task)
IHTMLWindow2_Release(&task->window->base.IHTMLWindow2_iface);
}
/* Takes ownership of old_value */
static HRESULT send_storage_event(HTMLStorage *storage, BSTR key, BSTR old_value, BSTR new_value)
struct send_storage_event_ctx {
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;
DOMEvent *event;
HRESULT hres;
if(!window) {
SysFreeString(old_value);
return S_OK;
}
hres = create_storage_event(window->doc, key, old_value, new_value, local, &event);
SysFreeString(old_value);
hres = create_storage_event(window->doc, ctx->key, ctx->old_value, ctx->new_value, commit, &event);
if(FAILED(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);
}
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)
{
HTMLStorage *This = impl_from_IHTMLStorage(iface);

View file

@ -1253,6 +1253,130 @@ sync_test("storage", function() {
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() {
var v = document.documentMode;
var elem = document.createElement("div"), r;