Package NVDAObjects :: Package IAccessible :: Module MSHTML
[hide private]
[frames] | no frames]

Source Code for Module NVDAObjects.IAccessible.MSHTML

  1  #NVDAObjects/MSHTML.py 
  2  #A part of NonVisual Desktop Access (NVDA) 
  3  #Copyright (C) 2006-2007 NVDA Contributors <http://www.nvda-project.org/> 
  4  #This file is covered by the GNU General Public License. 
  5  #See the file COPYING for more details. 
  6   
  7  import time 
  8  from comtypes import COMError 
  9  import comtypes.client 
 10  import comtypes.automation 
 11  from comtypes import IServiceProvider 
 12  import contextlib 
 13  import winUser 
 14  import oleacc 
 15  import IAccessibleHandler 
 16  import aria 
 17  from keyboardHandler import KeyboardInputGesture 
 18  import api 
 19  import textInfos 
 20  from logHandler import log 
 21  import controlTypes 
 22  from . import IAccessible 
 23  from ..behaviors import EditableTextWithoutAutoSelectDetection 
 24  from .. import InvalidNVDAObject 
 25  from ..window import Window 
 26   
 27  IID_IHTMLElement=comtypes.GUID('{3050F1FF-98B5-11CF-BB82-00AA00BDCE0B}') 
28 29 -class HTMLAttribCache(object):
30
31 - def __init__(self,HTMLNode):
32 self.HTMLNode=HTMLNode 33 self.cache={}
34
35 - def __getitem__(self,item):
36 try: 37 return self.cache[item] 38 except LookupError: 39 pass 40 try: 41 value=self.HTMLNode.getAttribute(item) 42 except (COMError,NameError): 43 value=None 44 self.cache[item]=value 45 return value
46 47 nodeNamesToNVDARoles={ 48 "FRAME":controlTypes.ROLE_FRAME, 49 "IFRAME":controlTypes.ROLE_FRAME, 50 "FRAMESET":controlTypes.ROLE_DOCUMENT, 51 "BODY":controlTypes.ROLE_DOCUMENT, 52 "TH":controlTypes.ROLE_TABLECELL, 53 "IMG":controlTypes.ROLE_GRAPHIC, 54 "A":controlTypes.ROLE_LINK, 55 "LABEL":controlTypes.ROLE_LABEL, 56 "#text":controlTypes.ROLE_STATICTEXT, 57 "H1":controlTypes.ROLE_HEADING, 58 "H2":controlTypes.ROLE_HEADING, 59 "H3":controlTypes.ROLE_HEADING, 60 "H4":controlTypes.ROLE_HEADING, 61 "H5":controlTypes.ROLE_HEADING, 62 "H6":controlTypes.ROLE_HEADING, 63 "DIV":controlTypes.ROLE_SECTION, 64 "P":controlTypes.ROLE_PARAGRAPH, 65 "FORM":controlTypes.ROLE_FORM, 66 "UL":controlTypes.ROLE_LIST, 67 "OL":controlTypes.ROLE_LIST, 68 "DL":controlTypes.ROLE_LIST, 69 "LI":controlTypes.ROLE_LISTITEM, 70 "DD":controlTypes.ROLE_LISTITEM, 71 "DT":controlTypes.ROLE_LISTITEM, 72 "TR":controlTypes.ROLE_TABLEROW, 73 "THEAD":controlTypes.ROLE_TABLEHEADER, 74 "TBODY":controlTypes.ROLE_TABLEBODY, 75 "HR":controlTypes.ROLE_SEPARATOR, 76 "OBJECT":controlTypes.ROLE_EMBEDDEDOBJECT, 77 "APPLET":controlTypes.ROLE_EMBEDDEDOBJECT, 78 "EMBED":controlTypes.ROLE_EMBEDDEDOBJECT, 79 "FIELDSET":controlTypes.ROLE_GROUPING, 80 "OPTION":controlTypes.ROLE_LISTITEM, 81 }
82 83 -def IAccessibleFromHTMLNode(HTMLNode):
84 try: 85 s=HTMLNode.QueryInterface(IServiceProvider) 86 return s.QueryService(oleacc.IAccessible._iid_,oleacc.IAccessible) 87 except COMError: 88 raise NotImplementedError
89
90 -def HTMLNodeFromIAccessible(IAccessibleObject):
91 try: 92 s=IAccessibleObject.QueryInterface(IServiceProvider) 93 i=s.QueryService(IID_IHTMLElement,comtypes.automation.IDispatch) 94 if not i: 95 # QueryService should fail if IHTMLElement is not supported, but some applications misbehave and return a null COM pointer. 96 raise NotImplementedError 97 return comtypes.client.dynamic.Dispatch(i) 98 except COMError: 99 raise NotImplementedError
100
101 -def locateHTMLElementByID(document,ID):
102 try: 103 element=document.getElementsByName(ID).item(0) 104 except COMError as e: 105 log.debugWarning("document.getElementsByName failed with COMError %s"%e) 106 element=None 107 if element: 108 return element 109 try: 110 nodeName=document.body.nodeName 111 except COMError as e: 112 log.debugWarning("document.body.nodeName failed with COMError %s"%e) 113 return None 114 if nodeName=="FRAMESET": 115 tag="frame" 116 else: 117 tag="iframe" 118 try: 119 frames=document.getElementsByTagName(tag) 120 except COMError as e: 121 log.debugWarning("document.getElementsByTagName failed with COMError %s"%e) 122 return None 123 for frame in frames: 124 try: 125 pacc=IAccessibleFromHTMLNode(frame) 126 except NotImplementedError: 127 # #1569: It's not possible to get an IAccessible from frames marked with an ARIA role of presentation. 128 # In this case, just skip this frame. 129 continue 130 res=IAccessibleHandler.accChild(pacc,1) 131 if not res: continue 132 childElement=HTMLNodeFromIAccessible(res[0]) 133 if not childElement: continue 134 childElement=locateHTMLElementByID(childElement.document,ID) 135 if not childElement: continue 136 return childElement
137
138 -class MSHTMLTextInfo(textInfos.TextInfo):
139
140 - def _expandToLine(self,textRange):
141 #Try to calculate the line range by finding screen coordinates and using moveToPoint 142 parent=textRange.parentElement() 143 if not parent.isMultiline: #fastest solution for single line edits (<input type="text">) 144 textRange.expand("textEdit") 145 return 146 parentRect=parent.getBoundingClientRect() 147 #This can be simplified when comtypes is fixed 148 lineTop=comtypes.client.dynamic._Dispatch(textRange._comobj).offsetTop 149 lineLeft=parentRect.left+parent.clientLeft 150 #editable documents have a different right most boundary to <textarea> elements. 151 if self.obj.HTMLNode.document.body.isContentEditable: 152 lineRight=parentRect.right 153 else: 154 lineRight=parentRect.left+parent.clientWidth 155 tempRange=textRange.duplicate() 156 try: 157 tempRange.moveToPoint(lineLeft,lineTop) 158 textRange.setEndPoint("startToStart",tempRange) 159 tempRange.moveToPoint(lineRight,lineTop) 160 textRange.setEndPoint("endToStart",tempRange) 161 return 162 except COMError: 163 pass 164 #MoveToPoint fails on Some (possibly floated) textArea elements. 165 #Instead use the physical selection, by moving it with key presses, to work out the line. 166 #This approach is somewhat slower, and less accurate. 167 with self.obj.suspendCaretEvents(): 168 selObj=parent.document.selection 169 oldSelRange=selObj.createRange().duplicate() 170 # #1566: Calling textRange.select() sometimes throws focus onto the document, 171 # so create a new range from the selection and move the selection using that. 172 selObj.createRange().moveToBookmark(textRange.getBookmark()) 173 KeyboardInputGesture.fromName("home").send() 174 api.processPendingEvents(False) 175 newSelStartMark=selObj.createRange().getBookmark() 176 KeyboardInputGesture.fromName("end").send() 177 api.processPendingEvents(False) 178 newSelEndMark=selObj.createRange().getBookmark() 179 tempRange.moveToBookmark(newSelStartMark) 180 textRange.setEndPoint("startToStart",tempRange) 181 tempRange.moveToBookmark(newSelEndMark) 182 textRange.setEndPoint("endToStart",tempRange) 183 oldSelRange.select()
184
185 - def __init__(self,obj,position,_rangeObj=None):
186 super(MSHTMLTextInfo,self).__init__(obj,position) 187 if _rangeObj: 188 self._rangeObj=_rangeObj.duplicate() 189 return 190 try: 191 editableBody=self.obj.HTMLNodeName=="BODY" and self.obj.isContentEditable 192 except: 193 editableBody=False 194 if editableBody: 195 self._rangeObj=self.obj.HTMLNode.document.selection.createRange() 196 else: 197 self._rangeObj=self.obj.HTMLNode.createTextRange() 198 if position in (textInfos.POSITION_CARET,textInfos.POSITION_SELECTION): 199 activeElement=self.obj.HTMLNode.document.activeElement 200 if not activeElement or self.obj.HTMLNode.uniqueNumber!=activeElement.uniqueNumber: 201 raise RuntimeError("Only works with currently selected element") 202 if not editableBody: 203 mark=self.obj.HTMLNode.document.selection.createRange().GetBookmark() 204 self._rangeObj.MoveToBookmark(mark) 205 if position==textInfos.POSITION_CARET: 206 self._rangeObj.collapse() 207 return 208 if position==textInfos.POSITION_FIRST: 209 self._rangeObj.collapse() 210 elif position==textInfos.POSITION_LAST: 211 self._rangeObj.expand("textedit") 212 self.collapse(True) 213 self._rangeObj.move("character",-1) 214 elif position==textInfos.POSITION_ALL: 215 self._rangeObj.expand("textedit") 216 elif isinstance(position,textInfos.Bookmark): 217 if position.infoClass==self.__class__: 218 self._rangeObj.moveToBookmark(position.data) 219 else: 220 raise TypeError("Bookmark was for %s type, not for %s type"%(position.infoClass.__name__,self.__class__.__name__)) 221 else: 222 raise NotImplementedError("position: %s"%position)
223
224 - def expand(self,unit):
225 if unit==textInfos.UNIT_PARAGRAPH: 226 unit=textInfos.UNIT_LINE 227 if unit==textInfos.UNIT_LINE and self.basePosition not in [textInfos.POSITION_SELECTION,textInfos.POSITION_CARET]: 228 unit=textInfos.UNIT_SENTENCE 229 if unit==textInfos.UNIT_READINGCHUNK: 230 unit=textInfos.UNIT_SENTENCE 231 if unit==textInfos.UNIT_CHARACTER: 232 self._rangeObj.expand("character") 233 elif unit==textInfos.UNIT_WORD: 234 #Expand to word at the start of a control is broken in MSHTML 235 #Unless we expand to character first. 236 self._rangeObj.expand("character") 237 self._rangeObj.expand("word") 238 elif unit==textInfos.UNIT_SENTENCE: 239 self._rangeObj.expand("sentence") 240 elif unit==textInfos.UNIT_LINE: 241 self._expandToLine(self._rangeObj) 242 elif unit==textInfos.UNIT_STORY: 243 self._rangeObj.expand("textedit") 244 else: 245 raise NotImplementedError("unit: %s"%unit)
246
247 - def _get_isCollapsed(self):
248 if self._rangeObj.compareEndPoints("startToEnd",self._rangeObj)==0: 249 return True 250 else: 251 return False
252
253 - def collapse(self,end=False):
254 self._rangeObj.collapse(not end)
255
256 - def copy(self):
257 return self.__class__(self.obj,None,_rangeObj=self._rangeObj.duplicate())
258
259 - def compareEndPoints(self,other,which):
260 return self._rangeObj.compareEndPoints(which,other._rangeObj)
261
262 - def setEndPoint(self,other,which):
263 self._rangeObj.setEndPoint(which,other._rangeObj)
264
265 - def _get_text(self):
266 text=self._rangeObj.text 267 if not text: 268 text=u"" 269 if controlTypes.STATE_PROTECTED in self.obj.states: 270 text=u'*'*len(text) 271 return text
272
273 - def move(self,unit,direction, endPoint=None):
274 if unit in [textInfos.UNIT_READINGCHUNK,textInfos.UNIT_LINE]: 275 unit=textInfos.UNIT_SENTENCE 276 if unit==textInfos.UNIT_STORY: 277 unit="textedit" 278 if endPoint=="start": 279 moveFunc=self._rangeObj.moveStart 280 elif endPoint=="end": 281 moveFunc=self._rangeObj.moveEnd 282 else: 283 moveFunc=self._rangeObj.move 284 if direction<0: 285 # #1605: If at the end of a line, moving back seems to land on a blank unit, 286 # which breaks backspacing. 287 # Expanding first seems to fix this. 288 self._rangeObj.expand("character") 289 res=moveFunc(unit,direction) 290 return res
291
292 - def updateCaret(self):
293 self._rangeObj.select()
294
295 - def updateSelection(self):
296 self._rangeObj.select()
297
298 - def _get_bookmark(self):
299 return textInfos.Bookmark(self.__class__,self._rangeObj.getBookmark())
300
301 -class MSHTML(IAccessible):
302 303 HTMLNodeNameNavSkipList=['#comment','SCRIPT','HEAD','HTML','PARAM','STYLE'] 304 HTMLNodeNameEmbedList=['OBJECT','EMBED','APPLET','FRAME','IFRAME'] 305 306 _ignoreCaretEvents=False #:Set to true when moving the caret to calculate lines, event_caret will be disabled. 307 308 @contextlib.contextmanager
309 - def suspendCaretEvents(self):
310 """Suspends caret events while you need to move the caret to calculate things.""" 311 oldVal=self._ignoreCaretEvents 312 self._ignoreCaretEvents=True 313 yield oldVal 314 self._ignoreCaretEvents=oldVal
315
316 - def event_caret(self):
317 if self._ignoreCaretEvents: return 318 if self.TextInfo is not MSHTMLTextInfo: 319 return 320 try: 321 newCaretBookmark=self.makeTextInfo(textInfos.POSITION_CARET).bookmark 322 except RuntimeError: #caret events can be fired on the object (focus) when its not the real MSHTML selection 323 newCaretBookmark=None 324 if not newCaretBookmark or newCaretBookmark==getattr(self,'_oldCaretBookmark',None): 325 return 326 self._oldCaretBookmark=newCaretBookmark 327 return super(MSHTML,self).event_caret()
328 329 @classmethod
330 - def kwargsFromSuper(cls,kwargs,relation=None):
331 IAccessibleObject=kwargs['IAccessibleObject'] 332 HTMLNode=None 333 try: 334 HTMLNode=HTMLNodeFromIAccessible(IAccessibleObject) 335 except NotImplementedError: 336 pass 337 if not HTMLNode: 338 return False 339 340 if relation=="focus": 341 try: 342 HTMLNode=HTMLNode.document.activeElement 343 # The IAccessibleObject may be incorrect now, so let the constructor recalculate it. 344 del kwargs['IAccessibleObject'] 345 except: 346 log.exception("Error getting activeElement") 347 348 kwargs['HTMLNode']=HTMLNode 349 return True
350
351 - def findOverlayClasses(self,clsList):
352 if self.TextInfo == MSHTMLTextInfo: 353 clsList.append(EditableTextWithoutAutoSelectDetection) 354 nodeName = self.HTMLNodeName 355 if nodeName: 356 if nodeName=="SELECT" and self.windowStyle&winUser.WS_POPUP: 357 clsList.append(PopupList) 358 elif nodeNamesToNVDARoles.get(nodeName) == controlTypes.ROLE_DOCUMENT: 359 clsList.append(Body) 360 elif nodeName == "OBJECT": 361 clsList.append(Object) 362 elif nodeName=="FIELDSET": 363 clsList.append(Fieldset) 364 clsList.append(MSHTML) 365 if not self.HTMLNodeHasAncestorIAccessible: 366 # The IAccessibleObject is for this node (not an ancestor), so IAccessible overlay classes are relevant. 367 super(MSHTML,self).findOverlayClasses(clsList)
368
370 if self.role==controlTypes.ROLE_DOCUMENT and not self.isContentEditable: 371 import virtualBuffers.MSHTML 372 return virtualBuffers.MSHTML.MSHTML 373 return super(MSHTML,self).treeInterceptorClass
374
375 - def _get_HTMLAttributes(self):
376 return HTMLAttribCache(self.HTMLNode)
377
378 - def __init__(self,HTMLNode=None,IAccessibleObject=None,IAccessibleChildID=None,**kwargs):
379 self.HTMLNodeHasAncestorIAccessible=False 380 if not IAccessibleObject: 381 # Find an IAccessible for HTMLNode and determine whether it is for an ancestor. 382 tempNode=HTMLNode 383 while tempNode: 384 try: 385 IAccessibleObject=IAccessibleFromHTMLNode(tempNode) 386 except NotImplementedError: 387 IAccessibleObject=None 388 if IAccessibleObject: 389 IAccessibleChildID=0 390 if tempNode is not HTMLNode: 391 self.HTMLNodeHasAncestorIAccessible=True 392 break 393 try: 394 tempNode=tempNode.parentNode 395 except COMError: 396 tempNode=None 397 398 if not IAccessibleObject: 399 raise InvalidNVDAObject("Couldn't get IAccessible, probably dead object") 400 401 super(MSHTML,self).__init__(IAccessibleObject=IAccessibleObject,IAccessibleChildID=IAccessibleChildID,**kwargs) 402 self.HTMLNode=HTMLNode 403 404 #object and embed nodes give back an incorrect IAccessible via queryService, so we must treet it as an ancestor IAccessible 405 if self.HTMLNodeName in ("OBJECT","EMBED"): 406 self.HTMLNodeHasAncestorIAccessible=True
407
408 - def _get_TextInfo(self):
409 if not hasattr(self,'_HTMLNodeSupportsTextRanges'): 410 try: 411 self.HTMLNode.createTextRange() 412 self._HTMLNodeSupportsTextRanges=True 413 except (COMError,NameError): 414 self._HTMLNodeSupportsTextRanges=False 415 if self._HTMLNodeSupportsTextRanges: 416 return MSHTMLTextInfo 417 return super(MSHTML,self).TextInfo
418
419 - def isDuplicateIAccessibleEvent(self,obj):
420 if not super(MSHTML,self).isDuplicateIAccessibleEvent(obj): 421 return False 422 #MSHTML winEvents can't be trusted for uniqueness, so just do normal object comparison. 423 return self==obj
424 425
426 - def _isEqual(self, other):
427 if self.HTMLNode and other.HTMLNode: 428 try: 429 return self.windowHandle == other.windowHandle and self.HTMLNodeUniqueNumber == other.HTMLNodeUniqueNumber 430 except (COMError,NameError): 431 pass 432 return super(MSHTML, self)._isEqual(other)
433
434 - def _get_presentationType(self):
435 presType=super(MSHTML,self).presentationType 436 if presType==self.presType_content and self.HTMLAttributes['role']=="presentation": 437 presType=self.presType_layout 438 if presType==self.presType_content and self.role in (controlTypes.ROLE_TABLECELL,controlTypes.ROLE_TABLEROW,controlTypes.ROLE_TABLE,controlTypes.ROLE_TABLEBODY) and self.treeInterceptor and self.treeInterceptor.isNVDAObjectPartOfLayoutTable(self): 439 presType=self.presType_layout 440 return presType
441 442
444 ariaRole=self.HTMLAttributes['aria-role'] 445 if ariaRole=="gridcell": 446 return True 447 return super(MSHTML,self).shouldAllowIAccessibleFocusEvent
448
449 - def _get_name(self):
450 ariaLabel=self.HTMLAttributes['aria-label'] 451 if ariaLabel: 452 return ariaLabel 453 ariaLabelledBy=self.HTMLAttributes['aria-labelledBy'] 454 if ariaLabelledBy: 455 try: 456 labelNode=self.HTMLNode.document.getElementById(ariaLabelledBy) 457 except (COMError,NameError): 458 labelNode=None 459 if labelNode: 460 try: 461 return labelNode.innerText 462 except (COMError,NameError): 463 pass 464 title=self.HTMLAttributes['title'] 465 if title: 466 return title 467 if self.IAccessibleRole==oleacc.ROLE_SYSTEM_TABLE: 468 summary=self.HTMLAttributes['summary'] 469 if summary: 470 return summary 471 if self.HTMLNodeHasAncestorIAccessible: 472 return "" 473 #IE inappropriately generates the name from descendants on some controls 474 if self.IAccessibleRole in (oleacc.ROLE_SYSTEM_MENUBAR,oleacc.ROLE_SYSTEM_TOOLBAR,oleacc.ROLE_SYSTEM_LIST,oleacc.ROLE_SYSTEM_TABLE,oleacc.ROLE_SYSTEM_DOCUMENT): 475 return "" 476 return super(MSHTML,self).name
477
478 - def _get_value(self):
479 if self.HTMLNodeHasAncestorIAccessible: 480 try: 481 value=self.HTMLNode.data 482 except (COMError,NameError): 483 value="" 484 return value 485 IARole=self.IAccessibleRole 486 if IARole in (oleacc.ROLE_SYSTEM_PANE,oleacc.ROLE_SYSTEM_TEXT): 487 return "" 488 else: 489 return super(MSHTML,self).value
490
491 - def _get_description(self):
492 ariaDescribedBy=self.HTMLAttributes['aria-describedBy'] 493 if ariaDescribedBy: 494 try: 495 descNode=self.HTMLNode.document.getElementById(ariaDescribedBy) 496 except (COMError,NameError): 497 descNode=None 498 if descNode: 499 try: 500 return descNode.innerText 501 except (COMError,NameError): 502 pass 503 if self.HTMLNodeHasAncestorIAccessible: 504 return "" 505 return super(MSHTML,self).description
506
507 - def _get_basicText(self):
508 if self.HTMLNode: 509 try: 510 return self.HTMLNode.data or "" 511 except (COMError, AttributeError, NameError): 512 pass 513 try: 514 return self.HTMLNode.innerText or "" 515 except (COMError, AttributeError, NameError): 516 pass 517 return super(MSHTML,self).basicText
518
519 - def _get_role(self):
520 if self.HTMLNode: 521 ariaRole=self.HTMLAttributes['role'] 522 if ariaRole: 523 role=aria.ariaRolesToNVDARoles.get(ariaRole) 524 if role: 525 return role 526 nodeName=self.HTMLNodeName 527 if nodeName: 528 if nodeName in ("OBJECT","EMBED","APPLET"): 529 return controlTypes.ROLE_EMBEDDEDOBJECT 530 if self.HTMLNodeHasAncestorIAccessible or nodeName in ("BODY","FRAMESET","FRAME","IFRAME","LABEL"): 531 return nodeNamesToNVDARoles.get(nodeName,controlTypes.ROLE_TEXTFRAME) 532 if self.IAccessibleChildID>0: 533 states=super(MSHTML,self).states 534 if controlTypes.STATE_LINKED in states: 535 return controlTypes.ROLE_LINK 536 return super(MSHTML,self).role
537
538 - def _get_states(self):
539 if not self.HTMLNodeHasAncestorIAccessible: 540 states=super(MSHTML,self).states 541 else: 542 states=set() 543 ariaSort=self.HTMLAttributes['aria-sort'] 544 state=aria.ariaSortValuesToNVDAStates.get(ariaSort) 545 if state is not None: 546 states.add(state) 547 ariaRequired=self.HTMLAttributes['aria-required'] 548 if ariaRequired=="true": 549 states.add(controlTypes.STATE_REQUIRED) 550 ariaSelected=self.HTMLAttributes['aria-selected'] 551 if ariaSelected=="true": 552 states.add(controlTypes.STATE_SELECTED) 553 elif ariaSelected=="false": 554 states.discard(controlTypes.STATE_SELECTED) 555 ariaExpanded=self.HTMLAttributes['aria-expanded'] 556 if ariaExpanded=="true": 557 states.add(controlTypes.STATE_EXPANDED) 558 elif ariaExpanded=="false": 559 states.add(controlTypes.STATE_COLLAPSED) 560 ariaInvalid=self.HTMLAttributes['aria-invalid'] 561 if ariaInvalid=="true": 562 states.add(controlTypes.STATE_INVALID) 563 ariaGrabbed=self.HTMLAttributes['aria-grabbed'] 564 if ariaGrabbed=="true": 565 states.add(controlTypes.STATE_DRAGGING) 566 elif ariaGrabbed=="false": 567 states.add(controlTypes.STATE_DRAGGABLE) 568 ariaDropeffect=self.HTMLAttributes['aria-dropeffect'] 569 if ariaDropeffect and ariaDropeffect!="none": 570 states.add(controlTypes.STATE_DROPTARGET) 571 if self.isContentEditable: 572 states.add(controlTypes.STATE_EDITABLE) 573 states.discard(controlTypes.STATE_READONLY) 574 nodeName=self.HTMLNodeName 575 if nodeName=="TEXTAREA": 576 states.add(controlTypes.STATE_MULTILINE) 577 return states
578
579 - def _get_isContentEditable(self):
580 try: 581 return bool(self.HTMLNode.isContentEditable) 582 except: 583 return False
584
585 - def _get_parent(self):
586 if self.HTMLNode: 587 try: 588 parentNode=self.HTMLNode.parentElement 589 except (COMError,NameError): 590 parentNode=None 591 if not parentNode and self.HTMLNodeHasAncestorIAccessible: 592 try: 593 parentNode=self.HTMLNode.parentNode 594 except (COMError,NameError): 595 parentNode=None 596 if parentNode: 597 obj=MSHTML(HTMLNode=parentNode) 598 if obj and obj.HTMLNodeName not in self.HTMLNodeNameNavSkipList: 599 return obj 600 return super(MSHTML,self).parent
601
602 - def _get_previous(self):
603 if self.HTMLNode: 604 try: 605 previousNode=self.HTMLNode.previousSibling 606 except COMError: 607 previousNode=None 608 if not previousNode: 609 return None 610 obj=MSHTML(HTMLNode=previousNode) 611 if obj and obj.HTMLNodeName in self.HTMLNodeNameNavSkipList: 612 obj=obj.previous 613 return obj 614 return super(MSHTML,self).previous
615
616 - def _get_next(self):
617 if self.HTMLNode: 618 try: 619 nextNode=self.HTMLNode.nextSibling 620 except COMError: 621 nextNode=None 622 if not nextNode: 623 return None 624 obj=MSHTML(HTMLNode=nextNode) 625 if obj and obj.HTMLNodeName in self.HTMLNodeNameNavSkipList: 626 obj=obj.next 627 return obj 628 return super(MSHTML,self).next
629
630 - def _get_firstChild(self):
631 if self.HTMLNode: 632 if self.HTMLNodeName in ("FRAME","IFRAME"): 633 return super(MSHTML,self).firstChild 634 try: 635 childNode=self.HTMLNode.firstChild 636 except COMError: 637 childNode=None 638 if not childNode: 639 return None 640 obj=MSHTML(HTMLNode=childNode) 641 if obj and obj.HTMLNodeName in self.HTMLNodeNameNavSkipList: 642 return obj.next 643 return obj 644 if self.HTMLNodeHasAncestorIAccessible: 645 return None 646 return super(MSHTML,self).firstChild
647
648 - def _get_lastChild(self):
649 if self.HTMLNode: 650 if self.HTMLNodeName in ("FRAME","IFRAME"): 651 return super(MSHTML,self).lastChild 652 try: 653 childNode=self.HTMLNode.lastChild 654 except COMError: 655 childNode=None 656 if not childNode: 657 return None 658 obj=MSHTML(HTMLNode=childNode) 659 if obj and obj.HTMLNodeName in self.HTMLNodeNameNavSkipList: 660 return obj.previous 661 return obj 662 if self.HTMLNodeHasAncestorIAccessible: 663 return None 664 return super(MSHTML,self).lastChild
665
666 - def _get_columnNumber(self):
667 if not self.role==controlTypes.ROLE_TABLECELL or not self.HTMLNode: 668 raise NotImplementedError 669 try: 670 return self.HTMLNode.cellIndex+1 671 except: 672 raise NotImplementedError
673
674 - def _get_rowNumber(self):
675 if not self.role==controlTypes.ROLE_TABLECELL or not self.HTMLNode: 676 raise NotImplementedError 677 HTMLNode=self.HTMLNode 678 while HTMLNode: 679 try: 680 return HTMLNode.rowIndex+1 681 except: 682 pass 683 HTMLNode=HTMLNode.parentNode 684 raise NotImplementedError
685
686 - def _get_rowCount(self):
687 if self.role!=controlTypes.ROLE_TABLE or not self.HTMLNode: 688 raise NotImplementedError 689 try: 690 return len([x for x in self.HTMLNode.rows]) 691 except: 692 raise NotImplementedError
693
694 - def scrollIntoView(self):
695 if not self.HTMLNode: 696 return 697 try: 698 self.HTMLNode.scrollInToView() 699 except (COMError,NameError): 700 pass
701
702 - def doAction(self, index=None):
703 if self.HTMLNode: 704 try: 705 self.HTMLNode.click() 706 return 707 except COMError: 708 return 709 except NameError: 710 pass 711 super(MSHTML,self).doAction(index=index)
712
713 - def setFocus(self):
714 if self.HTMLNodeHasAncestorIAccessible: 715 try: 716 self.HTMLNode.focus() 717 except (COMError, AttributeError, NameError): 718 pass 719 return 720 super(MSHTML,self).setFocus()
721
722 - def _get_table(self):
723 if self.role not in (controlTypes.ROLE_TABLECELL,controlTypes.ROLE_TABLEROW) or not self.HTMLNode: 724 raise NotImplementedError 725 HTMLNode=self.HTMLNode 726 while HTMLNode: 727 if HTMLNode.nodeName=="TABLE": return MSHTML(HTMLNode=HTMLNode) 728 HTMLNode=HTMLNode.parentNode 729 raise NotImplementedError
730
732 if not hasattr(self,'_HTMLNodeUniqueNumber'): 733 self._HTMLNodeUniqueNumber=self.HTMLNode.uniqueNumber 734 return self._HTMLNodeUniqueNumber
735
736 - def _get_HTMLNodeName(self):
737 if not hasattr(self,'_HTMLNodeName'): 738 try: 739 self._HTMLNodeName=self.HTMLNode.nodeName 740 except (COMError,NameError): 741 return "" 742 return self._HTMLNodeName
743
744 -class V6ComboBox(IAccessible):
745 """The object which receives value change events for combo boxes in MSHTML/IE 6. 746 """ 747
748 - def event_valueChange(self):
749 focus = api.getFocusObject() 750 if controlTypes.STATE_FOCUSED not in self.states or focus.role != controlTypes.ROLE_COMBOBOX: 751 # This combo box is not focused. 752 return super(V6ComboBox, self).event_valueChange() 753 # This combo box is focused. However, the value change is not fired on the real focus object. 754 # Therefore, redirect this event to the real focus object. 755 focus.event_valueChange()
756
757 -class Fieldset(MSHTML):
758
759 - def _get_name(self):
760 try: 761 child=self.HTMLNode.children[0] 762 except (COMError,NameError): 763 child=None 764 if not child: 765 return super(Fieldset,self).name 766 try: 767 nodeName=child.nodeName 768 except (COMError,NameError): 769 return super(Fieldset,self).name 770 if nodeName!="LEGEND": 771 return super(Fieldset,self).name 772 try: 773 text=child.innerText 774 except (COMError,NameError): 775 return super(Fieldset,self).name 776 return text
777
778 -class Body(MSHTML):
779
780 - def _get_parent(self):
781 # The parent of the body accessible is an irrelevant client object (description: MSAAHTML Registered Handler). 782 # This object isn't returned when requesting OBJID_CLIENT, nor is it returned as a child of its parent. 783 # Therefore, eliminate it from the ancestry completely. 784 parent = super(Body, self).parent 785 if parent: 786 return parent.parent 787 else: 788 return parent
789
791 # We must override this because we override parent to skip the MSAAHTML Registered Handler client, 792 # which might have the focused state. 793 if controlTypes.STATE_FOCUSED in self.states: 794 return True 795 parent = super(Body, self).parent 796 if not parent: 797 return False 798 return parent.shouldAllowIAccessibleFocusEvent
799
800 -class Object(MSHTML):
801
802 - def _get_firstChild(self):
803 # We want firstChild to return the accessible for the embedded object. 804 from objidl import IOleWindow 805 # Try to get the window for the embedded object. 806 try: 807 window = self.HTMLNode.object.QueryInterface(IOleWindow).GetWindow() 808 except COMError: 809 window = None 810 if not window or window == self.windowHandle: 811 return super(Object, self).firstChild 812 return Window(windowHandle=window)
813
814 -class PluginWindow(IAccessible):
815 """A window for a plugin. 816 """ 817 818 # MSHTML fires focus on this window after the plugin may already have fired a focus event. 819 # We don't want this to override the focus event fired by the plugin. 820 shouldAllowIAccessibleFocusEvent = False
821
822 -class PopupList(MSHTML):
823 """ 824 Temporary popup lists created when expanding a combo box have a correct accParent which points back to the combobox, so use that. The parentElement points to a temporary document fragment which is not useful. 825 """ 826
827 - def _get_parent(self):
828 return super(MSHTML,self).parent
829
830 -class RootClient(IAccessible):
831 """The top level client of an MSHTML control. 832 """ 833 834 # Get rid of the URL. 835 name = None 836 # Get rid of "MSAAHTML Registered Handler". 837 description = None
838
839 -def findExtraIAccessibleOverlayClasses(obj, clsList):
840 """Determine the most appropriate class for MSHTML objects. 841 This works similarly to L{NVDAObjects.NVDAObject.findOverlayClasses} except that it never calls any other findOverlayClasses method. 842 """ 843 windowClass = obj.windowClassName 844 iaRole = obj.IAccessibleRole 845 if windowClass == "Internet Explorer_TridentCmboBx" and iaRole == oleacc.ROLE_SYSTEM_COMBOBOX: 846 clsList.append(V6ComboBox) 847 return 848 849 if windowClass != "Internet Explorer_Server": 850 return 851 852 if iaRole == oleacc.ROLE_SYSTEM_WINDOW and obj.event_objectID > 0: 853 clsList.append(PluginWindow) 854 elif iaRole == oleacc.ROLE_SYSTEM_CLIENT and obj.event_objectID == winUser.OBJID_CLIENT: 855 clsList.append(RootClient)
856