1 /* 2 * Copyright 2006-2008 Jacek Caban for CodeWeavers 3 * 4 * This library is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU Lesser General Public 6 * License as published by the Free Software Foundation; either 7 * version 2.1 of the License, or (at your option) any later version. 8 * 9 * This library is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 * Lesser General Public License for more details. 13 * 14 * You should have received a copy of the GNU Lesser General Public 15 * License along with this library; if not, write to the Free Software 16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 17 */ 18 19 #include <stdarg.h> 20 21 #define COBJMACROS 22 23 #include "windef.h" 24 #include "winbase.h" 25 #include "winuser.h" 26 #include "ole2.h" 27 28 #include "wine/debug.h" 29 30 #include "mshtml_private.h" 31 32 WINE_DEFAULT_DEBUG_CHANNEL(mshtml); 33 34 typedef struct { 35 DispatchEx dispex; 36 const IHTMLElementCollectionVtbl *lpHTMLElementCollectionVtbl; 37 38 IUnknown *ref_unk; 39 HTMLElement **elems; 40 DWORD len; 41 42 LONG ref; 43 } HTMLElementCollection; 44 45 #define HTMLELEMCOL(x) ((IHTMLElementCollection*) &(x)->lpHTMLElementCollectionVtbl) 46 47 typedef struct { 48 HTMLElement **buf; 49 DWORD len; 50 DWORD size; 51 } elem_vector_t; 52 53 static IHTMLElementCollection *HTMLElementCollection_Create(IUnknown *ref_unk, 54 HTMLElement **elems, DWORD len); 55 56 static void elem_vector_add(elem_vector_t *buf, HTMLElement *elem) 57 { 58 if(buf->len == buf->size) { 59 buf->size <<= 1; 60 buf->buf = heap_realloc(buf->buf, buf->size*sizeof(HTMLElement**)); 61 } 62 63 buf->buf[buf->len++] = elem; 64 } 65 66 static void elem_vector_normalize(elem_vector_t *buf) 67 { 68 if(!buf->len) { 69 heap_free(buf->buf); 70 buf->buf = NULL; 71 }else if(buf->size > buf->len) { 72 buf->buf = heap_realloc(buf->buf, buf->len*sizeof(HTMLElement**)); 73 } 74 75 buf->size = buf->len; 76 } 77 78 static inline BOOL is_elem_node(nsIDOMNode *node) 79 { 80 PRUint16 type=0; 81 82 nsIDOMNode_GetNodeType(node, &type); 83 84 return type == ELEMENT_NODE || type == COMMENT_NODE; 85 } 86 87 #define ELEMCOL_THIS(iface) DEFINE_THIS(HTMLElementCollection, HTMLElementCollection, iface) 88 #define HTMLELEM_NODE_THIS(iface) DEFINE_THIS2(HTMLElement, node, iface) 89 90 static HRESULT WINAPI HTMLElementCollection_QueryInterface(IHTMLElementCollection *iface, 91 REFIID riid, void **ppv) 92 { 93 HTMLElementCollection *This = ELEMCOL_THIS(iface); 94 95 *ppv = NULL; 96 97 if(IsEqualGUID(&IID_IUnknown, riid)) { 98 TRACE("(%p)->(IID_IUnknown %p)\n", This, ppv); 99 *ppv = HTMLELEMCOL(This); 100 }else if(IsEqualGUID(&IID_IHTMLElementCollection, riid)) { 101 TRACE("(%p)->(IID_IHTMLElementCollection %p)\n", This, ppv); 102 *ppv = HTMLELEMCOL(This); 103 }else if(dispex_query_interface(&This->dispex, riid, ppv)) { 104 return *ppv ? S_OK : E_NOINTERFACE; 105 } 106 107 if(*ppv) { 108 IHTMLElementCollection_AddRef(HTMLELEMCOL(This)); 109 return S_OK; 110 } 111 112 FIXME("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppv); 113 return E_NOINTERFACE; 114 } 115 116 static ULONG WINAPI HTMLElementCollection_AddRef(IHTMLElementCollection *iface) 117 { 118 HTMLElementCollection *This = ELEMCOL_THIS(iface); 119 LONG ref = InterlockedIncrement(&This->ref); 120 121 TRACE("(%p) ref=%d\n", This, ref); 122 123 return ref; 124 } 125 126 static ULONG WINAPI HTMLElementCollection_Release(IHTMLElementCollection *iface) 127 { 128 HTMLElementCollection *This = ELEMCOL_THIS(iface); 129 LONG ref = InterlockedDecrement(&This->ref); 130 131 TRACE("(%p) ref=%d\n", This, ref); 132 133 if(!ref) { 134 IUnknown_Release(This->ref_unk); 135 release_dispex(&This->dispex); 136 heap_free(This->elems); 137 heap_free(This); 138 } 139 140 return ref; 141 } 142 143 static HRESULT WINAPI HTMLElementCollection_GetTypeInfoCount(IHTMLElementCollection *iface, 144 UINT *pctinfo) 145 { 146 HTMLElementCollection *This = ELEMCOL_THIS(iface); 147 return IDispatchEx_GetTypeInfoCount(DISPATCHEX(&This->dispex), pctinfo); 148 } 149 150 static HRESULT WINAPI HTMLElementCollection_GetTypeInfo(IHTMLElementCollection *iface, 151 UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo) 152 { 153 HTMLElementCollection *This = ELEMCOL_THIS(iface); 154 return IDispatchEx_GetTypeInfo(DISPATCHEX(&This->dispex), iTInfo, lcid, ppTInfo); 155 } 156 157 static HRESULT WINAPI HTMLElementCollection_GetIDsOfNames(IHTMLElementCollection *iface, 158 REFIID riid, LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId) 159 { 160 HTMLElementCollection *This = ELEMCOL_THIS(iface); 161 return IDispatchEx_GetIDsOfNames(DISPATCHEX(&This->dispex), riid, rgszNames, cNames, lcid, rgDispId); 162 } 163 164 static HRESULT WINAPI HTMLElementCollection_Invoke(IHTMLElementCollection *iface, 165 DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams, 166 VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr) 167 { 168 HTMLElementCollection *This = ELEMCOL_THIS(iface); 169 return IDispatchEx_Invoke(DISPATCHEX(&This->dispex), dispIdMember, riid, lcid, 170 wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr); 171 } 172 173 static HRESULT WINAPI HTMLElementCollection_toString(IHTMLElementCollection *iface, 174 BSTR *String) 175 { 176 HTMLElementCollection *This = ELEMCOL_THIS(iface); 177 FIXME("(%p)->(%p)\n", This, String); 178 return E_NOTIMPL; 179 } 180 181 static HRESULT WINAPI HTMLElementCollection_put_length(IHTMLElementCollection *iface, 182 LONG v) 183 { 184 HTMLElementCollection *This = ELEMCOL_THIS(iface); 185 FIXME("(%p)->(%d)\n", This, v); 186 return E_NOTIMPL; 187 } 188 189 static HRESULT WINAPI HTMLElementCollection_get_length(IHTMLElementCollection *iface, 190 LONG *p) 191 { 192 HTMLElementCollection *This = ELEMCOL_THIS(iface); 193 194 TRACE("(%p)->(%p)\n", This, p); 195 196 *p = This->len; 197 return S_OK; 198 } 199 200 static HRESULT WINAPI HTMLElementCollection_get__newEnum(IHTMLElementCollection *iface, 201 IUnknown **p) 202 { 203 HTMLElementCollection *This = ELEMCOL_THIS(iface); 204 FIXME("(%p)->(%p)\n", This, p); 205 return E_NOTIMPL; 206 } 207 208 static BOOL is_elem_id(HTMLElement *elem, LPCWSTR name) 209 { 210 BSTR elem_id; 211 HRESULT hres; 212 213 hres = IHTMLElement_get_id(HTMLELEM(elem), &elem_id); 214 if(FAILED(hres)){ 215 WARN("IHTMLElement_get_id failed: 0x%08x\n", hres); 216 return FALSE; 217 } 218 219 if(elem_id && !strcmpW(elem_id, name)) { 220 SysFreeString(elem_id); 221 return TRUE; 222 } 223 224 SysFreeString(elem_id); 225 return FALSE; 226 } 227 228 static BOOL is_elem_name(HTMLElement *elem, LPCWSTR name) 229 { 230 const PRUnichar *str; 231 nsAString nsstr, nsname; 232 BOOL ret = FALSE; 233 nsresult nsres; 234 235 static const PRUnichar nameW[] = {'n','a','m','e',0}; 236 237 if(!elem->nselem) 238 return FALSE; 239 240 nsAString_Init(&nsstr, NULL); 241 nsIDOMHTMLElement_GetId(elem->nselem, &nsstr); 242 nsAString_GetData(&nsstr, &str); 243 if(!strcmpiW(str, name)) { 244 nsAString_Finish(&nsstr); 245 return TRUE; 246 } 247 248 nsAString_Init(&nsname, nameW); 249 nsres = nsIDOMHTMLElement_GetAttribute(elem->nselem, &nsname, &nsstr); 250 nsAString_Finish(&nsname); 251 if(NS_SUCCEEDED(nsres)) { 252 nsAString_GetData(&nsstr, &str); 253 ret = !strcmpiW(str, name); 254 } 255 256 nsAString_Finish(&nsstr); 257 return ret; 258 } 259 260 static HRESULT get_item_idx(HTMLElementCollection *This, UINT idx, IDispatch **ret) 261 { 262 if(idx < This->len) { 263 *ret = (IDispatch*)This->elems[idx]; 264 IDispatch_AddRef(*ret); 265 } 266 267 return S_OK; 268 } 269 270 static HRESULT WINAPI HTMLElementCollection_item(IHTMLElementCollection *iface, 271 VARIANT name, VARIANT index, IDispatch **pdisp) 272 { 273 HTMLElementCollection *This = ELEMCOL_THIS(iface); 274 HRESULT hres = S_OK; 275 276 TRACE("(%p)->(%s %s %p)\n", This, debugstr_variant(&name), debugstr_variant(&index), pdisp); 277 278 *pdisp = NULL; 279 280 switch(V_VT(&name)) { 281 case VT_I4: 282 if(V_I4(&name) < 0) 283 return E_INVALIDARG; 284 hres = get_item_idx(This, V_I4(&name), pdisp); 285 break; 286 287 case VT_UINT: 288 hres = get_item_idx(This, V_UINT(&name), pdisp); 289 break; 290 291 case VT_BSTR: { 292 DWORD i; 293 294 if(V_VT(&index) == VT_I4) { 295 LONG idx = V_I4(&index); 296 297 if(idx < 0) 298 return E_INVALIDARG; 299 300 for(i=0; i<This->len; i++) { 301 if(is_elem_name(This->elems[i], V_BSTR(&name)) && !idx--) 302 break; 303 } 304 305 if(i != This->len) { 306 *pdisp = (IDispatch*)HTMLELEM(This->elems[i]); 307 IDispatch_AddRef(*pdisp); 308 } 309 }else { 310 elem_vector_t buf = {NULL, 0, 8}; 311 312 buf.buf = heap_alloc(buf.size*sizeof(HTMLElement*)); 313 314 for(i=0; i<This->len; i++) { 315 if(is_elem_name(This->elems[i], V_BSTR(&name))) 316 elem_vector_add(&buf, This->elems[i]); 317 } 318 319 if(buf.len > 1) { 320 elem_vector_normalize(&buf); 321 *pdisp = (IDispatch*)HTMLElementCollection_Create(This->ref_unk, buf.buf, buf.len); 322 }else { 323 if(buf.len == 1) { 324 *pdisp = (IDispatch*)HTMLELEM(buf.buf[0]); 325 IDispatch_AddRef(*pdisp); 326 } 327 328 heap_free(buf.buf); 329 } 330 } 331 break; 332 } 333 334 default: 335 FIXME("Unsupported name %s\n", debugstr_variant(&name)); 336 hres = E_NOTIMPL; 337 } 338 339 if(SUCCEEDED(hres)) 340 TRACE("returning %p\n", *pdisp); 341 return hres; 342 } 343 344 static HRESULT WINAPI HTMLElementCollection_tags(IHTMLElementCollection *iface, 345 VARIANT tagName, IDispatch **pdisp) 346 { 347 HTMLElementCollection *This = ELEMCOL_THIS(iface); 348 DWORD i; 349 nsAString tag_str; 350 const PRUnichar *tag; 351 elem_vector_t buf = {NULL, 0, 8}; 352 353 if(V_VT(&tagName) != VT_BSTR) { 354 WARN("Invalid arg\n"); 355 return DISP_E_MEMBERNOTFOUND; 356 } 357 358 TRACE("(%p)->(%s %p)\n", This, debugstr_w(V_BSTR(&tagName)), pdisp); 359 360 buf.buf = heap_alloc(buf.size*sizeof(HTMLElement*)); 361 362 nsAString_Init(&tag_str, NULL); 363 364 for(i=0; i<This->len; i++) { 365 if(!This->elems[i]->nselem) 366 continue; 367 368 nsIDOMElement_GetTagName(This->elems[i]->nselem, &tag_str); 369 nsAString_GetData(&tag_str, &tag); 370 371 if(CompareStringW(LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE, tag, -1, 372 V_BSTR(&tagName), -1) == CSTR_EQUAL) 373 elem_vector_add(&buf, This->elems[i]); 374 } 375 376 nsAString_Finish(&tag_str); 377 elem_vector_normalize(&buf); 378 379 TRACE("fount %d tags\n", buf.len); 380 381 *pdisp = (IDispatch*)HTMLElementCollection_Create(This->ref_unk, buf.buf, buf.len); 382 return S_OK; 383 } 384 385 #define DISPID_ELEMCOL_0 MSHTML_DISPID_CUSTOM_MIN 386 387 static HRESULT HTMLElementCollection_get_dispid(IUnknown *iface, BSTR name, DWORD flags, DISPID *dispid) 388 { 389 HTMLElementCollection *This = ELEMCOL_THIS(iface); 390 WCHAR *ptr; 391 DWORD idx=0; 392 393 if(!*name) 394 return DISP_E_UNKNOWNNAME; 395 396 for(ptr = name; *ptr && isdigitW(*ptr); ptr++) 397 idx = idx*10 + (*ptr-'0'); 398 399 if(*ptr) { 400 /* the name contains alpha characters, so search by name & id */ 401 for(idx = 0; idx < This->len; ++idx) { 402 if(is_elem_id(This->elems[idx], name) || 403 is_elem_name(This->elems[idx], name)) 404 break; 405 } 406 } 407 408 if(idx >= This->len) 409 return DISP_E_UNKNOWNNAME; 410 411 *dispid = DISPID_ELEMCOL_0 + idx; 412 TRACE("ret %x\n", *dispid); 413 return S_OK; 414 } 415 416 static HRESULT HTMLElementCollection_invoke(IUnknown *iface, DISPID id, LCID lcid, WORD flags, DISPPARAMS *params, 417 VARIANT *res, EXCEPINFO *ei, IServiceProvider *caller) 418 { 419 HTMLElementCollection *This = ELEMCOL_THIS(iface); 420 DWORD idx; 421 422 TRACE("(%p)->(%x %x %x %p %p %p %p)\n", This, id, lcid, flags, params, res, ei, caller); 423 424 idx = id - DISPID_ELEMCOL_0; 425 if(idx >= This->len) 426 return DISP_E_UNKNOWNNAME; 427 428 switch(flags) { 429 case DISPATCH_PROPERTYGET: 430 V_VT(res) = VT_DISPATCH; 431 V_DISPATCH(res) = (IDispatch*)HTMLELEM(This->elems[idx]); 432 IHTMLElement_AddRef(HTMLELEM(This->elems[idx])); 433 break; 434 default: 435 FIXME("unimplemented flags %x\n", flags); 436 return E_NOTIMPL; 437 } 438 439 return S_OK; 440 } 441 442 #undef ELEMCOL_THIS 443 444 static const IHTMLElementCollectionVtbl HTMLElementCollectionVtbl = { 445 HTMLElementCollection_QueryInterface, 446 HTMLElementCollection_AddRef, 447 HTMLElementCollection_Release, 448 HTMLElementCollection_GetTypeInfoCount, 449 HTMLElementCollection_GetTypeInfo, 450 HTMLElementCollection_GetIDsOfNames, 451 HTMLElementCollection_Invoke, 452 HTMLElementCollection_toString, 453 HTMLElementCollection_put_length, 454 HTMLElementCollection_get_length, 455 HTMLElementCollection_get__newEnum, 456 HTMLElementCollection_item, 457 HTMLElementCollection_tags 458 }; 459 460 static const dispex_static_data_vtbl_t HTMLElementColection_dispex_vtbl = { 461 NULL, 462 HTMLElementCollection_get_dispid, 463 HTMLElementCollection_invoke 464 }; 465 466 static const tid_t HTMLElementCollection_iface_tids[] = { 467 IHTMLElementCollection_tid, 468 0 469 }; 470 static dispex_static_data_t HTMLElementCollection_dispex = { 471 &HTMLElementColection_dispex_vtbl, 472 DispHTMLElementCollection_tid, 473 NULL, 474 HTMLElementCollection_iface_tids 475 }; 476 477 static void create_all_list(HTMLDocumentNode *doc, HTMLDOMNode *elem, elem_vector_t *buf) 478 { 479 nsIDOMNodeList *nsnode_list; 480 nsIDOMNode *iter; 481 PRUint32 list_len = 0, i; 482 nsresult nsres; 483 484 nsres = nsIDOMNode_GetChildNodes(elem->nsnode, &nsnode_list); 485 if(NS_FAILED(nsres)) { 486 ERR("GetChildNodes failed: %08x\n", nsres); 487 return; 488 } 489 490 nsIDOMNodeList_GetLength(nsnode_list, &list_len); 491 if(!list_len) 492 return; 493 494 for(i=0; i<list_len; i++) { 495 nsres = nsIDOMNodeList_Item(nsnode_list, i, &iter); 496 if(NS_FAILED(nsres)) { 497 ERR("Item failed: %08x\n", nsres); 498 continue; 499 } 500 501 if(is_elem_node(iter)) { 502 HTMLDOMNode *node = get_node(doc, iter, TRUE); 503 504 elem_vector_add(buf, HTMLELEM_NODE_THIS(node)); 505 create_all_list(doc, node, buf); 506 } 507 } 508 } 509 510 IHTMLElementCollection *create_all_collection(HTMLDOMNode *node, BOOL include_root) 511 { 512 elem_vector_t buf = {NULL, 0, 8}; 513 514 buf.buf = heap_alloc(buf.size*sizeof(HTMLElement**)); 515 516 if(include_root) 517 elem_vector_add(&buf, HTMLELEM_NODE_THIS(node)); 518 create_all_list(node->doc, node, &buf); 519 elem_vector_normalize(&buf); 520 521 return HTMLElementCollection_Create((IUnknown*)HTMLDOMNODE(node), buf.buf, buf.len); 522 } 523 524 IHTMLElementCollection *create_collection_from_nodelist(HTMLDocumentNode *doc, IUnknown *unk, nsIDOMNodeList *nslist) 525 { 526 PRUint32 length = 0, i; 527 elem_vector_t buf; 528 529 nsIDOMNodeList_GetLength(nslist, &length); 530 531 buf.len = 0; 532 buf.size = length; 533 if(length) { 534 nsIDOMNode *nsnode; 535 536 buf.buf = heap_alloc(buf.size*sizeof(HTMLElement*)); 537 538 for(i=0; i<length; i++) { 539 nsIDOMNodeList_Item(nslist, i, &nsnode); 540 if(is_elem_node(nsnode)) 541 buf.buf[buf.len++] = HTMLELEM_NODE_THIS(get_node(doc, nsnode, TRUE)); 542 nsIDOMNode_Release(nsnode); 543 } 544 545 elem_vector_normalize(&buf); 546 }else { 547 buf.buf = NULL; 548 } 549 550 return HTMLElementCollection_Create(unk, buf.buf, buf.len); 551 } 552 553 IHTMLElementCollection *create_collection_from_htmlcol(HTMLDocumentNode *doc, IUnknown *unk, nsIDOMHTMLCollection *nscol) 554 { 555 PRUint32 length = 0, i; 556 elem_vector_t buf; 557 558 nsIDOMHTMLCollection_GetLength(nscol, &length); 559 560 buf.len = buf.size = length; 561 if(buf.len) { 562 nsIDOMNode *nsnode; 563 564 buf.buf = heap_alloc(buf.size*sizeof(HTMLElement*)); 565 566 for(i=0; i<length; i++) { 567 nsIDOMHTMLCollection_Item(nscol, i, &nsnode); 568 buf.buf[i] = HTMLELEM_NODE_THIS(get_node(doc, nsnode, TRUE)); 569 nsIDOMNode_Release(nsnode); 570 } 571 }else { 572 buf.buf = NULL; 573 } 574 575 return HTMLElementCollection_Create(unk, buf.buf, buf.len); 576 } 577 578 static IHTMLElementCollection *HTMLElementCollection_Create(IUnknown *ref_unk, 579 HTMLElement **elems, DWORD len) 580 { 581 HTMLElementCollection *ret = heap_alloc_zero(sizeof(HTMLElementCollection)); 582 583 ret->lpHTMLElementCollectionVtbl = &HTMLElementCollectionVtbl; 584 ret->ref = 1; 585 ret->elems = elems; 586 ret->len = len; 587 588 init_dispex(&ret->dispex, (IUnknown*)HTMLELEMCOL(ret), &HTMLElementCollection_dispex); 589 590 IUnknown_AddRef(ref_unk); 591 ret->ref_unk = ref_unk; 592 593 TRACE("ret=%p len=%d\n", ret, len); 594 595 return HTMLELEMCOL(ret); 596 } 597