lib/watir/locator.rb in watir-2.0.1 vs lib/watir/locator.rb in watir-2.0.2.rc1

- old
+ new

@@ -1,10 +1,14 @@ module Watir class Locator include Watir include Watir::Exception + def document + @document ||= @container.document + end + def normalize_specifiers!(specifiers) specifiers.each do |how, what| case how when :index what = what.to_i @@ -26,24 +30,59 @@ def match_with_specifiers?(element) @specifiers.all? {|how, what| how == :index || match?(element, how, what)} end + def has_excluding_specifiers? + @specifiers.keys.any? {|specifier| [:css, :xpath, :ole_object].include? specifier} + end + def set_specifier(how, what=nil) specifiers = what ? {how => what} : how @specifiers = {:index => Watir::IE.base_index} # default if not specified normalize_specifiers! specifiers end + + def locate_by_id + # Searching through all elements returned by __ole_inner_elements + # is *significantly* slower than IE's getElementById() and + # getElementsByName() calls when how is :id. However + # IE doesn't match Regexps, so first we make sure what is a String. + # In addition, IE's getElementById() will also return an element + # where the :name matches, so we will only return the results of + # getElementById() if the matching element actually HAS a matching + # :id. + the_id = @specifiers[:id] + if the_id && the_id.class == String + element = document.getElementById(the_id) rescue nil + # Return if our fast match really HAS a matching :id + return element if element && element.invoke('id') == the_id + end + + nil + end + def locate_by_xpath_css_ole if @specifiers[:xpath] return @container.element_by_xpath(@specifiers[:xpath]) elsif @specifiers[:css] return @container.element_by_css(@specifiers[:css]) elsif @specifiers[:ole_object] return @specifiers[:ole_object] + end + end + + def create_element ole_object + if @klass == Element + element = Element.new(ole_object) + else + element = @klass.new(@container, @specifiers, nil) + element.ole_object = ole_object + def element.locate; @o; end end + element end end class TaggedElementLocator < Locator def initialize(container, tag, klass) @@ -51,19 +90,12 @@ @tag = tag @klass = klass || Element end def each_element tag - @container.document.getElementsByTagName(tag).each do |ole_object| - if @klass == Element - element = Element.new(ole_object) - else - element = @klass.new(@container, @specifiers, nil) - element.ole_object = ole_object - def element.locate; @o; end unless @klass == TableRow - end - yield element + document.getElementsByTagName(tag).each do |ole_object| + yield create_element ole_object end end def each each_element(@tag) do |element| @@ -72,11 +104,13 @@ end nil end def locate - return locate_by_xpath_css_ole if @specifiers[:xpath] || @specifiers[:css] || @specifiers[:ole_object] + el = locate_by_id + return el if el + return locate_by_xpath_css_ole if has_excluding_specifiers? count = Watir::IE.base_index - 1 each do |element| count += 1 return element.ole_object if count == @specifiers[:index] @@ -87,56 +121,55 @@ def match?(element, how, what) begin method = element.method(how) rescue NameError raise MissingWayOfFindingObjectException, - "#{how} is an unknown way of finding a <#{@tag}> element (#{what})" + "#{how} is an unknown way of finding a <#{@tag || @tags.join(", ")}> element (#{what})" end case method.arity when 0 what.matches method.call when 1 method.call(what) else raise MissingWayOfFindingObjectException, - "#{how} is an unknown way of finding a <#{@tag}> element (#{what})" + "#{how} is an unknown way of finding a <#{@tag || @tags.join(", ")}> element (#{what})" end end end class FrameLocator < TaggedElementLocator def initialize(container) - @container = container - @tags = Frame::TAG + super(container, Frame::TAG, Frame) end def each_element tag - frames = @container.document.frames + frames = document.frames i = 0 - @container.document.getElementsByTagName(tag).each do |ole_object| - frame = Frame.new(@container, @specifiers, nil) - frame.ole_object = ole_object + document.getElementsByTagName(tag).each do |ole_object| + frame = create_element ole_object frame.document = frames.item(i) - def frame.locate; @o; end yield frame i += 1 end end def each - @tags.each do |tag| - each_element(tag) do |element| + @tag.each do |t| + each_element(t) do |element| next unless match_with_specifiers?(element) yield element end end nil end def locate - return locate_by_xpath_css_ole if @specifiers[:xpath] || @specifiers[:css] || @specifiers[:ole_object] + # do not locate frames by getElementById since can't get the correct + # 'document' related with that ole_object like it's done in #each_element + return locate_by_xpath_css_ole if has_excluding_specifiers? count = Watir::IE.base_index - 1 each do |frame| count += 1 return frame.ole_object, frame.document if count == @specifiers[:index] @@ -148,46 +181,35 @@ def initialize(container) super(container, 'FORM', Form) end def each_element(tag) - @container.document.forms.each do |form| - form_element = Form.new(@container, @specifiers, nil) - form_element.ole_object = form - def form_element.locate; @o; end - yield form_element + document.forms.each do |form| + yield create_element form end end end class InputElementLocator < Locator - - attr_accessor :document, :element, :elements, :klass - def initialize container, types, klass @container = container @types = types - @elements = nil @klass = klass || Element end def each_element - @elements.each do |object| - if @klass == Element - element = Element.new(object) - else - element = @klass.new(@container, @specifiers, nil) - element.ole_object = object - def element.locate; @o; end - end - yield element + elements = locate_by_name || @container.__ole_inner_elements + elements.each do |object| + yield create_element object end nil end def locate - return locate_by_xpath_css_ole if @specifiers[:xpath] || @specifiers[:css] || @specifiers[:ole_object] + el = locate_by_id + return el if el + return locate_by_xpath_css_ole if has_excluding_specifiers? count = Watir::IE.base_index - 1 each do |element| count += 1 return element.ole_object if count == @specifiers[:index] @@ -212,32 +234,17 @@ end what.matches(attribute) end - def fast_locate - # Searching through all elements returned by ole_inner_elements - # is *significantly* slower than IE's getElementById() and - # getElementsByName() calls when how is :id or :name. However - # IE doesn't match Regexps, so first we make sure what is a String. - # In addition, IE's getElementById() will also return an element - # where the :name matches, so we will only return the results of - # getElementById() if the matching element actually HAS a matching - # :id. + private - the_id = @specifiers[:id] - if the_id && the_id.class == String && - @specifiers[:index] == Watir::IE.base_index && @specifiers.length == 2 - @element = @document.getElementById(the_id) rescue nil - # Return if our fast match really HAS a matching :id - return true if @element && @element.invoke('id') == the_id && @types.include?(@element.getAttribute('type')) - end - + def locate_by_name the_name = @specifiers[:name] if the_name && the_name.class == String - @elements = @document.getElementsByName(the_name) rescue nil - end - false + return document.getElementsByName(the_name) rescue nil + end + nil end end # This is like the TaggedElementLocator but # get all the elements by forcing @tag to be '*'