mirror of
git://source.winehq.org/git/wine.git
synced 2024-09-01 21:03:38 +00:00
1497 lines
41 KiB
C
1497 lines
41 KiB
C
/*
|
|
* Copyright 2012 Jacek Caban for CodeWeavers
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library 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
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
|
*/
|
|
|
|
#include <stdarg.h>
|
|
|
|
#define COBJMACROS
|
|
|
|
#include "windef.h"
|
|
#include "winbase.h"
|
|
#include "winuser.h"
|
|
#include "ole2.h"
|
|
#include "shlobj.h"
|
|
|
|
#include "wine/debug.h"
|
|
|
|
#include "mshtml_private.h"
|
|
#include "htmlevent.h"
|
|
|
|
WINE_DEFAULT_DEBUG_CHANNEL(mshtml);
|
|
|
|
/* Native defaults to 5 million chars per origin */
|
|
enum { MAX_QUOTA = 5000000 };
|
|
|
|
typedef struct {
|
|
DispatchEx dispex;
|
|
IHTMLStorage IHTMLStorage_iface;
|
|
LONG ref;
|
|
unsigned num_props;
|
|
BSTR *props;
|
|
HTMLInnerWindow *window;
|
|
struct session_map_entry *session_storage;
|
|
WCHAR *filename;
|
|
HANDLE mutex;
|
|
} HTMLStorage;
|
|
|
|
struct session_map_entry {
|
|
struct wine_rb_entry entry;
|
|
struct wine_rb_tree data_map;
|
|
struct list data_list; /* for key() */
|
|
UINT ref;
|
|
UINT quota;
|
|
UINT num_keys;
|
|
UINT origin_len;
|
|
WCHAR origin[1];
|
|
};
|
|
|
|
int session_storage_map_cmp(const void *key, const struct wine_rb_entry *entry)
|
|
{
|
|
struct session_map_entry *p = WINE_RB_ENTRY_VALUE(entry, struct session_map_entry, entry);
|
|
UINT len = SysStringLen((BSTR)key);
|
|
|
|
return (len != p->origin_len) ? (len - p->origin_len) : memcmp(key, p->origin, len * sizeof(WCHAR));
|
|
}
|
|
|
|
struct session_entry
|
|
{
|
|
struct wine_rb_entry entry;
|
|
struct list list_entry;
|
|
BSTR value;
|
|
WCHAR key[1];
|
|
};
|
|
|
|
static int session_entry_cmp(const void *key, const struct wine_rb_entry *entry)
|
|
{
|
|
struct session_entry *data = WINE_RB_ENTRY_VALUE(entry, struct session_entry, entry);
|
|
return wcscmp(key, data->key);
|
|
}
|
|
|
|
static struct session_map_entry *grab_session_map_entry(BSTR origin)
|
|
{
|
|
struct session_map_entry *entry;
|
|
struct wine_rb_entry *rb_entry;
|
|
thread_data_t *thread_data;
|
|
UINT origin_len;
|
|
|
|
thread_data = get_thread_data(TRUE);
|
|
if(!thread_data)
|
|
return NULL;
|
|
|
|
rb_entry = wine_rb_get(&thread_data->session_storage_map, origin);
|
|
if(rb_entry) {
|
|
entry = WINE_RB_ENTRY_VALUE(rb_entry, struct session_map_entry, entry);
|
|
entry->ref++;
|
|
return entry;
|
|
}
|
|
|
|
origin_len = SysStringLen(origin);
|
|
entry = malloc(FIELD_OFFSET(struct session_map_entry, origin[origin_len]));
|
|
if(!entry)
|
|
return NULL;
|
|
wine_rb_init(&entry->data_map, session_entry_cmp);
|
|
list_init(&entry->data_list);
|
|
entry->ref = 1;
|
|
entry->quota = MAX_QUOTA;
|
|
entry->num_keys = 0;
|
|
entry->origin_len = origin_len;
|
|
memcpy(entry->origin, origin, origin_len * sizeof(WCHAR));
|
|
|
|
wine_rb_put(&thread_data->session_storage_map, origin, &entry->entry);
|
|
return entry;
|
|
}
|
|
|
|
static void release_session_map_entry(struct session_map_entry *entry)
|
|
{
|
|
if(!entry || --entry->ref || entry->num_keys)
|
|
return;
|
|
|
|
wine_rb_remove(&get_thread_data(FALSE)->session_storage_map, &entry->entry);
|
|
free(entry);
|
|
}
|
|
|
|
static HRESULT get_session_entry(struct session_map_entry *entry, const WCHAR *name, BOOL create, struct session_entry **ret)
|
|
{
|
|
const WCHAR *key = name ? name : L"";
|
|
struct wine_rb_entry *rb_entry;
|
|
struct session_entry *data;
|
|
UINT key_len;
|
|
|
|
rb_entry = wine_rb_get(&entry->data_map, key);
|
|
if(rb_entry) {
|
|
*ret = WINE_RB_ENTRY_VALUE(rb_entry, struct session_entry, entry);
|
|
return S_OK;
|
|
}
|
|
|
|
if(!create) {
|
|
*ret = NULL;
|
|
return S_OK;
|
|
}
|
|
|
|
key_len = wcslen(key);
|
|
if(entry->quota < key_len)
|
|
return E_OUTOFMEMORY; /* native returns this when quota is exceeded */
|
|
if(!(data = malloc(FIELD_OFFSET(struct session_entry, key[key_len + 1]))))
|
|
return E_OUTOFMEMORY;
|
|
data->value = NULL;
|
|
memcpy(data->key, key, (key_len + 1) * sizeof(WCHAR));
|
|
|
|
entry->quota -= key_len;
|
|
entry->num_keys++;
|
|
list_add_tail(&entry->data_list, &data->list_entry);
|
|
wine_rb_put(&entry->data_map, key, &data->entry);
|
|
*ret = data;
|
|
return S_OK;
|
|
}
|
|
|
|
static void clear_session_storage(struct session_map_entry *entry)
|
|
{
|
|
struct session_entry *iter, *iter2;
|
|
|
|
LIST_FOR_EACH_ENTRY_SAFE(iter, iter2, &entry->data_list, struct session_entry, list_entry) {
|
|
SysFreeString(iter->value);
|
|
free(iter);
|
|
}
|
|
wine_rb_destroy(&entry->data_map, NULL, NULL);
|
|
list_init(&entry->data_list);
|
|
entry->quota = MAX_QUOTA;
|
|
entry->num_keys = 0;
|
|
}
|
|
|
|
void destroy_session_storage(thread_data_t *thread_data)
|
|
{
|
|
struct session_map_entry *iter, *iter2;
|
|
|
|
WINE_RB_FOR_EACH_ENTRY_DESTRUCTOR(iter, iter2, &thread_data->session_storage_map, struct session_map_entry, entry) {
|
|
clear_session_storage(iter);
|
|
free(iter);
|
|
}
|
|
}
|
|
|
|
static void release_props(HTMLStorage *This)
|
|
{
|
|
BSTR *prop = This->props, *end = prop + This->num_props;
|
|
while(prop != end) {
|
|
SysFreeString(*prop);
|
|
prop++;
|
|
}
|
|
free(This->props);
|
|
}
|
|
|
|
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;
|
|
DOMEvent *event;
|
|
};
|
|
|
|
static void storage_event_proc(task_t *_task)
|
|
{
|
|
struct storage_event_task *task = (struct storage_event_task*)_task;
|
|
HTMLInnerWindow *window = task->window;
|
|
DOMEvent *event = task->event;
|
|
VARIANT_BOOL cancelled;
|
|
|
|
if(event->event_id == EVENTID_STORAGE && dispex_compat_mode(&window->event_target.dispex) >= COMPAT_MODE_IE9) {
|
|
dispatch_event(&window->event_target, event);
|
|
if(window->doc)
|
|
fire_event(&window->doc->node, L"onstorage", NULL, &cancelled);
|
|
}else if(window->doc) {
|
|
dispatch_event(&window->doc->node.event_target, event);
|
|
}
|
|
}
|
|
|
|
static void storage_event_destr(task_t *_task)
|
|
{
|
|
struct storage_event_task *task = (struct storage_event_task*)_task;
|
|
IDOMEvent_Release(&task->event->IDOMEvent_iface);
|
|
IHTMLWindow2_Release(&task->window->base.IHTMLWindow2_iface);
|
|
}
|
|
|
|
struct send_storage_event_ctx {
|
|
HTMLInnerWindow *skip_window;
|
|
const WCHAR *origin;
|
|
UINT origin_len;
|
|
BSTR key;
|
|
BSTR old_value;
|
|
BSTR new_value;
|
|
BSTR url;
|
|
};
|
|
|
|
static HRESULT push_storage_event_task(struct send_storage_event_ctx *ctx, HTMLInnerWindow *window, BOOL commit)
|
|
{
|
|
struct storage_event_task *task;
|
|
DOMEvent *event;
|
|
HRESULT hres;
|
|
|
|
hres = create_storage_event(window->doc, ctx->key, ctx->old_value, ctx->new_value, ctx->url, commit, &event);
|
|
if(FAILED(hres))
|
|
return hres;
|
|
|
|
if(!(task = malloc(sizeof(*task)))) {
|
|
IDOMEvent_Release(&event->IDOMEvent_iface);
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
task->event = event;
|
|
task->window = window;
|
|
IHTMLWindow2_AddRef(&task->window->base.IHTMLWindow2_iface);
|
|
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;
|
|
|
|
ctx.url = NULL;
|
|
if(!window)
|
|
goto done;
|
|
if(window->base.outer_window->uri_nofrag) {
|
|
hres = IUri_GetDisplayUri(window->base.outer_window->uri_nofrag, &ctx.url);
|
|
if(hres != S_OK)
|
|
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(ctx.url);
|
|
SysFreeString(hostname);
|
|
SysFreeString(old_value);
|
|
return FAILED(hres) ? hres : S_OK;
|
|
}
|
|
|
|
static HRESULT WINAPI HTMLStorage_QueryInterface(IHTMLStorage *iface, REFIID riid, void **ppv)
|
|
{
|
|
HTMLStorage *This = impl_from_IHTMLStorage(iface);
|
|
|
|
TRACE("(%p)->(%s %p)\n", This, debugstr_mshtml_guid(riid), ppv);
|
|
|
|
if(IsEqualGUID(&IID_IUnknown, riid)) {
|
|
*ppv = &This->IHTMLStorage_iface;
|
|
}else if(IsEqualGUID(&IID_IHTMLStorage, riid)) {
|
|
*ppv = &This->IHTMLStorage_iface;
|
|
}else if(dispex_query_interface(&This->dispex, riid, ppv)) {
|
|
return *ppv ? S_OK : E_NOINTERFACE;
|
|
}else {
|
|
*ppv = NULL;
|
|
WARN("(%p)->(%s %p)\n", This, debugstr_mshtml_guid(riid), ppv);
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
IUnknown_AddRef((IUnknown*)*ppv);
|
|
return S_OK;
|
|
}
|
|
|
|
static ULONG WINAPI HTMLStorage_AddRef(IHTMLStorage *iface)
|
|
{
|
|
HTMLStorage *This = impl_from_IHTMLStorage(iface);
|
|
LONG ref = InterlockedIncrement(&This->ref);
|
|
|
|
TRACE("(%p) ref=%ld\n", This, ref);
|
|
|
|
return ref;
|
|
}
|
|
|
|
static ULONG WINAPI HTMLStorage_Release(IHTMLStorage *iface)
|
|
{
|
|
HTMLStorage *This = impl_from_IHTMLStorage(iface);
|
|
LONG ref = InterlockedDecrement(&This->ref);
|
|
|
|
TRACE("(%p) ref=%ld\n", This, ref);
|
|
|
|
if(!ref) {
|
|
release_session_map_entry(This->session_storage);
|
|
release_dispex(&This->dispex);
|
|
free(This->filename);
|
|
CloseHandle(This->mutex);
|
|
release_props(This);
|
|
free(This);
|
|
}
|
|
|
|
return ref;
|
|
}
|
|
|
|
static HRESULT WINAPI HTMLStorage_GetTypeInfoCount(IHTMLStorage *iface, UINT *pctinfo)
|
|
{
|
|
HTMLStorage *This = impl_from_IHTMLStorage(iface);
|
|
return IDispatchEx_GetTypeInfoCount(&This->dispex.IDispatchEx_iface, pctinfo);
|
|
}
|
|
|
|
static HRESULT WINAPI HTMLStorage_GetTypeInfo(IHTMLStorage *iface, UINT iTInfo,
|
|
LCID lcid, ITypeInfo **ppTInfo)
|
|
{
|
|
HTMLStorage *This = impl_from_IHTMLStorage(iface);
|
|
|
|
return IDispatchEx_GetTypeInfo(&This->dispex.IDispatchEx_iface, iTInfo, lcid, ppTInfo);
|
|
}
|
|
|
|
static HRESULT WINAPI HTMLStorage_GetIDsOfNames(IHTMLStorage *iface, REFIID riid, LPOLESTR *rgszNames, UINT cNames,
|
|
LCID lcid, DISPID *rgDispId)
|
|
{
|
|
HTMLStorage *This = impl_from_IHTMLStorage(iface);
|
|
|
|
return IDispatchEx_GetIDsOfNames(&This->dispex.IDispatchEx_iface, riid, rgszNames, cNames,
|
|
lcid, rgDispId);
|
|
}
|
|
|
|
static HRESULT WINAPI HTMLStorage_Invoke(IHTMLStorage *iface, DISPID dispIdMember, REFIID riid, LCID lcid,
|
|
WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr)
|
|
{
|
|
HTMLStorage *This = impl_from_IHTMLStorage(iface);
|
|
|
|
return IDispatchEx_Invoke(&This->dispex.IDispatchEx_iface, dispIdMember, riid, lcid, wFlags,
|
|
pDispParams, pVarResult, pExcepInfo, puArgErr);
|
|
}
|
|
|
|
static BOOL create_path(const WCHAR *path)
|
|
{
|
|
BOOL ret = TRUE;
|
|
WCHAR *new_path;
|
|
int len;
|
|
|
|
new_path = malloc((wcslen(path) + 1) * sizeof(WCHAR));
|
|
if(!new_path)
|
|
return FALSE;
|
|
wcscpy(new_path, path);
|
|
|
|
while((len = wcslen(new_path)) && new_path[len - 1] == '\\')
|
|
new_path[len - 1] = 0;
|
|
|
|
while(!CreateDirectoryW(new_path, NULL)) {
|
|
WCHAR *slash;
|
|
DWORD error = GetLastError();
|
|
if(error == ERROR_ALREADY_EXISTS) break;
|
|
if(error != ERROR_PATH_NOT_FOUND) {
|
|
ret = FALSE;
|
|
break;
|
|
}
|
|
slash = wcsrchr(new_path, '\\');
|
|
if(!slash) {
|
|
ret = FALSE;
|
|
break;
|
|
}
|
|
len = slash - new_path;
|
|
new_path[len] = 0;
|
|
if(!create_path(new_path)) {
|
|
ret = FALSE;
|
|
break;
|
|
}
|
|
new_path[len] = '\\';
|
|
}
|
|
free(new_path);
|
|
return ret;
|
|
}
|
|
|
|
static HRESULT open_document(const WCHAR *filename, IXMLDOMDocument **ret)
|
|
{
|
|
IXMLDOMDocument *doc = NULL;
|
|
HRESULT hres = E_OUTOFMEMORY;
|
|
WCHAR *ptr, *path;
|
|
VARIANT var;
|
|
VARIANT_BOOL success;
|
|
|
|
path = wcsdup(filename);
|
|
if(!path)
|
|
return E_OUTOFMEMORY;
|
|
|
|
*(ptr = wcsrchr(path, '\\')) = 0;
|
|
if(!create_path(path))
|
|
goto done;
|
|
|
|
if(GetFileAttributesW(filename) == INVALID_FILE_ATTRIBUTES) {
|
|
DWORD count;
|
|
HANDLE file = CreateFileW(filename, GENERIC_WRITE, 0, NULL, CREATE_NEW, 0, NULL);
|
|
if(file == INVALID_HANDLE_VALUE) {
|
|
hres = HRESULT_FROM_WIN32(GetLastError());
|
|
goto done;
|
|
}
|
|
if(!WriteFile(file, "<root/>", sizeof("<root/>") - 1, &count, NULL)) {
|
|
CloseHandle(file);
|
|
hres = HRESULT_FROM_WIN32(GetLastError());
|
|
goto done;
|
|
}
|
|
CloseHandle(file);
|
|
}
|
|
|
|
hres = CoCreateInstance(&CLSID_DOMDocument, NULL, CLSCTX_INPROC_SERVER, &IID_IXMLDOMDocument, (void**)&doc);
|
|
if(hres != S_OK)
|
|
goto done;
|
|
|
|
V_VT(&var) = VT_BSTR;
|
|
V_BSTR(&var) = SysAllocString(filename);
|
|
if(!V_BSTR(&var)) {
|
|
hres = E_OUTOFMEMORY;
|
|
goto done;
|
|
}
|
|
|
|
hres = IXMLDOMDocument_load(doc, var, &success);
|
|
if(hres == S_FALSE || success == VARIANT_FALSE)
|
|
hres = E_FAIL;
|
|
|
|
SysFreeString(V_BSTR(&var));
|
|
|
|
done:
|
|
free(path);
|
|
if(hres == S_OK)
|
|
*ret = doc;
|
|
else if(doc)
|
|
IXMLDOMDocument_Release(doc);
|
|
|
|
return hres;
|
|
}
|
|
|
|
static HRESULT get_root_node(IXMLDOMDocument *doc, IXMLDOMNode **root)
|
|
{
|
|
HRESULT hres;
|
|
BSTR str;
|
|
|
|
str = SysAllocString(L"root");
|
|
if(!str)
|
|
return E_OUTOFMEMORY;
|
|
|
|
hres = IXMLDOMDocument_selectSingleNode(doc, str, root);
|
|
SysFreeString(str);
|
|
return hres;
|
|
}
|
|
|
|
static HRESULT get_node_list(const WCHAR *filename, IXMLDOMNodeList **node_list)
|
|
{
|
|
IXMLDOMDocument *doc;
|
|
IXMLDOMNode *root;
|
|
HRESULT hres;
|
|
BSTR query;
|
|
|
|
hres = open_document(filename, &doc);
|
|
if(hres != S_OK)
|
|
return hres;
|
|
|
|
hres = get_root_node(doc, &root);
|
|
IXMLDOMDocument_Release(doc);
|
|
if(hres != S_OK)
|
|
return hres;
|
|
|
|
if(!(query = SysAllocString(L"item")))
|
|
hres = E_OUTOFMEMORY;
|
|
else {
|
|
hres = IXMLDOMNode_selectNodes(root, query, node_list);
|
|
SysFreeString(query);
|
|
}
|
|
IXMLDOMNode_Release(root);
|
|
return hres;
|
|
}
|
|
|
|
static HRESULT WINAPI HTMLStorage_get_length(IHTMLStorage *iface, LONG *p)
|
|
{
|
|
HTMLStorage *This = impl_from_IHTMLStorage(iface);
|
|
IXMLDOMNodeList *node_list;
|
|
HRESULT hres;
|
|
|
|
TRACE("(%p)->(%p)\n", This, p);
|
|
|
|
if(!This->filename) {
|
|
*p = This->session_storage->num_keys;
|
|
return S_OK;
|
|
}
|
|
|
|
WaitForSingleObject(This->mutex, INFINITE);
|
|
hres = get_node_list(This->filename, &node_list);
|
|
if(SUCCEEDED(hres)) {
|
|
hres = IXMLDOMNodeList_get_length(node_list, p);
|
|
IXMLDOMNodeList_Release(node_list);
|
|
}
|
|
ReleaseMutex(This->mutex);
|
|
|
|
return hres;
|
|
}
|
|
|
|
static HRESULT WINAPI HTMLStorage_get_remainingSpace(IHTMLStorage *iface, LONG *p)
|
|
{
|
|
HTMLStorage *This = impl_from_IHTMLStorage(iface);
|
|
|
|
TRACE("(%p)->(%p)\n", This, p);
|
|
|
|
if(!This->filename) {
|
|
*p = This->session_storage->quota;
|
|
return S_OK;
|
|
}
|
|
|
|
FIXME("local storage not supported\n");
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
static HRESULT get_key(const WCHAR *filename, LONG index, BSTR *ret)
|
|
{
|
|
IXMLDOMNodeList *node_list;
|
|
IXMLDOMElement *elem;
|
|
IXMLDOMNode *node;
|
|
HRESULT hres;
|
|
VARIANT key;
|
|
|
|
hres = get_node_list(filename, &node_list);
|
|
if(FAILED(hres))
|
|
return hres;
|
|
|
|
hres = IXMLDOMNodeList_get_item(node_list, index, &node);
|
|
IXMLDOMNodeList_Release(node_list);
|
|
if(hres != S_OK)
|
|
return FAILED(hres) ? hres : E_INVALIDARG;
|
|
|
|
hres = IXMLDOMNode_QueryInterface(node, &IID_IXMLDOMElement, (void**)&elem);
|
|
IXMLDOMNode_Release(node);
|
|
if(hres != S_OK)
|
|
return E_INVALIDARG;
|
|
|
|
hres = IXMLDOMElement_getAttribute(elem, (BSTR)L"name", &key);
|
|
IXMLDOMElement_Release(elem);
|
|
if(FAILED(hres))
|
|
return hres;
|
|
|
|
if(V_VT(&key) != VT_BSTR) {
|
|
FIXME("non-string key %s\n", debugstr_variant(&key));
|
|
VariantClear(&key);
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
*ret = V_BSTR(&key);
|
|
return S_OK;
|
|
}
|
|
|
|
static HRESULT WINAPI HTMLStorage_key(IHTMLStorage *iface, LONG lIndex, BSTR *p)
|
|
{
|
|
HTMLStorage *This = impl_from_IHTMLStorage(iface);
|
|
struct session_entry *session_entry;
|
|
HRESULT hres;
|
|
|
|
TRACE("(%p)->(%ld %p)\n", This, lIndex, p);
|
|
|
|
if(!This->filename) {
|
|
struct list *entry = &This->session_storage->data_list;
|
|
unsigned i = 0;
|
|
|
|
if(lIndex >= This->session_storage->num_keys)
|
|
return E_INVALIDARG;
|
|
|
|
do entry = entry->next; while(i++ < lIndex);
|
|
session_entry = LIST_ENTRY(entry, struct session_entry, list_entry);
|
|
|
|
*p = SysAllocString(session_entry->key);
|
|
return *p ? S_OK : E_OUTOFMEMORY;
|
|
}
|
|
|
|
WaitForSingleObject(This->mutex, INFINITE);
|
|
hres = get_key(This->filename, lIndex, p);
|
|
ReleaseMutex(This->mutex);
|
|
|
|
return hres;
|
|
}
|
|
|
|
static BSTR build_query(const WCHAR *key)
|
|
{
|
|
static const WCHAR fmt[] = L"item[@name='%s']";
|
|
const WCHAR *str = key ? key : L"";
|
|
UINT len = ARRAY_SIZE(fmt) + wcslen(str);
|
|
BSTR ret = SysAllocStringLen(NULL, len);
|
|
|
|
if(ret) swprintf(ret, len, fmt, str);
|
|
return ret;
|
|
}
|
|
|
|
static HRESULT get_item(const WCHAR *filename, BSTR key, VARIANT *value)
|
|
{
|
|
IXMLDOMDocument *doc;
|
|
BSTR query = NULL;
|
|
IXMLDOMNode *root = NULL, *node = NULL;
|
|
IXMLDOMElement *elem = NULL;
|
|
HRESULT hres;
|
|
|
|
hres = open_document(filename, &doc);
|
|
if(hres != S_OK)
|
|
return hres;
|
|
|
|
hres = get_root_node(doc, &root);
|
|
if(hres != S_OK)
|
|
goto done;
|
|
|
|
query = build_query(key);
|
|
if(!query) {
|
|
hres = E_OUTOFMEMORY;
|
|
goto done;
|
|
}
|
|
|
|
hres = IXMLDOMNode_selectSingleNode(root, query, &node);
|
|
if(hres == S_OK) {
|
|
hres = IXMLDOMNode_QueryInterface(node, &IID_IXMLDOMElement, (void**)&elem);
|
|
if(hres != S_OK)
|
|
goto done;
|
|
|
|
hres = IXMLDOMElement_getAttribute(elem, (BSTR)L"value", value);
|
|
}else {
|
|
V_VT(value) = VT_NULL;
|
|
hres = S_OK;
|
|
}
|
|
|
|
done:
|
|
SysFreeString(query);
|
|
if(root)
|
|
IXMLDOMNode_Release(root);
|
|
if(node)
|
|
IXMLDOMNode_Release(node);
|
|
if(elem)
|
|
IXMLDOMElement_Release(elem);
|
|
IXMLDOMDocument_Release(doc);
|
|
return hres;
|
|
}
|
|
|
|
static HRESULT WINAPI HTMLStorage_getItem(IHTMLStorage *iface, BSTR bstrKey, VARIANT *value)
|
|
{
|
|
HTMLStorage *This = impl_from_IHTMLStorage(iface);
|
|
struct session_entry *session_entry;
|
|
HRESULT hres;
|
|
|
|
TRACE("(%p)->(%s %p)\n", This, debugstr_w(bstrKey), value);
|
|
|
|
if(!value)
|
|
return E_POINTER;
|
|
|
|
if(!This->filename) {
|
|
hres = get_session_entry(This->session_storage, bstrKey, FALSE, &session_entry);
|
|
if(SUCCEEDED(hres)) {
|
|
if(!session_entry || !session_entry->value)
|
|
V_VT(value) = VT_NULL;
|
|
else {
|
|
V_VT(value) = VT_BSTR;
|
|
V_BSTR(value) = SysAllocStringLen(session_entry->value, SysStringLen(session_entry->value));
|
|
hres = V_BSTR(value) ? S_OK : E_OUTOFMEMORY;
|
|
}
|
|
}
|
|
return hres;
|
|
}
|
|
|
|
WaitForSingleObject(This->mutex, INFINITE);
|
|
hres = get_item(This->filename, bstrKey, value);
|
|
ReleaseMutex(This->mutex);
|
|
|
|
return hres;
|
|
}
|
|
|
|
static HRESULT set_attribute(IXMLDOMElement *elem, const WCHAR *name, BSTR value)
|
|
{
|
|
BSTR str;
|
|
VARIANT var;
|
|
HRESULT hres;
|
|
|
|
str = SysAllocString(name);
|
|
if(!str)
|
|
return E_OUTOFMEMORY;
|
|
V_VT(&var) = VT_BSTR;
|
|
V_BSTR(&var) = value;
|
|
|
|
hres = IXMLDOMElement_setAttribute(elem, str, var);
|
|
SysFreeString(str);
|
|
return hres;
|
|
}
|
|
|
|
static HRESULT save_document(IXMLDOMDocument *doc, const WCHAR *filename)
|
|
{
|
|
VARIANT var;
|
|
HRESULT hres;
|
|
|
|
V_VT(&var) = VT_BSTR;
|
|
V_BSTR(&var) = SysAllocString(filename);
|
|
if(!V_BSTR(&var))
|
|
return E_OUTOFMEMORY;
|
|
|
|
hres = IXMLDOMDocument_save(doc, var);
|
|
SysFreeString(V_BSTR(&var));
|
|
return hres;
|
|
}
|
|
|
|
static HRESULT set_item(const WCHAR *filename, BSTR key, BSTR value, BSTR *old_value)
|
|
{
|
|
IXMLDOMDocument *doc;
|
|
IXMLDOMNode *root = NULL, *node = NULL;
|
|
IXMLDOMElement *elem = NULL;
|
|
BSTR query = NULL;
|
|
HRESULT hres;
|
|
|
|
*old_value = NULL;
|
|
hres = open_document(filename, &doc);
|
|
if(hres != S_OK)
|
|
return hres;
|
|
|
|
hres = get_root_node(doc, &root);
|
|
if(hres != S_OK)
|
|
goto done;
|
|
|
|
query = build_query(key);
|
|
if(!query) {
|
|
hres = E_OUTOFMEMORY;
|
|
goto done;
|
|
}
|
|
|
|
hres = IXMLDOMNode_selectSingleNode(root, query, &node);
|
|
if(hres == S_OK) {
|
|
VARIANT old;
|
|
|
|
hres = IXMLDOMNode_QueryInterface(node, &IID_IXMLDOMElement, (void**)&elem);
|
|
if(hres != S_OK)
|
|
goto done;
|
|
|
|
hres = IXMLDOMElement_getAttribute(elem, (BSTR)L"value", &old);
|
|
if(hres == S_OK) {
|
|
if(V_VT(&old) == VT_BSTR)
|
|
*old_value = V_BSTR(&old);
|
|
else
|
|
VariantClear(&old);
|
|
}
|
|
|
|
hres = set_attribute(elem, L"value", value);
|
|
if(hres != S_OK)
|
|
goto done;
|
|
}else {
|
|
BSTR str = SysAllocString(L"item");
|
|
hres = IXMLDOMDocument_createElement(doc, str, &elem);
|
|
SysFreeString(str);
|
|
if(hres != S_OK)
|
|
goto done;
|
|
|
|
hres = set_attribute(elem, L"name", key);
|
|
if(hres != S_OK)
|
|
goto done;
|
|
|
|
hres = set_attribute(elem, L"value", value);
|
|
if(hres != S_OK)
|
|
goto done;
|
|
|
|
hres = IXMLDOMNode_appendChild(root, (IXMLDOMNode*)elem, NULL);
|
|
if(hres != S_OK)
|
|
goto done;
|
|
}
|
|
|
|
hres = save_document(doc, filename);
|
|
|
|
done:
|
|
SysFreeString(query);
|
|
if(root)
|
|
IXMLDOMNode_Release(root);
|
|
if(node)
|
|
IXMLDOMNode_Release(node);
|
|
if(elem)
|
|
IXMLDOMElement_Release(elem);
|
|
IXMLDOMDocument_Release(doc);
|
|
if(hres != S_OK)
|
|
SysFreeString(*old_value);
|
|
return hres;
|
|
}
|
|
|
|
static HRESULT WINAPI HTMLStorage_setItem(IHTMLStorage *iface, BSTR bstrKey, BSTR bstrValue)
|
|
{
|
|
HTMLStorage *This = impl_from_IHTMLStorage(iface);
|
|
struct session_entry *session_entry;
|
|
BSTR old_value;
|
|
HRESULT hres;
|
|
|
|
TRACE("(%p)->(%s %s)\n", This, debugstr_w(bstrKey), debugstr_w(bstrValue));
|
|
|
|
if(!This->filename) {
|
|
UINT value_len = bstrValue ? wcslen(bstrValue) : 0;
|
|
BSTR value = SysAllocStringLen(bstrValue, value_len);
|
|
if(!value)
|
|
return E_OUTOFMEMORY;
|
|
|
|
hres = get_session_entry(This->session_storage, bstrKey, TRUE, &session_entry);
|
|
if(FAILED(hres))
|
|
SysFreeString(value);
|
|
else {
|
|
UINT old_len = SysStringLen(session_entry->value);
|
|
if(old_len < value_len && This->session_storage->quota < value_len - old_len) {
|
|
SysFreeString(value);
|
|
return E_OUTOFMEMORY; /* native returns this when quota is exceeded */
|
|
}
|
|
This->session_storage->quota -= value_len - old_len;
|
|
old_value = session_entry->value;
|
|
session_entry->value = value;
|
|
|
|
hres = send_storage_event(This, bstrKey, old_value, value);
|
|
}
|
|
return hres;
|
|
}
|
|
|
|
WaitForSingleObject(This->mutex, INFINITE);
|
|
hres = set_item(This->filename, bstrKey, bstrValue, &old_value);
|
|
ReleaseMutex(This->mutex);
|
|
|
|
if(hres == S_OK)
|
|
hres = send_storage_event(This, bstrKey, old_value, bstrValue);
|
|
return hres;
|
|
}
|
|
|
|
static HRESULT remove_item(const WCHAR *filename, BSTR key, BSTR *old_value, BOOL *changed)
|
|
{
|
|
IXMLDOMDocument *doc;
|
|
IXMLDOMNode *root = NULL, *node = NULL;
|
|
BSTR query = NULL;
|
|
HRESULT hres;
|
|
|
|
*old_value = NULL;
|
|
*changed = FALSE;
|
|
hres = open_document(filename, &doc);
|
|
if(hres != S_OK)
|
|
return hres;
|
|
|
|
hres = get_root_node(doc, &root);
|
|
if(hres != S_OK)
|
|
goto done;
|
|
|
|
query = build_query(key);
|
|
if(!query) {
|
|
hres = E_OUTOFMEMORY;
|
|
goto done;
|
|
}
|
|
|
|
hres = IXMLDOMNode_selectSingleNode(root, query, &node);
|
|
if(hres == S_OK) {
|
|
IXMLDOMElement *elem;
|
|
VARIANT old;
|
|
|
|
hres = IXMLDOMNode_QueryInterface(node, &IID_IXMLDOMElement, (void**)&elem);
|
|
if(hres == S_OK) {
|
|
hres = IXMLDOMElement_getAttribute(elem, (BSTR)L"value", &old);
|
|
if(hres == S_OK) {
|
|
if(V_VT(&old) == VT_BSTR)
|
|
*old_value = V_BSTR(&old);
|
|
else
|
|
VariantClear(&old);
|
|
}
|
|
IXMLDOMElement_Release(elem);
|
|
*changed = TRUE;
|
|
}
|
|
|
|
hres = IXMLDOMNode_removeChild(root, node, NULL);
|
|
if(hres != S_OK)
|
|
goto done;
|
|
}
|
|
|
|
hres = save_document(doc, filename);
|
|
|
|
done:
|
|
SysFreeString(query);
|
|
if(root)
|
|
IXMLDOMNode_Release(root);
|
|
if(node)
|
|
IXMLDOMNode_Release(node);
|
|
IXMLDOMDocument_Release(doc);
|
|
if(hres != S_OK || !changed)
|
|
SysFreeString(*old_value);
|
|
return hres;
|
|
}
|
|
|
|
static HRESULT WINAPI HTMLStorage_removeItem(IHTMLStorage *iface, BSTR bstrKey)
|
|
{
|
|
HTMLStorage *This = impl_from_IHTMLStorage(iface);
|
|
struct session_entry *session_entry;
|
|
BSTR old_value;
|
|
HRESULT hres;
|
|
BOOL changed;
|
|
|
|
TRACE("(%p)->(%s)\n", This, debugstr_w(bstrKey));
|
|
|
|
if(!This->filename) {
|
|
hres = get_session_entry(This->session_storage, bstrKey, FALSE, &session_entry);
|
|
if(SUCCEEDED(hres) && session_entry) {
|
|
This->session_storage->quota += wcslen(session_entry->key) + SysStringLen(session_entry->value);
|
|
This->session_storage->num_keys--;
|
|
list_remove(&session_entry->list_entry);
|
|
wine_rb_remove(&This->session_storage->data_map, &session_entry->entry);
|
|
old_value = session_entry->value;
|
|
free(session_entry);
|
|
|
|
hres = send_storage_event(This, bstrKey, old_value, NULL);
|
|
}
|
|
return hres;
|
|
}
|
|
|
|
WaitForSingleObject(This->mutex, INFINITE);
|
|
hres = remove_item(This->filename, bstrKey, &old_value, &changed);
|
|
ReleaseMutex(This->mutex);
|
|
|
|
if(hres == S_OK && changed)
|
|
hres = send_storage_event(This, bstrKey, old_value, NULL);
|
|
return hres;
|
|
}
|
|
|
|
static HRESULT WINAPI HTMLStorage_clear(IHTMLStorage *iface)
|
|
{
|
|
HTMLStorage *This = impl_from_IHTMLStorage(iface);
|
|
HRESULT hres = S_OK;
|
|
|
|
if(!This->filename) {
|
|
UINT num_keys = This->session_storage->num_keys;
|
|
clear_session_storage(This->session_storage);
|
|
return num_keys ? send_storage_event(This, NULL, NULL, NULL) : S_OK;
|
|
}
|
|
|
|
WaitForSingleObject(This->mutex, INFINITE);
|
|
if(!DeleteFileW(This->filename)) {
|
|
DWORD error = GetLastError();
|
|
if(error != ERROR_FILE_NOT_FOUND && error != ERROR_PATH_NOT_FOUND)
|
|
hres = HRESULT_FROM_WIN32(error);
|
|
}
|
|
ReleaseMutex(This->mutex);
|
|
if(hres == S_OK)
|
|
hres = send_storage_event(This, NULL, NULL, NULL);
|
|
return hres;
|
|
}
|
|
|
|
static const IHTMLStorageVtbl HTMLStorageVtbl = {
|
|
HTMLStorage_QueryInterface,
|
|
HTMLStorage_AddRef,
|
|
HTMLStorage_Release,
|
|
HTMLStorage_GetTypeInfoCount,
|
|
HTMLStorage_GetTypeInfo,
|
|
HTMLStorage_GetIDsOfNames,
|
|
HTMLStorage_Invoke,
|
|
HTMLStorage_get_length,
|
|
HTMLStorage_get_remainingSpace,
|
|
HTMLStorage_key,
|
|
HTMLStorage_getItem,
|
|
HTMLStorage_setItem,
|
|
HTMLStorage_removeItem,
|
|
HTMLStorage_clear
|
|
};
|
|
|
|
static inline HTMLStorage *impl_from_DispatchEx(DispatchEx *iface)
|
|
{
|
|
return CONTAINING_RECORD(iface, HTMLStorage, dispex);
|
|
}
|
|
|
|
static HRESULT check_item(HTMLStorage *This, const WCHAR *key)
|
|
{
|
|
struct session_entry *session_entry;
|
|
IXMLDOMNode *root, *node;
|
|
IXMLDOMDocument *doc;
|
|
HRESULT hres;
|
|
BSTR query;
|
|
|
|
if(!This->filename) {
|
|
hres = get_session_entry(This->session_storage, key, FALSE, &session_entry);
|
|
if(SUCCEEDED(hres))
|
|
hres = (session_entry && session_entry->value) ? S_OK : S_FALSE;
|
|
return hres;
|
|
}
|
|
|
|
WaitForSingleObject(This->mutex, INFINITE);
|
|
|
|
hres = open_document(This->filename, &doc);
|
|
if(hres == S_OK) {
|
|
hres = get_root_node(doc, &root);
|
|
IXMLDOMDocument_Release(doc);
|
|
if(hres == S_OK) {
|
|
if(!(query = build_query(key)))
|
|
hres = E_OUTOFMEMORY;
|
|
else {
|
|
hres = IXMLDOMNode_selectSingleNode(root, query, &node);
|
|
SysFreeString(query);
|
|
if(hres == S_OK)
|
|
IXMLDOMNode_Release(node);
|
|
}
|
|
IXMLDOMNode_Release(root);
|
|
}
|
|
}
|
|
|
|
ReleaseMutex(This->mutex);
|
|
|
|
return hres;
|
|
}
|
|
|
|
static HRESULT get_prop(HTMLStorage *This, const WCHAR *name, DISPID *dispid)
|
|
{
|
|
UINT name_len = wcslen(name);
|
|
BSTR p, *prop, *end;
|
|
|
|
for(prop = This->props, end = prop + This->num_props; prop != end; prop++) {
|
|
if(SysStringLen(*prop) == name_len && !memcmp(*prop, name, name_len * sizeof(WCHAR))) {
|
|
*dispid = MSHTML_DISPID_CUSTOM_MIN + (prop - This->props);
|
|
return S_OK;
|
|
}
|
|
}
|
|
|
|
if(is_power_of_2(This->num_props)) {
|
|
BSTR *new_props = realloc(This->props, max(This->num_props * 2, 1) * sizeof(*This->props));
|
|
if(!new_props)
|
|
return E_OUTOFMEMORY;
|
|
This->props = new_props;
|
|
}
|
|
|
|
if(!(p = SysAllocStringLen(name, name_len)))
|
|
return E_OUTOFMEMORY;
|
|
|
|
This->props[This->num_props] = p;
|
|
*dispid = MSHTML_DISPID_CUSTOM_MIN + This->num_props++;
|
|
return S_OK;
|
|
}
|
|
|
|
static HRESULT HTMLStorage_get_dispid(DispatchEx *dispex, BSTR name, DWORD flags, DISPID *dispid)
|
|
{
|
|
HTMLStorage *This = impl_from_DispatchEx(dispex);
|
|
HRESULT hres;
|
|
|
|
if(flags & fdexNameCaseInsensitive)
|
|
FIXME("case insensitive not supported\n");
|
|
|
|
if(!(flags & fdexNameEnsure)) {
|
|
hres = check_item(This, name);
|
|
if(hres != S_OK)
|
|
return FAILED(hres) ? hres : DISP_E_UNKNOWNNAME;
|
|
}
|
|
|
|
return get_prop(This, name, dispid);
|
|
}
|
|
|
|
static HRESULT HTMLStorage_get_name(DispatchEx *dispex, DISPID id, BSTR *name)
|
|
{
|
|
HTMLStorage *This = impl_from_DispatchEx(dispex);
|
|
DWORD idx = id - MSHTML_DISPID_CUSTOM_MIN;
|
|
|
|
if(idx >= This->num_props)
|
|
return DISP_E_MEMBERNOTFOUND;
|
|
|
|
return (*name = SysAllocString(This->props[idx])) ? S_OK : E_OUTOFMEMORY;
|
|
}
|
|
|
|
static HRESULT HTMLStorage_invoke(DispatchEx *dispex, DISPID id, LCID lcid, WORD flags, DISPPARAMS *params,
|
|
VARIANT *res, EXCEPINFO *ei, IServiceProvider *caller)
|
|
{
|
|
HTMLStorage *This = impl_from_DispatchEx(dispex);
|
|
DWORD idx = id - MSHTML_DISPID_CUSTOM_MIN;
|
|
HRESULT hres;
|
|
BSTR bstr;
|
|
|
|
if(idx >= This->num_props)
|
|
return DISP_E_MEMBERNOTFOUND;
|
|
|
|
switch(flags) {
|
|
case DISPATCH_PROPERTYGET:
|
|
hres = HTMLStorage_getItem(&This->IHTMLStorage_iface, This->props[idx], res);
|
|
if(FAILED(hres))
|
|
return hres;
|
|
if(V_VT(res) == VT_NULL)
|
|
return DISP_E_MEMBERNOTFOUND;
|
|
break;
|
|
case DISPATCH_PROPERTYPUTREF:
|
|
case DISPATCH_PROPERTYPUT:
|
|
if(params->cArgs != 1 || (params->cNamedArgs && params->rgdispidNamedArgs[0] != DISPID_PROPERTYPUT)) {
|
|
FIXME("unimplemented args %u %u\n", params->cArgs, params->cNamedArgs);
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
bstr = V_BSTR(params->rgvarg);
|
|
if(V_VT(params->rgvarg) != VT_BSTR) {
|
|
VARIANT var;
|
|
hres = change_type(&var, params->rgvarg, VT_BSTR, caller);
|
|
if(FAILED(hres))
|
|
return hres;
|
|
bstr = V_BSTR(&var);
|
|
}
|
|
|
|
hres = HTMLStorage_setItem(&This->IHTMLStorage_iface, This->props[idx], bstr);
|
|
|
|
if(V_VT(params->rgvarg) != VT_BSTR)
|
|
SysFreeString(bstr);
|
|
return hres;
|
|
|
|
default:
|
|
FIXME("unimplemented flags %x\n", flags);
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
static HRESULT HTMLStorage_delete(DispatchEx *dispex, DISPID id)
|
|
{
|
|
HTMLStorage *This = impl_from_DispatchEx(dispex);
|
|
DWORD idx = id - MSHTML_DISPID_CUSTOM_MIN;
|
|
|
|
if(idx >= This->num_props)
|
|
return DISP_E_MEMBERNOTFOUND;
|
|
|
|
if(dispex_compat_mode(dispex) < COMPAT_MODE_IE8)
|
|
return MSHTML_E_INVALID_ACTION;
|
|
|
|
return HTMLStorage_removeItem(&This->IHTMLStorage_iface, This->props[idx]);
|
|
}
|
|
|
|
static HRESULT HTMLStorage_next_dispid(DispatchEx *dispex, DISPID id, DISPID *pid)
|
|
{
|
|
DWORD idx = (id == DISPID_STARTENUM) ? 0 : id - MSHTML_DISPID_CUSTOM_MIN + 1;
|
|
HTMLStorage *This = impl_from_DispatchEx(dispex);
|
|
HRESULT hres;
|
|
DISPID tmp;
|
|
|
|
if(idx > MSHTML_CUSTOM_DISPID_CNT)
|
|
return S_FALSE;
|
|
|
|
while(idx < This->num_props) {
|
|
hres = check_item(This, This->props[idx]);
|
|
if(hres == S_OK) {
|
|
*pid = idx + MSHTML_DISPID_CUSTOM_MIN;
|
|
return S_OK;
|
|
}
|
|
if(FAILED(hres))
|
|
return hres;
|
|
idx++;
|
|
}
|
|
|
|
/* Populate possibly missing DISPIDs */
|
|
if(!This->filename) {
|
|
struct session_entry *session_entry;
|
|
|
|
LIST_FOR_EACH_ENTRY(session_entry, &This->session_storage->data_list, struct session_entry, list_entry) {
|
|
hres = get_prop(This, session_entry->key, &tmp);
|
|
if(FAILED(hres))
|
|
return hres;
|
|
}
|
|
}else {
|
|
IXMLDOMNodeList *node_list;
|
|
IXMLDOMElement *elem;
|
|
IXMLDOMNode *node;
|
|
LONG index = 0;
|
|
HRESULT hres;
|
|
VARIANT key;
|
|
|
|
hres = get_node_list(This->filename, &node_list);
|
|
if(FAILED(hres))
|
|
return hres;
|
|
|
|
for(;;) {
|
|
hres = IXMLDOMNodeList_get_item(node_list, index++, &node);
|
|
if(hres != S_OK) {
|
|
IXMLDOMNodeList_Release(node_list);
|
|
if(FAILED(hres))
|
|
return hres;
|
|
break;
|
|
}
|
|
|
|
hres = IXMLDOMNode_QueryInterface(node, &IID_IXMLDOMElement, (void**)&elem);
|
|
IXMLDOMNode_Release(node);
|
|
if(hres != S_OK)
|
|
continue;
|
|
|
|
hres = IXMLDOMElement_getAttribute(elem, (BSTR)L"name", &key);
|
|
IXMLDOMElement_Release(elem);
|
|
if(hres != S_OK) {
|
|
if(SUCCEEDED(hres))
|
|
continue;
|
|
IXMLDOMNodeList_Release(node_list);
|
|
return hres;
|
|
}
|
|
|
|
if(V_VT(&key) != VT_BSTR) {
|
|
FIXME("non-string key %s\n", debugstr_variant(&key));
|
|
VariantClear(&key);
|
|
continue;
|
|
}
|
|
|
|
hres = get_prop(This, V_BSTR(&key), &tmp);
|
|
SysFreeString(V_BSTR(&key));
|
|
if(FAILED(hres)) {
|
|
IXMLDOMNodeList_Release(node_list);
|
|
return hres;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(idx >= This->num_props)
|
|
return S_FALSE;
|
|
|
|
*pid = idx + MSHTML_DISPID_CUSTOM_MIN;
|
|
return S_OK;
|
|
}
|
|
|
|
static const dispex_static_data_vtbl_t HTMLStorage_dispex_vtbl = {
|
|
NULL,
|
|
HTMLStorage_get_dispid,
|
|
HTMLStorage_get_name,
|
|
HTMLStorage_invoke,
|
|
HTMLStorage_delete,
|
|
HTMLStorage_next_dispid,
|
|
NULL
|
|
};
|
|
|
|
static const tid_t HTMLStorage_iface_tids[] = {
|
|
IHTMLStorage_tid,
|
|
0
|
|
};
|
|
static dispex_static_data_t HTMLStorage_dispex = {
|
|
L"Storage",
|
|
&HTMLStorage_dispex_vtbl,
|
|
IHTMLStorage_tid,
|
|
HTMLStorage_iface_tids
|
|
};
|
|
|
|
static HRESULT build_session_origin(IUri *uri, BSTR hostname, BSTR *ret)
|
|
{
|
|
UINT host_len, scheme_len;
|
|
BSTR scheme, origin;
|
|
HRESULT hres;
|
|
|
|
hres = IUri_GetSchemeName(uri, &scheme);
|
|
if(FAILED(hres))
|
|
return hres;
|
|
if(hres != S_OK) {
|
|
SysFreeString(scheme);
|
|
scheme = NULL;
|
|
}
|
|
|
|
/* Since it's only used for lookup, we can apply transformations to
|
|
keep the lookup itself simple and fast. First, we convert `https`
|
|
to `http` because they are equal for lookup. Next, we place the
|
|
scheme after the hostname, separated by NUL, to compare the host
|
|
first, since it tends to differ more often. Lastly, we lowercase
|
|
the whole thing since lookup must be case-insensitive. */
|
|
scheme_len = SysStringLen(scheme);
|
|
host_len = SysStringLen(hostname);
|
|
|
|
if(scheme_len == 5 && !wcsicmp(scheme, L"https"))
|
|
scheme_len--;
|
|
|
|
origin = SysAllocStringLen(NULL, host_len + 1 + scheme_len);
|
|
if(origin) {
|
|
WCHAR *p = origin;
|
|
memcpy(p, hostname, host_len * sizeof(WCHAR));
|
|
p += host_len;
|
|
*p = ' '; /* for wcslwr */
|
|
memcpy(p + 1, scheme, scheme_len * sizeof(WCHAR));
|
|
p[1 + scheme_len] = '\0';
|
|
_wcslwr(origin);
|
|
*p = '\0';
|
|
}
|
|
SysFreeString(scheme);
|
|
|
|
if(!origin)
|
|
return E_OUTOFMEMORY;
|
|
|
|
*ret = origin;
|
|
return S_OK;
|
|
}
|
|
|
|
static WCHAR *build_filename(BSTR hostname)
|
|
{
|
|
static const WCHAR store[] = L"\\Microsoft\\Internet Explorer\\DOMStore\\";
|
|
WCHAR path[MAX_PATH], *ret;
|
|
int len;
|
|
|
|
if(!SHGetSpecialFolderPathW(NULL, path, CSIDL_LOCAL_APPDATA, TRUE)) {
|
|
ERR("Can't get folder path %lu\n", GetLastError());
|
|
return NULL;
|
|
}
|
|
|
|
len = wcslen(path);
|
|
if(len + ARRAY_SIZE(store) > ARRAY_SIZE(path)) {
|
|
ERR("Path too long\n");
|
|
return NULL;
|
|
}
|
|
memcpy(path + len, store, sizeof(store));
|
|
|
|
len += ARRAY_SIZE(store);
|
|
ret = malloc((len + wcslen(hostname) + ARRAY_SIZE(L".xml")) * sizeof(WCHAR));
|
|
if(!ret) {
|
|
return NULL;
|
|
}
|
|
|
|
wcscpy(ret, path);
|
|
wcscat(ret, hostname);
|
|
wcscat(ret, L".xml");
|
|
|
|
return ret;
|
|
}
|
|
|
|
static WCHAR *build_mutexname(const WCHAR *filename)
|
|
{
|
|
WCHAR *ret, *ptr;
|
|
ret = wcsdup(filename);
|
|
if(!ret)
|
|
return NULL;
|
|
for(ptr = ret; *ptr; ptr++)
|
|
if(*ptr == '\\') *ptr = '_';
|
|
return ret;
|
|
}
|
|
|
|
HRESULT create_html_storage(HTMLInnerWindow *window, BOOL local, IHTMLStorage **p)
|
|
{
|
|
IUri *uri = window->base.outer_window->uri;
|
|
BSTR origin, hostname = NULL;
|
|
HTMLStorage *storage;
|
|
HRESULT hres;
|
|
|
|
if(!uri)
|
|
return S_FALSE;
|
|
|
|
hres = IUri_GetHost(uri, &hostname);
|
|
if(hres != S_OK) {
|
|
SysFreeString(hostname);
|
|
return hres;
|
|
}
|
|
|
|
storage = calloc(1, sizeof(*storage));
|
|
if(!storage) {
|
|
SysFreeString(hostname);
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
if(local) {
|
|
WCHAR *mutexname;
|
|
storage->filename = build_filename(hostname);
|
|
SysFreeString(hostname);
|
|
if(!storage->filename) {
|
|
free(storage);
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
mutexname = build_mutexname(storage->filename);
|
|
if(!mutexname) {
|
|
free(storage->filename);
|
|
free(storage);
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
storage->mutex = CreateMutexW(NULL, FALSE, mutexname);
|
|
free(mutexname);
|
|
if(!storage->mutex) {
|
|
free(storage->filename);
|
|
free(storage);
|
|
return HRESULT_FROM_WIN32(GetLastError());
|
|
}
|
|
}else {
|
|
hres = build_session_origin(uri, hostname, &origin);
|
|
SysFreeString(hostname);
|
|
if(hres != S_OK) {
|
|
free(storage);
|
|
return hres;
|
|
}
|
|
storage->session_storage = grab_session_map_entry(origin);
|
|
SysFreeString(origin);
|
|
if(!storage->session_storage) {
|
|
free(storage);
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
}
|
|
|
|
storage->IHTMLStorage_iface.lpVtbl = &HTMLStorageVtbl;
|
|
storage->ref = 1;
|
|
storage->window = window;
|
|
|
|
init_dispatch(&storage->dispex, (IUnknown*)&storage->IHTMLStorage_iface, &HTMLStorage_dispex,
|
|
dispex_compat_mode(&window->event_target.dispex));
|
|
|
|
*p = &storage->IHTMLStorage_iface;
|
|
return S_OK;
|
|
}
|
|
|
|
void detach_html_storage(IHTMLStorage *iface)
|
|
{
|
|
HTMLStorage *storage = impl_from_IHTMLStorage(iface);
|
|
storage->window = NULL;
|
|
}
|