lib/watir/ie-class.rb in watir-1.6.5 vs lib/watir/ie-class.rb in watir-1.6.6.rc1
- old
+ new
@@ -1,1009 +1,1006 @@
-module Watir
- class IE
- include Watir::Exception
- include Container
- include PageContainer
-
- # Maximum number of seconds to wait when attaching to a window
- @@attach_timeout = 2.0 # default value
- def self.attach_timeout
- @@attach_timeout
- end
- def self.attach_timeout=(timeout)
- @@attach_timeout = timeout
- end
-
- # Return the options used when creating new instances of IE.
- # BUG: this interface invites misunderstanding/misuse such as IE.options[:speed] = :zippy]
- def self.options
- {:speed => self.speed, :visible => self.visible, :attach_timeout => self.attach_timeout}
- end
- # set values for options used when creating new instances of IE.
- def self.set_options options
- options.each do |name, value|
- send "#{name}=", value
- end
- end
- # The globals $FAST_SPEED and $HIDE_IE are checked both at initialization
- # and later, because they
- # might be set after initialization. Setting them beforehand (e.g. from
- # the command line) will affect the class, otherwise it is only a temporary
- # effect
- @@speed = $FAST_SPEED ? :fast : :slow
- def self.speed
- return :fast if $FAST_SPEED
- @@speed
- end
- def self.speed= x
- $FAST_SPEED = nil
- @@speed = x
- end
- @@visible = $HIDE_IE ? false : true
- def self.visible
- return false if $HIDE_IE
- @@visible
- end
- def self.visible= x
- $HIDE_IE = nil
- @@visible = x
- end
-
- # Used internally to determine when IE has finished loading a page
- READYSTATE_COMPLETE = 4
-
- # The default color for highlighting objects as they are accessed.
- HIGHLIGHT_COLOR = 'yellow'
-
- # IE inserts some element whose tagName is empty and just acts as block level element
- # Probably some IE method of cleaning things
- # To pass the same to the xml parser we need to give some name to empty tagName
- EMPTY_TAG_NAME = "DUMMY"
-
- # The time, in seconds, it took for the new page to load after executing the
- # the last command
- attr_reader :down_load_time
-
- # the OLE Internet Explorer object
- attr_accessor :ie
-
- # access to the logger object
- attr_accessor :logger
-
- # this contains the list of unique urls that have been visited
- attr_reader :url_list
-
- # Create a new IE window. Works just like IE.new in Watir 1.4.
- def self.new_window
- ie = new true
- ie._new_window_init
- ie
- end
-
- # Create an IE browser.
- def initialize suppress_new_window=nil
- _new_window_init unless suppress_new_window
- end
-
- def _new_window_init
- create_browser_window
- initialize_options
- goto 'about:blank' # this avoids numerous problems caused by lack of a document
- end
-
- # Create a new IE Window, starting at the specified url.
- # If no url is given, start empty.
- def self.start url=nil
- start_window url
- end
-
- # Create a new IE window, starting at the specified url.
- # If no url is given, start empty. Works like IE.start in Watir 1.4.
- def self.start_window url=nil
- ie = new_window
- ie.goto url if url
- ie
- end
-
- # Create a new IE window in a new process.
- # This method will not work when
- # Watir/Ruby is run under a service (instead of a user).
- def self.new_process
- ie = new true
- ie._new_process_init
- ie
- end
-
- def _new_process_init
- iep = Process.start
- @ie = iep.window
- @process_id = iep.process_id
- initialize_options
- goto 'about:blank'
- end
-
- # Create a new IE window in a new process, starting at the specified URL.
- # Same as IE.start.
- def self.start_process url=nil
- ie = new_process
- ie.goto url if url
- ie
- end
-
- # Return a Watir::IE object for an existing IE window. Window can be
- # referenced by url, title, or window handle.
- # Second argument can be either a string or a regular expression in the
- # case of of :url or :title.
- # IE.attach(:url, 'http://www.google.com')
- # IE.attach(:title, 'Google')
- # IE.attach(:hwnd, 528140)
- # This method will not work when
- # Watir/Ruby is run under a service (instead of a user).
- def self.attach how, what
- ie = new true # don't create window
- ie._attach_init(how, what)
- ie
- end
-
- # this method is used internally to attach to an existing window
- def _attach_init how, what
- attach_browser_window how, what
- initialize_options
- wait
- end
-
- # Return an IE object that wraps the given window, typically obtained from
- # Shell.Application.windows.
- def self.bind window
- ie = new true
- ie.ie = window
- ie.initialize_options
- ie
- end
-
- def create_browser_window
- @ie = WIN32OLE.new('InternetExplorer.Application')
- end
- private :create_browser_window
-
- def initialize_options
- self.visible = IE.visible
- self.speed = IE.speed
-
- @ole_object = nil
- @page_container = self
- @error_checkers = []
- @activeObjectHighLightColor = HIGHLIGHT_COLOR
-
-
- @logger = DefaultLogger.new
- @url_list = []
- end
-
- # Specifies the speed that commands will be executed at. Choices are:
- # * :slow (default)
- # * :fast
- # * :zippy
- # With IE#speed= :zippy, text fields will be entered at once, instead of
- # character by character (default).
- def speed= how_fast
- case how_fast
- when :zippy then
- @typingspeed = 0
- @pause_after_wait = 0.01
- @type_keys = false
- @speed = :fast
- when :fast then
- @typingspeed = 0
- @pause_after_wait = 0.01
- @type_keys = true
- @speed = :fast
- when :slow then
- @typingspeed = 0.08
- @pause_after_wait = 0.1
- @type_keys = true
- @speed = :slow
- else
- raise ArgumentError, "Invalid speed: #{how_fast}"
- end
- end
-
- def speed
- return @speed if @speed == :slow
- return @type_keys ? :fast : :zippy
- end
-
- # deprecated: use speed = :fast instead
- def set_fast_speed
- self.speed = :fast
- end
-
- # deprecated: use speed = :slow instead
- def set_slow_speed
- self.speed = :slow
- end
-
- def visible
- @ie.visible
- end
- def visible=(boolean)
- @ie.visible = boolean if boolean != @ie.visible
- end
-
- # Yields successively to each IE window on the current desktop. Takes a block.
- # This method will not work when
- # Watir/Ruby is run under a service (instead of a user).
- # Yields to the window and its hwnd.
- def self.each
- shell = WIN32OLE.new('Shell.Application')
- shell.Windows.each do |window|
- next unless (window.path =~ /Internet Explorer/ rescue false)
- next unless (hwnd = window.hwnd rescue false)
- ie = IE.bind(window)
- ie.hwnd = hwnd
- yield ie
- end
- end
-
- # return internet explorer instance as specified. if none is found,
- # return nil.
- # arguments:
- # :url, url -- the URL of the IE browser window
- # :title, title -- the title of the browser page
- # :hwnd, hwnd -- the window handle of the browser window.
- # This method will not work when
- # Watir/Ruby is run under a service (instead of a user).
- def self.find(how, what)
- ie_ole = IE._find(how, what)
- IE.bind ie_ole if ie_ole
- end
-
- def self._find(how, what)
- ieTemp = nil
- IE.each do |ie|
- window = ie.ie
-
- case how
- when :url
- ieTemp = window if (what.matches(window.locationURL))
- when :title
- # normal windows explorer shells do not have document
- # note window.document will fail for "new" browsers
- begin
- title = window.locationname
- title = window.document.title
- rescue WIN32OLERuntimeError
- end
- ieTemp = window if what.matches(title)
- when :hwnd
- begin
- ieTemp = window if what == window.HWND
- rescue WIN32OLERuntimeError
- end
- else
- raise ArgumentError
- end
- end
- return ieTemp
- end
-
- def attach_browser_window how, what
- log "Seeking Window with #{how}: #{what}"
- ieTemp = nil
- begin
- Watir::until_with_timeout do
- ieTemp = IE._find how, what
- end
- rescue TimeOutException
- raise NoMatchingWindowFoundException,
- "Unable to locate a window with #{how} of #{what}"
- end
- @ie = ieTemp
- end
- private :attach_browser_window
-
- # Return the current window handle
- def hwnd
- raise "Not attached to a browser" if @ie.nil?
- @hwnd ||= @ie.hwnd
- end
- attr_writer :hwnd
-
- include Watir::Win32
-
- # Are we attached to an open browser?
- def exists?
- return false if @closing
- begin
- @ie.name =~ /Internet Explorer/
- rescue WIN32OLERuntimeError
- false
- end
- end
- alias :exist? :exists?
-
- # deprecated: use logger= instead
- def set_logger(logger)
- @logger = logger
- end
-
- def log(what)
- @logger.debug(what) if @logger
- end
-
- #
- # Accessing data outside the document
- #
-
- # Return the title of the document
- def title
- @ie.document.title
- end
-
- # Return the status of the window, typically from the status bar at the bottom.
- def status
- return @ie.statusText
- end
-
- #
- # Navigation
- #
-
- # Navigate to the specified URL.
- # * url - string - the URL to navigate to
- def goto(url)
- @ie.navigate(url)
- wait
- return @down_load_time
- end
-
- # Go to the previous page - the same as clicking the browsers back button
- # an WIN32OLERuntimeError exception is raised if the browser cant go back
- def back
- @ie.GoBack
- wait
- end
-
- # Go to the next page - the same as clicking the browsers forward button
- # an WIN32OLERuntimeError exception is raised if the browser cant go forward
- def forward
- @ie.GoForward
- wait
- end
-
- # Refresh the current page - the same as clicking the browsers refresh button
- # an WIN32OLERuntimeError exception is raised if the browser cant refresh
- def refresh
- @ie.refresh2(3)
- wait
- end
-
- def inspect
- '#<%s:0x%x url=%s title=%s>' % [self.class, hash*2, url.inspect, title.inspect]
- end
-
- # Execute the given JavaScript string
- def execute_script(source)
- document.parentWindow.eval(source.to_s)
- rescue WIN32OLERuntimeError
- document.parentWindow.execScript(source.to_s)
- end
-
- # clear the list of urls that we have visited
- def clear_url_list
- @url_list.clear
- end
-
- # Closes the Browser
- def close
- return unless exists?
- @closing = true
- @ie.stop
- wait
- chwnd = @ie.hwnd.to_i
- @ie.quit
- while Win32API.new("user32","IsWindow", 'L', 'L').Call(chwnd) == 1
- sleep 0.3
- end
- end
-
- # Maximize the window (expands to fill the screen)
- def maximize
- set_window_state :SW_MAXIMIZE
- end
-
- # Minimize the window (appears as icon on taskbar)
- def minimize
- set_window_state :SW_MINIMIZE
- end
-
- # Restore the window (after minimizing or maximizing)
- def restore
- set_window_state :SW_RESTORE
- end
-
- # Make the window come to the front
- def bring_to_front
- autoit.WinActivate title, ''
- end
-
- def front?
- 1 == autoit.WinActive(title, '')
- end
-
- private
- def set_window_state(state)
- autoit.WinSetState title, '', autoit.send(state)
- end
- def autoit
- Watir::autoit
- end
- public
-
- # Send key events to IE window.
- # See http://www.autoitscript.com/autoit3/docs/appendix/SendKeys.htm
- # for complete documentation on keys supported and syntax.
- def send_keys(key_string)
- autoit.WinActivate title
- autoit.Send key_string
- end
-
- def dir
- return File.expand_path(File.dirname(__FILE__))
- end
-
- #
- # Document and Document Data
- #
-
- # Return the current document
- def document
- return @ie.document
- end
-
- # returns the current url, as displayed in the address bar of the browser
- def url
- return @ie.LocationURL
- end
-
- #
- # Synchronization
- #
- include Watir::Utils
-
- # Block execution until the page has loaded.
- # =nodoc
- # Note: This code needs to be prepared for the ie object to be closed at
- # any moment!
- def wait(no_sleep=false)
- @xml_parser_doc = nil
- @down_load_time = 0.0
- a_moment = 0.2 # seconds
- start_load_time = Time.now
-
- begin
- while @ie.busy # XXX need to add time out
- sleep a_moment
- end
- until @ie.readyState == READYSTATE_COMPLETE do
- sleep a_moment
- end
- sleep a_moment
- until @ie.document do
- sleep a_moment
- end
-
- documents_to_wait_for = [@ie.document]
-
- rescue WIN32OLERuntimeError # IE window must have been closed
- @down_load_time = Time.now - start_load_time
- sleep @pause_after_wait unless no_sleep
- return @down_load_time
- end
-
- while doc = documents_to_wait_for.shift
- begin
- until doc.readyState == "complete" do
- sleep a_moment
- end
- @url_list << doc.location.href unless @url_list.include?(doc.location.href)
- doc.frames.length.times do |n|
- begin
- documents_to_wait_for << doc.frames[n.to_s].document
- rescue WIN32OLERuntimeError, NoMethodError
- end
- end
- rescue WIN32OLERuntimeError
- end
- end
-
- @down_load_time = Time.now - start_load_time
- run_error_checks
- sleep @pause_after_wait unless no_sleep
- @down_load_time
- end
-
- # Error checkers
-
- # this method runs the predefined error checks
- def run_error_checks
- @error_checkers.each { |e| e.call(self) }
- end
-
- # this method is used to add an error checker that gets executed on every page load
- # * checker Proc Object, that contains the code to be run
- def add_checker(checker)
- @error_checkers << checker
- end
-
- # this allows a checker to be disabled
- # * checker Proc Object, the checker that is to be disabled
- def disable_checker(checker)
- @error_checkers.delete(checker)
- end
-
- #
- # Show me state
- #
-
- # Show all forms displays all the forms that are on a web page.
- def show_forms
- if allForms = document.forms
- count = allForms.length
- puts "There are #{count} forms"
- for i in 0..count-1 do
- wrapped = FormWrapper.new(allForms.item(i))
- puts "Form name: #{wrapped.name}"
- puts " id: #{wrapped.id}"
- puts " method: #{wrapped.method}"
- puts " action: #{wrapped.action}"
- end
- else
- puts "No forms"
- end
- end
-
- # this method shows all the images availble in the document
- def show_images
- doc = document
- index = 1
- doc.images.each do |l|
- puts "image: name: #{l.name}"
- puts " id: #{l.invoke("id")}"
- puts " src: #{l.src}"
- puts " index: #{index}"
- index += 1
- end
- end
-
- # this method shows all the links availble in the document
- def show_links
- props = ["name", "id", "href"]
- print_sizes = [12, 12, 60]
- doc = document
- index = 0
- text_size = 60
- # draw the table header
- s = "index".ljust(6)
- props.each_with_index do |p, i|
- s += p.ljust(print_sizes[i])
- end
- s += "text/src".ljust(text_size)
- s += "\n"
-
- # now get the details of the links
- doc.links.each do |n|
- index += 1
- s = s + index.to_s.ljust(6)
- props.each_with_index do |prop, i|
- printsize = print_sizes[i]
- begin
- p = n.invoke(prop)
- temp_var = "#{p}".to_s.ljust(printsize)
- rescue
- # this object probably doesnt have this property
- temp_var = "".to_s.ljust(printsize)
- end
- s += temp_var
- end
- s += n.innerText
- if n.getElementsByTagName("IMG").length > 0
- s += " / " + n.getElementsByTagName("IMG").item(0).src
- end
- s += "\n"
- end
- puts s
- end
-
- # this method shows the name, id etc of the object that is currently active - ie the element that has focus
- # its mostly used in irb when creating a script
- def show_active
- s = ""
-
- current = document.activeElement
- begin
- s += current.invoke("type").to_s.ljust(16)
- rescue
- end
- props = ["name", "id", "value", "alt", "src", "innerText", "href"]
- props.each do |prop|
- begin
- p = current.invoke(prop)
- s += " " + "#{prop}=#{p}".to_s.ljust(18)
- rescue
- #this object probably doesnt have this property
- end
- end
- s += "\n"
- end
-
- # this method shows all the divs availble in the document
- def show_divs
- divs = document.getElementsByTagName("DIV")
- puts "Found #{divs.length} div tags"
- index = 1
- divs.each do |d|
- puts "#{index} id=#{d.invoke('id')} class=#{d.invoke("className")}"
- index += 1
- end
- end
-
- # this method is used to show all the tables that are available
- def show_tables
- tables = document.getElementsByTagName("TABLE")
- puts "Found #{tables.length} tables"
- index = 1
- tables.each do |d|
- puts "#{index} id=#{d.invoke('id')} rows=#{d.rows.length} columns=#{begin d.rows["0"].cells.length; rescue; end}"
- index += 1
- end
- end
-
- def show_pres
- pres = document.getElementsByTagName("PRE")
- puts "Found #{ pres.length } pre tags"
- index = 1
- pres.each do |d|
- puts "#{index} id=#{d.invoke('id')} class=#{d.invoke("className")}"
- index+=1
- end
- end
-
- # this method shows all the spans availble in the document
- def show_spans
- spans = document.getElementsByTagName("SPAN")
- puts "Found #{spans.length} span tags"
- index = 1
- spans.each do |d|
- puts "#{index} id=#{d.invoke('id')} class=#{d.invoke("className")}"
- index += 1
- end
- end
-
- def show_labels
- labels = document.getElementsByTagName("LABEL")
- puts "Found #{labels.length} label tags"
- index = 1
- labels.each do |d|
- puts "#{index} text=#{d.invoke('innerText')} class=#{d.invoke("className")} for=#{d.invoke("htmlFor")}"
- index += 1
- end
- end
-
- # Gives focus to the frame
- def focus
- document.activeElement.blur
- document.focus
- end
-
-
- # Functions written for using xpath for getting the elements.
- def xmlparser_document_object
- if @xml_parser_doc == nil
- create_xml_parser_doc
- end
- return @xml_parser_doc
- end
-
- # Create the Nokogiri object if it is nil. This method is private so can be called only
- # from xmlparser_document_object method.
- def create_xml_parser_doc
- require 'nokogiri'
- if @xml_parser_doc == nil
- htmlSource ="<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<HTML>\n"
- htmlSource = html_source(document.body,htmlSource," ")
- htmlSource += "\n</HTML>\n"
- # Angrez: Resolving Jira issue WTR-114
- htmlSource = htmlSource.gsub(/ /, ' ')
- begin
- #@xml_parser_doc = Nokogiri::HTML::Document.new(htmlSource)
- @xml_parser_doc = Nokogiri.parse(htmlSource)
- rescue => e
- output_xml_parser_doc("error.xml", htmlSource)
- raise e
- end
- end
- end
- private :create_xml_parser_doc
-
- def output_xml_parser_doc(name, text)
- file = File.open(name,"w")
- file.print(text)
- file.close
- end
- private :output_xml_parser_doc
-
- #Function Tokenizes the tag line and returns array of tokens.
- #Token could be either tagName or "=" or attribute name or attribute value
- #Attribute value could be either quoted string or single word
- def tokenize_tagline(outerHtml)
- outerHtml = outerHtml.gsub(/\n|\r/," ")
- #removing "< symbol", opening of current tag
- outerHtml =~ /^\s*<(.*)$/
- outerHtml = $1
- tokens = Array.new
- i = startOffset = 0
- length = outerHtml.length
- #puts outerHtml
- parsingValue = false
- while i < length do
- i +=1 while (i < length && outerHtml[i,1] =~ /\s/)
- next if i == length
- currentToken = outerHtml[i,1]
-
- #Either current tag has been closed or user has not closed the tag >
- # and we have received the opening of next element
- break if currentToken =~ /<|>/
-
- #parse quoted value
- if(currentToken == "\"" || currentToken == "'")
- parsingValue = false
- quote = currentToken
- startOffset = i
- i += 1
- i += 1 while (i < length && (outerHtml[i,1] != quote || outerHtml[i-1,1] == "\\"))
- if i == length
- tokens.push quote + outerHtml[startOffset..i-1]
- else
- tokens.push outerHtml[startOffset..i]
- end
- elsif currentToken == "="
- tokens.push "="
- parsingValue = true
- else
- startOffset = i
- i += 1 while (i < length && !(outerHtml[i,1] =~ /\s|=|<|>/)) if !parsingValue
- i += 1 while (i < length && !(outerHtml[i,1] =~ /\s|<|>/)) if parsingValue
- parsingValue = false
- i -= 1
- tokens.push outerHtml[startOffset..i]
- end
- i += 1
- end
- return tokens
- end
- private :tokenize_tagline
-
- # This function get and clean all the attributes of the tag.
- def all_tag_attributes(outerHtml)
- tokens = tokenize_tagline(outerHtml)
- #puts tokens
- tagLine = ""
- count = 1
- tokensLength = tokens.length
- expectedEqualityOP= false
- while count < tokensLength do
- if expectedEqualityOP == false
- #print Attribute Name
- # If attribute name is valid. Refer: http://www.w3.org/TR/REC-xml/#NT-Name
- if tokens[count] =~ /^(\w|_|:)(.*)$/
- tagLine += " #{tokens[count]}"
- expectedEqualityOP = true
- end
- elsif tokens[count] == "="
- count += 1
- if count == tokensLength
- tagLine += "=\"\""
- elsif(tokens[count][0,1] == "\"" || tokens[count][0,1] == "'")
- tagLine += "=#{tokens[count]}"
- else
- tagLine += "=\"#{tokens[count]}\""
- end
- expectedEqualityOP = false
- else
- #Opps! equality was expected but its not there.
- #Set value same as the attribute name e.g. selected="selected"
- tagLine += "=\"#{tokens[count-1]}\""
- expectedEqualityOP = false
- next
- end
- count += 1
- end
- tagLine += "=\"#{tokens[count-1]}\" " if expectedEqualityOP == true
- #puts tagLine
- return tagLine
- end
- private :all_tag_attributes
-
- # This function is used to escape the characters that are not valid XML data.
- def xml_escape(str)
- str = str.gsub(/&/,'&')
- str = str.gsub(/</,'<')
- str = str.gsub(/>/,'>')
- str = str.gsub(/"/, '"')
- str
- end
- private :xml_escape
-
- # Returns HTML Source
- # Traverse the DOM tree rooted at body element
- # and generate the HTML source.
- # element: Represent Current element
- # htmlString:HTML Source
- # spaces:(Used for debugging). Helps in indentation
- def html_source(element, htmlString, spaceString)
- begin
- tagLine = ""
- outerHtml = ""
- tagName = ""
- begin
- tagName = element.tagName.downcase
- tagName = EMPTY_TAG_NAME if tagName == ""
- # If tag is a mismatched tag.
- if !(tagName =~ /^(\w|_|:)(.*)$/)
- return htmlString
- end
- rescue
- #handling text nodes
- htmlString += xml_escape(element.toString)
- return htmlString
- end
- #puts tagName
- #Skip comment and script tag
- if tagName =~ /^!/ || tagName== "script" || tagName =="style"
- return htmlString
- end
- #tagLine += spaceString
- outerHtml = all_tag_attributes(element.outerHtml) if tagName != EMPTY_TAG_NAME
- tagLine += "<#{tagName} #{outerHtml}"
-
- canHaveChildren = element.canHaveChildren
- if canHaveChildren
- tagLine += ">"
- else
- tagLine += "/>" #self closing tag
- end
- #spaceString += spaceString
- htmlString += tagLine
- childElements = element.childnodes
- childElements.each do |child|
- htmlString = html_source(child,htmlString,spaceString)
- end
- if canHaveChildren
- #tagLine += spaceString
- tagLine ="</" + tagName + ">"
- htmlString += tagLine
- end
- return htmlString
- rescue => e
- puts e.to_s
- end
- return htmlString
- end
- private :html_source
-
- # return the first element that matches the xpath
- def element_by_xpath(xpath)
- temp = elements_by_xpath(xpath)
- temp = temp[0] if temp
- return temp
- end
-
- # execute xpath and return an array of elements
- def elements_by_xpath(xpath)
- doc = xmlparser_document_object
- modifiedXpath = ""
- selectedElements = Array.new
-
- # strip any trailing slash from the xpath expression (as used in watir unit tests)
- xpath.chop! unless (/\/$/ =~ xpath).nil?
-
- doc.search(xpath).each do |element|
- modifiedXpath = element.path
- temp = element_by_absolute_xpath(modifiedXpath) # temp = a DOM/COM element
- selectedElements << temp if temp != nil
- end
- #puts selectedElements.length
- if selectedElements.length == 0
- return nil
- else
- return selectedElements
- end
- end
-
- # Method that iterates over IE DOM object and get the elements for the given
- # xpath.
- def element_by_absolute_xpath(xpath)
- curElem = nil
-
- #puts "Hello; Given xpath is : #{xpath}"
- doc = document
- curElem = doc.getElementsByTagName("body").item(0)
- xpath =~ /^.*\/body\[?\d*\]?\/(.*)/
- xpath = $1
-
- if xpath == nil
- puts "Function Requires absolute XPath."
- return
- end
-
- arr = xpath.split(/\//)
- return nil if arr.length == 0
-
- lastTagName = arr[arr.length-1].to_s.upcase
-
- # lastTagName is like tagName[number] or just tagName. For the first case we need to
- # separate tagName and number.
- lastTagName =~ /(\w*)\[?\d*\]?/
- lastTagName = $1
- #puts lastTagName
-
- for element in arr do
- element =~ /(\w*)\[?(\d*)\]?/
- tagname = $1
- tagname = tagname.upcase
-
- if $2 != nil && $2 != ""
- index = $2
- index = "#{index}".to_i - 1
- else
- index = 0
- end
-
- #puts "#{element} #{tagname} #{index}"
- allElemns = curElem.childnodes
- if allElemns == nil || allElemns.length == 0
- puts "#{element} is null"
- next # Go to next element
- end
-
- #puts "Current element is : #{curElem.tagName}"
- allElemns.each do |child|
- gotIt = false
- begin
- curTag = child.tagName
- curTag = EMPTY_TAG_NAME if curTag == ""
- rescue
- next
- end
- #puts child.tagName
- if curTag == tagname
- index-=1
- if index < 0
- curElem = child
- break
- end
- end
- end
-
- #puts "Node selected at index #{index.to_s} : #{curElem.tagName}"
- end
- begin
- if curElem.tagName == lastTagName
- #puts curElem.tagName
- return curElem
- else
- return nil
- end
- rescue
- return nil
- end
- end
- private :element_by_absolute_xpath
-
- def attach_command
- "Watir::IE.attach(:hwnd, #{hwnd})"
- end
-
-
- end # class IE
-end
+module Watir
+ class IE
+ include Watir::Exception
+ include Container
+ include PageContainer
+
+ # Maximum number of seconds to wait when attaching to a window
+ @@attach_timeout = 2.0 # default value
+ def self.attach_timeout
+ @@attach_timeout
+ end
+ def self.attach_timeout=(timeout)
+ @@attach_timeout = timeout
+ end
+
+ # Return the options used when creating new instances of IE.
+ # BUG: this interface invites misunderstanding/misuse such as IE.options[:speed] = :zippy]
+ def self.options
+ {:speed => self.speed, :visible => self.visible, :attach_timeout => self.attach_timeout}
+ end
+ # set values for options used when creating new instances of IE.
+ def self.set_options options
+ options.each do |name, value|
+ send "#{name}=", value
+ end
+ end
+ # The globals $FAST_SPEED and $HIDE_IE are checked both at initialization
+ # and later, because they
+ # might be set after initialization. Setting them beforehand (e.g. from
+ # the command line) will affect the class, otherwise it is only a temporary
+ # effect
+ @@speed = $FAST_SPEED ? :fast : :slow
+ def self.speed
+ return :fast if $FAST_SPEED
+ @@speed
+ end
+ def self.speed= x
+ $FAST_SPEED = nil
+ @@speed = x
+ end
+ @@visible = $HIDE_IE ? false : true
+ def self.visible
+ return false if $HIDE_IE
+ @@visible
+ end
+ def self.visible= x
+ $HIDE_IE = nil
+ @@visible = x
+ end
+
+ # Used internally to determine when IE has finished loading a page
+ READYSTATE_INTERACTIVE = 3
+ READYSTATE_COMPLETE = 4
+
+ # The default color for highlighting objects as they are accessed.
+ HIGHLIGHT_COLOR = 'yellow'
+
+ # IE inserts some element whose tagName is empty and just acts as block level element
+ # Probably some IE method of cleaning things
+ # To pass the same to the xml parser we need to give some name to empty tagName
+ EMPTY_TAG_NAME = "DUMMY"
+
+ # The time, in seconds, it took for the new page to load after executing the
+ # the last command
+ attr_reader :down_load_time
+
+ # the OLE Internet Explorer object
+ attr_accessor :ie
+
+ # access to the logger object
+ attr_accessor :logger
+
+ # this contains the list of unique urls that have been visited
+ attr_reader :url_list
+
+ # Create a new IE window. Works just like IE.new in Watir 1.4.
+ def self.new_window
+ ie = new true
+ ie._new_window_init
+ ie
+ end
+
+ # Create an IE browser.
+ def initialize suppress_new_window=nil
+ _new_window_init unless suppress_new_window
+ end
+
+ def _new_window_init
+ create_browser_window
+ initialize_options
+ goto 'about:blank' # this avoids numerous problems caused by lack of a document
+ end
+
+ # Create a new IE Window, starting at the specified url.
+ # If no url is given, start empty.
+ def self.start url=nil
+ start_window url
+ end
+
+ # Create a new IE window, starting at the specified url.
+ # If no url is given, start empty. Works like IE.start in Watir 1.4.
+ def self.start_window url=nil
+ ie = new_window
+ ie.goto url if url
+ ie
+ end
+
+ # Create a new IE window in a new process.
+ # This method will not work when
+ # Watir/Ruby is run under a service (instead of a user).
+ def self.new_process
+ ie = new true
+ ie._new_process_init
+ ie
+ end
+
+ def _new_process_init
+ iep = Process.start
+ @ie = iep.window
+ @process_id = iep.process_id
+ initialize_options
+ goto 'about:blank'
+ end
+
+ # Create a new IE window in a new process, starting at the specified URL.
+ # Same as IE.start.
+ def self.start_process url=nil
+ ie = new_process
+ ie.goto url if url
+ ie
+ end
+
+ # Return a Watir::IE object for an existing IE window. Window can be
+ # referenced by url, title, or window handle.
+ # Second argument can be either a string or a regular expression in the
+ # case of of :url or :title.
+ # IE.attach(:url, 'http://www.google.com')
+ # IE.attach(:title, 'Google')
+ # IE.attach(:hwnd, 528140)
+ # This method will not work when
+ # Watir/Ruby is run under a service (instead of a user).
+ def self.attach how, what
+ ie = new true # don't create window
+ ie._attach_init(how, what)
+ ie
+ end
+
+ # this method is used internally to attach to an existing window
+ def _attach_init how, what
+ attach_browser_window how, what
+ initialize_options
+ wait
+ end
+
+ # Return an IE object that wraps the given window, typically obtained from
+ # Shell.Application.windows.
+ def self.bind window
+ ie = new true
+ ie.ie = window
+ ie.initialize_options
+ ie
+ end
+
+ def create_browser_window
+ @ie = WIN32OLE.new('InternetExplorer.Application')
+ end
+ private :create_browser_window
+
+ def initialize_options
+ self.visible = IE.visible
+ self.speed = IE.speed
+
+ @ole_object = nil
+ @page_container = self
+ @error_checkers = []
+ @activeObjectHighLightColor = HIGHLIGHT_COLOR
+
+
+ @logger = DefaultLogger.new
+ @url_list = []
+ end
+
+ # Specifies the speed that commands will be executed at. Choices are:
+ # * :slow (default)
+ # * :fast
+ # * :zippy
+ # With IE#speed= :zippy, text fields will be entered at once, instead of
+ # character by character (default).
+ def speed= how_fast
+ case how_fast
+ when :zippy then
+ @typingspeed = 0
+ @pause_after_wait = 0.01
+ @type_keys = false
+ @speed = :fast
+ when :fast then
+ @typingspeed = 0
+ @pause_after_wait = 0.01
+ @type_keys = true
+ @speed = :fast
+ when :slow then
+ @typingspeed = 0.08
+ @pause_after_wait = 0.1
+ @type_keys = true
+ @speed = :slow
+ else
+ raise ArgumentError, "Invalid speed: #{how_fast}"
+ end
+ end
+
+ def speed
+ return @speed if @speed == :slow
+ return @type_keys ? :fast : :zippy
+ end
+
+ # deprecated: use speed = :fast instead
+ def set_fast_speed
+ self.speed = :fast
+ end
+
+ # deprecated: use speed = :slow instead
+ def set_slow_speed
+ self.speed = :slow
+ end
+
+ def visible
+ @ie.visible
+ end
+ def visible=(boolean)
+ @ie.visible = boolean if boolean != @ie.visible
+ end
+
+ # Yields successively to each IE window on the current desktop. Takes a block.
+ # This method will not work when
+ # Watir/Ruby is run under a service (instead of a user).
+ # Yields to the window and its hwnd.
+ def self.each
+ shell = WIN32OLE.new('Shell.Application')
+ shell.Windows.each do |window|
+ next unless (window.path =~ /Internet Explorer/ rescue false)
+ next unless (hwnd = window.hwnd rescue false)
+ ie = IE.bind(window)
+ ie.hwnd = hwnd
+ yield ie
+ end
+ end
+
+ # return internet explorer instance as specified. if none is found,
+ # return nil.
+ # arguments:
+ # :url, url -- the URL of the IE browser window
+ # :title, title -- the title of the browser page
+ # :hwnd, hwnd -- the window handle of the browser window.
+ # This method will not work when
+ # Watir/Ruby is run under a service (instead of a user).
+ def self.find(how, what)
+ ie_ole = IE._find(how, what)
+ IE.bind ie_ole if ie_ole
+ end
+
+ def self._find(how, what)
+ ieTemp = nil
+ IE.each do |ie|
+ window = ie.ie
+
+ case how
+ when :url
+ ieTemp = window if (what.matches(window.locationURL))
+ when :title
+ # normal windows explorer shells do not have document
+ # note window.document will fail for "new" browsers
+ begin
+ title = window.locationname
+ title = window.document.title
+ rescue WIN32OLERuntimeError
+ end
+ ieTemp = window if what.matches(title)
+ when :hwnd
+ begin
+ ieTemp = window if what == window.HWND
+ rescue WIN32OLERuntimeError
+ end
+ else
+ raise ArgumentError
+ end
+ end
+ return ieTemp
+ end
+
+ def attach_browser_window how, what
+ log "Seeking Window with #{how}: #{what}"
+ ieTemp = nil
+ begin
+ Watir::until_with_timeout do
+ ieTemp = IE._find how, what
+ end
+ rescue TimeOutException
+ raise NoMatchingWindowFoundException,
+ "Unable to locate a window with #{how} of #{what}"
+ end
+ @ie = ieTemp
+ end
+ private :attach_browser_window
+
+ # Return the current window handle
+ def hwnd
+ raise "Not attached to a browser" if @ie.nil?
+ @hwnd ||= @ie.hwnd
+ end
+ attr_writer :hwnd
+
+ # Are we attached to an open browser?
+ def exists?
+ return false if @closing
+ begin
+ @ie.name =~ /Internet Explorer/
+ rescue WIN32OLERuntimeError
+ false
+ end
+ end
+ alias :exist? :exists?
+
+ # deprecated: use logger= instead
+ def set_logger(logger)
+ @logger = logger
+ end
+
+ def log(what)
+ @logger.debug(what) if @logger
+ end
+
+ #
+ # Accessing data outside the document
+ #
+
+ # Return the title of the document
+ def title
+ @ie.document.title
+ end
+
+ # Return the status of the window, typically from the status bar at the bottom.
+ def status
+ return @ie.statusText
+ end
+
+ #
+ # Navigation
+ #
+
+ # Navigate to the specified URL.
+ # * url - string - the URL to navigate to
+ def goto(url)
+ @ie.navigate(url)
+ wait
+ return @down_load_time
+ end
+
+ # Go to the previous page - the same as clicking the browsers back button
+ # an WIN32OLERuntimeError exception is raised if the browser cant go back
+ def back
+ @ie.GoBack
+ wait
+ end
+
+ # Go to the next page - the same as clicking the browsers forward button
+ # an WIN32OLERuntimeError exception is raised if the browser cant go forward
+ def forward
+ @ie.GoForward
+ wait
+ end
+
+ # Refresh the current page - the same as clicking the browsers refresh button
+ # an WIN32OLERuntimeError exception is raised if the browser cant refresh
+ def refresh
+ @ie.refresh2(3)
+ wait
+ end
+
+ def inspect
+ '#<%s:0x%x url=%s title=%s>' % [self.class, hash*2, url.inspect, title.inspect]
+ end
+
+ # Execute the given JavaScript string
+ def execute_script(source)
+ document.parentWindow.eval(source.to_s)
+ rescue WIN32OLERuntimeError
+ document.parentWindow.execScript(source.to_s)
+ end
+
+ # clear the list of urls that we have visited
+ def clear_url_list
+ @url_list.clear
+ end
+
+ # Closes the Browser
+ def close
+ return unless exists?
+ @closing = true
+ @ie.stop
+ wait rescue nil
+ chwnd = @ie.hwnd.to_i
+ @ie.quit
+ while Win32API.new("user32","IsWindow", 'L', 'L').Call(chwnd) == 1
+ sleep 0.3
+ end
+ end
+
+ # Maximize the window (expands to fill the screen)
+ def maximize
+ set_window_state :SW_MAXIMIZE
+ end
+
+ # Minimize the window (appears as icon on taskbar)
+ def minimize
+ set_window_state :SW_MINIMIZE
+ end
+
+ # Restore the window (after minimizing or maximizing)
+ def restore
+ set_window_state :SW_RESTORE
+ end
+
+ # Make the window come to the front
+ def bring_to_front
+ autoit.WinActivate title, ''
+ end
+
+ def front?
+ 1 == autoit.WinActive(title, '')
+ end
+
+ private
+ def set_window_state(state)
+ autoit.WinSetState title, '', autoit.send(state)
+ end
+ def autoit
+ Watir::autoit
+ end
+ public
+
+ # Send key events to IE window.
+ # See http://www.autoitscript.com/autoit3/docs/appendix/SendKeys.htm
+ # for complete documentation on keys supported and syntax.
+ def send_keys(key_string)
+ autoit.WinActivate title
+ autoit.Send key_string
+ end
+
+ def dir
+ return File.expand_path(File.dirname(__FILE__))
+ end
+
+ #
+ # Document and Document Data
+ #
+
+ # Return the current document
+ def document
+ return @ie.document
+ end
+
+ # returns the current url, as displayed in the address bar of the browser
+ def url
+ return @ie.LocationURL
+ end
+
+ #
+ # Synchronization
+ #
+
+ # Block execution until the page has loaded.
+ #
+ # Will raise Timeout::Error if page hasn't been loaded within 5 minutes.
+ # =nodoc
+ # Note: This code needs to be prepared for the ie object to be closed at
+ # any moment!
+ def wait(no_sleep=false)
+ @xml_parser_doc = nil
+ @down_load_time = 0.0
+ interval = 0.05
+ start_load_time = Time.now
+
+ Timeout::timeout(5*60) do
+ begin
+ until [READYSTATE_INTERACTIVE, READYSTATE_COMPLETE].include?(@ie.readyState)
+ sleep interval
+ end
+
+ until @ie.document
+ sleep interval
+ end
+
+ documents_to_wait_for = [@ie.document]
+ rescue WIN32OLERuntimeError # IE window must have been closed
+ @down_load_time = Time.now - start_load_time
+ return @down_load_time
+ end
+
+ while doc = documents_to_wait_for.shift
+ begin
+ until ["interactive", "complete"].include?(doc.readyState)
+ sleep interval
+ end
+ @url_list << doc.location.href unless @url_list.include?(doc.location.href)
+ doc.frames.length.times do |n|
+ begin
+ documents_to_wait_for << doc.frames[n.to_s].document
+ rescue WIN32OLERuntimeError, NoMethodError
+ end
+ end
+ rescue WIN32OLERuntimeError
+ end
+ end
+ end
+
+ @down_load_time = Time.now - start_load_time
+ run_error_checks
+ sleep @pause_after_wait unless no_sleep
+ @down_load_time
+ end
+
+ # Error checkers
+
+ # this method runs the predefined error checks
+ def run_error_checks
+ @error_checkers.each { |e| e.call(self) }
+ end
+
+ # this method is used to add an error checker that gets executed on every page load
+ # * checker Proc Object, that contains the code to be run
+ def add_checker(checker)
+ @error_checkers << checker
+ end
+
+ # this allows a checker to be disabled
+ # * checker Proc Object, the checker that is to be disabled
+ def disable_checker(checker)
+ @error_checkers.delete(checker)
+ end
+
+ #
+ # Show me state
+ #
+
+ # Show all forms displays all the forms that are on a web page.
+ def show_forms
+ if allForms = document.forms
+ count = allForms.length
+ puts "There are #{count} forms"
+ for i in 0..count-1 do
+ wrapped = FormWrapper.new(allForms.item(i))
+ puts "Form name: #{wrapped.name}"
+ puts " id: #{wrapped.id}"
+ puts " method: #{wrapped.method}"
+ puts " action: #{wrapped.action}"
+ end
+ else
+ puts "No forms"
+ end
+ end
+
+ # this method shows all the images availble in the document
+ def show_images
+ doc = document
+ index = 1
+ doc.images.each do |l|
+ puts "image: name: #{l.name}"
+ puts " id: #{l.invoke("id")}"
+ puts " src: #{l.src}"
+ puts " index: #{index}"
+ index += 1
+ end
+ end
+
+ # this method shows all the links availble in the document
+ def show_links
+ props = ["name", "id", "href"]
+ print_sizes = [12, 12, 60]
+ doc = document
+ index = 0
+ text_size = 60
+ # draw the table header
+ s = "index".ljust(6)
+ props.each_with_index do |p, i|
+ s += p.ljust(print_sizes[i])
+ end
+ s += "text/src".ljust(text_size)
+ s += "\n"
+
+ # now get the details of the links
+ doc.links.each do |n|
+ index += 1
+ s = s + index.to_s.ljust(6)
+ props.each_with_index do |prop, i|
+ printsize = print_sizes[i]
+ begin
+ p = n.invoke(prop)
+ temp_var = "#{p}".to_s.ljust(printsize)
+ rescue
+ # this object probably doesnt have this property
+ temp_var = "".to_s.ljust(printsize)
+ end
+ s += temp_var
+ end
+ s += n.innerText
+ if n.getElementsByTagName("IMG").length > 0
+ s += " / " + n.getElementsByTagName("IMG").item(0).src
+ end
+ s += "\n"
+ end
+ puts s
+ end
+
+ # this method shows the name, id etc of the object that is currently active - ie the element that has focus
+ # its mostly used in irb when creating a script
+ def show_active
+ s = ""
+
+ current = document.activeElement
+ begin
+ s += current.invoke("type").to_s.ljust(16)
+ rescue
+ end
+ props = ["name", "id", "value", "alt", "src", "innerText", "href"]
+ props.each do |prop|
+ begin
+ p = current.invoke(prop)
+ s += " " + "#{prop}=#{p}".to_s.ljust(18)
+ rescue
+ #this object probably doesnt have this property
+ end
+ end
+ s += "\n"
+ end
+
+ # this method shows all the divs availble in the document
+ def show_divs
+ divs = document.getElementsByTagName("DIV")
+ puts "Found #{divs.length} div tags"
+ index = 1
+ divs.each do |d|
+ puts "#{index} id=#{d.invoke('id')} class=#{d.invoke("className")}"
+ index += 1
+ end
+ end
+
+ # this method is used to show all the tables that are available
+ def show_tables
+ tables = document.getElementsByTagName("TABLE")
+ puts "Found #{tables.length} tables"
+ index = 1
+ tables.each do |d|
+ puts "#{index} id=#{d.invoke('id')} rows=#{d.rows.length} columns=#{begin d.rows["0"].cells.length; rescue; end}"
+ index += 1
+ end
+ end
+
+ def show_pres
+ pres = document.getElementsByTagName("PRE")
+ puts "Found #{ pres.length } pre tags"
+ index = 1
+ pres.each do |d|
+ puts "#{index} id=#{d.invoke('id')} class=#{d.invoke("className")}"
+ index+=1
+ end
+ end
+
+ # this method shows all the spans availble in the document
+ def show_spans
+ spans = document.getElementsByTagName("SPAN")
+ puts "Found #{spans.length} span tags"
+ index = 1
+ spans.each do |d|
+ puts "#{index} id=#{d.invoke('id')} class=#{d.invoke("className")}"
+ index += 1
+ end
+ end
+
+ def show_labels
+ labels = document.getElementsByTagName("LABEL")
+ puts "Found #{labels.length} label tags"
+ index = 1
+ labels.each do |d|
+ puts "#{index} text=#{d.invoke('innerText')} class=#{d.invoke("className")} for=#{d.invoke("htmlFor")}"
+ index += 1
+ end
+ end
+
+ # Gives focus to the frame
+ def focus
+ document.activeElement.blur
+ document.focus
+ end
+
+
+ # Functions written for using xpath for getting the elements.
+ def xmlparser_document_object
+ if @xml_parser_doc == nil
+ create_xml_parser_doc
+ end
+ return @xml_parser_doc
+ end
+
+ # Create the Nokogiri object if it is nil. This method is private so can be called only
+ # from xmlparser_document_object method.
+ def create_xml_parser_doc
+ require 'nokogiri'
+ if @xml_parser_doc == nil
+ htmlSource ="<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<HTML>\n"
+ htmlSource = html_source(document.body,htmlSource," ")
+ htmlSource += "\n</HTML>\n"
+ # Angrez: Resolving Jira issue WTR-114
+ htmlSource = htmlSource.gsub(/ /, ' ')
+ begin
+ #@xml_parser_doc = Nokogiri::HTML::Document.new(htmlSource)
+ @xml_parser_doc = Nokogiri.parse(htmlSource)
+ rescue => e
+ output_xml_parser_doc("error.xml", htmlSource)
+ raise e
+ end
+ end
+ end
+ private :create_xml_parser_doc
+
+ def output_xml_parser_doc(name, text)
+ file = File.open(name,"w")
+ file.print(text)
+ file.close
+ end
+ private :output_xml_parser_doc
+
+ #Function Tokenizes the tag line and returns array of tokens.
+ #Token could be either tagName or "=" or attribute name or attribute value
+ #Attribute value could be either quoted string or single word
+ def tokenize_tagline(outerHtml)
+ outerHtml = outerHtml.gsub(/\n|\r/," ")
+ #removing "< symbol", opening of current tag
+ outerHtml =~ /^\s*<(.*)$/
+ outerHtml = $1
+ tokens = Array.new
+ i = startOffset = 0
+ length = outerHtml.length
+ #puts outerHtml
+ parsingValue = false
+ while i < length do
+ i +=1 while (i < length && outerHtml[i,1] =~ /\s/)
+ next if i == length
+ currentToken = outerHtml[i,1]
+
+ #Either current tag has been closed or user has not closed the tag >
+ # and we have received the opening of next element
+ break if currentToken =~ /<|>/
+
+ #parse quoted value
+ if(currentToken == "\"" || currentToken == "'")
+ parsingValue = false
+ quote = currentToken
+ startOffset = i
+ i += 1
+ i += 1 while (i < length && (outerHtml[i,1] != quote || outerHtml[i-1,1] == "\\"))
+ if i == length
+ tokens.push quote + outerHtml[startOffset..i-1]
+ else
+ tokens.push outerHtml[startOffset..i]
+ end
+ elsif currentToken == "="
+ tokens.push "="
+ parsingValue = true
+ else
+ startOffset = i
+ i += 1 while (i < length && !(outerHtml[i,1] =~ /\s|=|<|>/)) if !parsingValue
+ i += 1 while (i < length && !(outerHtml[i,1] =~ /\s|<|>/)) if parsingValue
+ parsingValue = false
+ i -= 1
+ tokens.push outerHtml[startOffset..i]
+ end
+ i += 1
+ end
+ return tokens
+ end
+ private :tokenize_tagline
+
+ # This function get and clean all the attributes of the tag.
+ def all_tag_attributes(outerHtml)
+ tokens = tokenize_tagline(outerHtml)
+ #puts tokens
+ tagLine = ""
+ count = 1
+ tokensLength = tokens.length
+ expectedEqualityOP= false
+ while count < tokensLength do
+ if expectedEqualityOP == false
+ #print Attribute Name
+ # If attribute name is valid. Refer: http://www.w3.org/TR/REC-xml/#NT-Name
+ if tokens[count] =~ /^(\w|_|:)(.*)$/
+ tagLine += " #{tokens[count]}"
+ expectedEqualityOP = true
+ end
+ elsif tokens[count] == "="
+ count += 1
+ if count == tokensLength
+ tagLine += "=\"\""
+ elsif(tokens[count][0,1] == "\"" || tokens[count][0,1] == "'")
+ tagLine += "=#{tokens[count]}"
+ else
+ tagLine += "=\"#{tokens[count]}\""
+ end
+ expectedEqualityOP = false
+ else
+ #Opps! equality was expected but its not there.
+ #Set value same as the attribute name e.g. selected="selected"
+ tagLine += "=\"#{tokens[count-1]}\""
+ expectedEqualityOP = false
+ next
+ end
+ count += 1
+ end
+ tagLine += "=\"#{tokens[count-1]}\" " if expectedEqualityOP == true
+ #puts tagLine
+ return tagLine
+ end
+ private :all_tag_attributes
+
+ # This function is used to escape the characters that are not valid XML data.
+ def xml_escape(str)
+ str = str.gsub(/&/,'&')
+ str = str.gsub(/</,'<')
+ str = str.gsub(/>/,'>')
+ str = str.gsub(/"/, '"')
+ str
+ end
+ private :xml_escape
+
+ # Returns HTML Source
+ # Traverse the DOM tree rooted at body element
+ # and generate the HTML source.
+ # element: Represent Current element
+ # htmlString:HTML Source
+ # spaces:(Used for debugging). Helps in indentation
+ def html_source(element, htmlString, spaceString)
+ begin
+ tagLine = ""
+ outerHtml = ""
+ tagName = ""
+ begin
+ tagName = element.tagName.downcase
+ tagName = EMPTY_TAG_NAME if tagName == ""
+ # If tag is a mismatched tag.
+ if !(tagName =~ /^(\w|_|:)(.*)$/)
+ return htmlString
+ end
+ rescue
+ #handling text nodes
+ htmlString += xml_escape(element.toString)
+ return htmlString
+ end
+ #puts tagName
+ #Skip comment and script tag
+ if tagName =~ /^!/ || tagName== "script" || tagName =="style"
+ return htmlString
+ end
+ #tagLine += spaceString
+ outerHtml = all_tag_attributes(element.outerHtml) if tagName != EMPTY_TAG_NAME
+ tagLine += "<#{tagName} #{outerHtml}"
+
+ canHaveChildren = element.canHaveChildren
+ if canHaveChildren
+ tagLine += ">"
+ else
+ tagLine += "/>" #self closing tag
+ end
+ #spaceString += spaceString
+ htmlString += tagLine
+ childElements = element.childnodes
+ childElements.each do |child|
+ htmlString = html_source(child,htmlString,spaceString)
+ end
+ if canHaveChildren
+ #tagLine += spaceString
+ tagLine ="</" + tagName + ">"
+ htmlString += tagLine
+ end
+ return htmlString
+ rescue => e
+ puts e.to_s
+ end
+ return htmlString
+ end
+ private :html_source
+
+ # return the first element that matches the xpath
+ def element_by_xpath(xpath)
+ temp = elements_by_xpath(xpath)
+ temp = temp[0] if temp
+ return temp
+ end
+
+ # execute xpath and return an array of elements
+ def elements_by_xpath(xpath)
+ doc = xmlparser_document_object
+ modifiedXpath = ""
+ selectedElements = Array.new
+
+ # strip any trailing slash from the xpath expression (as used in watir unit tests)
+ xpath.chop! unless (/\/$/ =~ xpath).nil?
+
+ doc.xpath(xpath).each do |element|
+ modifiedXpath = element.path
+ temp = element_by_absolute_xpath(modifiedXpath) # temp = a DOM/COM element
+ selectedElements << temp if temp != nil
+ end
+ #puts selectedElements.length
+ if selectedElements.length == 0
+ return nil
+ else
+ return selectedElements
+ end
+ end
+
+ # Method that iterates over IE DOM object and get the elements for the given
+ # xpath.
+ def element_by_absolute_xpath(xpath)
+ curElem = nil
+
+ #puts "Hello; Given xpath is : #{xpath}"
+ doc = document
+ curElem = doc.getElementsByTagName("body").item(0)
+ xpath =~ /^.*\/body\[?\d*\]?\/(.*)/
+ xpath = $1
+
+ if xpath == nil
+ puts "Function Requires absolute XPath."
+ return
+ end
+
+ arr = xpath.split(/\//)
+ return nil if arr.length == 0
+
+ lastTagName = arr[arr.length-1].to_s.upcase
+
+ # lastTagName is like tagName[number] or just tagName. For the first case we need to
+ # separate tagName and number.
+ lastTagName =~ /(\w*)\[?\d*\]?/
+ lastTagName = $1
+ #puts lastTagName
+
+ for element in arr do
+ element =~ /(\w*)\[?(\d*)\]?/
+ tagname = $1
+ tagname = tagname.upcase
+
+ if $2 != nil && $2 != ""
+ index = $2
+ index = "#{index}".to_i - 1
+ else
+ index = 0
+ end
+
+ #puts "#{element} #{tagname} #{index}"
+ allElemns = curElem.childnodes
+ if allElemns == nil || allElemns.length == 0
+ puts "#{element} is null"
+ next # Go to next element
+ end
+
+ #puts "Current element is : #{curElem.tagName}"
+ allElemns.each do |child|
+ gotIt = false
+ begin
+ curTag = child.tagName
+ curTag = EMPTY_TAG_NAME if curTag == ""
+ rescue
+ next
+ end
+ #puts child.tagName
+ if curTag == tagname
+ index-=1
+ if index < 0
+ curElem = child
+ break
+ end
+ end
+ end
+
+ #puts "Node selected at index #{index.to_s} : #{curElem.tagName}"
+ end
+ begin
+ if curElem.tagName == lastTagName
+ #puts curElem.tagName
+ return curElem
+ else
+ return nil
+ end
+ rescue
+ return nil
+ end
+ end
+ private :element_by_absolute_xpath
+
+ def attach_command
+ "Watir::IE.attach(:hwnd, #{hwnd})"
+ end
+
+
+ end # class IE
+end