[SCM] WebKit Debian packaging branch, debian/unstable, updated. debian/1.1.15-1-40151-g37bb677

darin darin at 268f45cc-cd09-0410-ab3c-d52691b4dbfc
Sat Sep 26 08:38:04 UTC 2009


The following commit has been merged in the debian/unstable branch:
commit a184cc66924812dd150bc5360a9b0f37da0e3560
Author: darin <darin at 268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Date:   Mon May 3 22:29:59 2004 +0000

            Reviewed by Ken
    
            - fixed <rdar://problem/3091531>: "should format copied tables with tabs between cells, spreadsheet-style, for pasting to Excel"
            - fixed <rdar://problem/3110002>: "Find doesn't match across formatting boundaries"
            - fixed <rdar://problem/3640340>: "REGRESSION (136-137): nil-deref in KHTMLPart::setFocusNodeIfNeeded inside triple click code after repeatedly click/drag selecting text"
            - fixed <rdar://problem/3640422>: "too many blank lines between paragraphs in copied text"
    
            * khtml/dom/dom2_range.h: Added == and != operators to DOMRange.
            * khtml/dom/dom2_range.cpp: (DOM::operator==): Added.
    
            * khtml/khtml_part.cpp:
            (KHTMLPart::init): Don't call findTextBegin any more; it's now not used at all
            in WebCore.
            (KHTMLPart::clear): Don't call findTextBegin any more. Also don't set up the
            load statistics variables.
            (KHTMLPart::findTextNext): Roll out APPLE_CHANGES; this function is no longer
            used in WebCore any more and instead the entire thing is compiled out.
            (KHTMLPart::text): Reimplement this by calling one of the new text operations. All the interesting
            parts were moved into the TextIterator class.
            (KHTMLPart::setFocusNodeIfNeeded): Add a check for nil; this is the fix for 364030.
    
            * khtml/khtmlpart_p.h: (KHTMLPartPrivate::KHTMLPartPrivate): Put m_findPos,
            m_findNode, m_overURL, m_overURLTarget, m_scrollTimer, m_loadedObjects,
            m_totalObjectCount, and m_jobPercent inside !APPLE_CHANGES.
    
            * khtml/xml/dom_selection.h: Replaced uses of 4-character tabs with spaces.
            (DOM::Selection::Selection): Added a constructor that takes a DOM range.
            (DOM::Selection::operator=): Overloaded operator= for DOM range and position.
            This is slightly more efficient than letting a second Selection object be constructed.
            * khtml/xml/dom_selection.cpp: Replaced uses of 4-character tabs with spaces.
            (DOM::Selection::Selection): Added a constructor that takes a DOM range.
    
            * kwq/KWQKHTMLPart.mm: (KWQKHTMLPart::findString): Reimplement find so it uses
            the new text operations function for finding.
    
            * khtml/misc/khtml_text_operations.h:
            * khtml/misc/khtml_text_operations.cpp:
            * WebCore.pbproj/project.pbxproj:
            Added new text iterator classes that do the heavy lifting.
    
    
    git-svn-id: http://svn.webkit.org/repository/webkit/trunk@6532 268f45cc-cd09-0410-ab3c-d52691b4dbfc

diff --git a/WebCore/ChangeLog-2005-08-23 b/WebCore/ChangeLog-2005-08-23
index ab55223..64bf7d8 100644
--- a/WebCore/ChangeLog-2005-08-23
+++ b/WebCore/ChangeLog-2005-08-23
@@ -1,3 +1,45 @@
+2004-05-03  Darin Adler  <darin at apple.com>
+
+        Reviewed by Ken
+
+        - fixed <rdar://problem/3091531>: "should format copied tables with tabs between cells, spreadsheet-style, for pasting to Excel"
+        - fixed <rdar://problem/3110002>: "Find doesn't match across formatting boundaries"
+        - fixed <rdar://problem/3640340>: "REGRESSION (136-137): nil-deref in KHTMLPart::setFocusNodeIfNeeded inside triple click code after repeatedly click/drag selecting text"
+        - fixed <rdar://problem/3640422>: "too many blank lines between paragraphs in copied text"
+
+        * khtml/dom/dom2_range.h: Added == and != operators to DOMRange.
+        * khtml/dom/dom2_range.cpp: (DOM::operator==): Added.
+
+        * khtml/khtml_part.cpp:
+        (KHTMLPart::init): Don't call findTextBegin any more; it's now not used at all
+        in WebCore.
+        (KHTMLPart::clear): Don't call findTextBegin any more. Also don't set up the
+        load statistics variables.
+        (KHTMLPart::findTextNext): Roll out APPLE_CHANGES; this function is no longer
+        used in WebCore any more and instead the entire thing is compiled out.
+        (KHTMLPart::text): Reimplement this by calling one of the new text operations. All the interesting
+        parts were moved into the TextIterator class.
+        (KHTMLPart::setFocusNodeIfNeeded): Add a check for nil; this is the fix for 364030.
+
+        * khtml/khtmlpart_p.h: (KHTMLPartPrivate::KHTMLPartPrivate): Put m_findPos,
+        m_findNode, m_overURL, m_overURLTarget, m_scrollTimer, m_loadedObjects,
+        m_totalObjectCount, and m_jobPercent inside !APPLE_CHANGES.
+
+        * khtml/xml/dom_selection.h: Replaced uses of 4-character tabs with spaces.
+        (DOM::Selection::Selection): Added a constructor that takes a DOM range.
+        (DOM::Selection::operator=): Overloaded operator= for DOM range and position.
+        This is slightly more efficient than letting a second Selection object be constructed.
+        * khtml/xml/dom_selection.cpp: Replaced uses of 4-character tabs with spaces.
+        (DOM::Selection::Selection): Added a constructor that takes a DOM range.
+
+        * kwq/KWQKHTMLPart.mm: (KWQKHTMLPart::findString): Reimplement find so it uses
+        the new text operations function for finding.
+
+        * khtml/misc/khtml_text_operations.h:
+        * khtml/misc/khtml_text_operations.cpp:
+        * WebCore.pbproj/project.pbxproj:
+        Added new text iterator classes that do the heavy lifting.
+
 2004-05-03  David Hyatt  <hyatt at apple.com>
 
 	Make sure that XML processing instructions set themselves as the parent node of the stylesheets they load,
diff --git a/WebCore/WebCore.pbproj/project.pbxproj b/WebCore/WebCore.pbproj/project.pbxproj
index e06d154..7871e3f 100644
--- a/WebCore/WebCore.pbproj/project.pbxproj
+++ b/WebCore/WebCore.pbproj/project.pbxproj
@@ -533,6 +533,7 @@
 				BC86FB8F061F5C23006BB822,
 				BE8BD8F506359F6000D3F20B,
 				BE8BD90B0635CC2F00D3F20B,
+				9342E3EB0646C8FF00004B05,
 			);
 			isa = PBXHeadersBuildPhase;
 			runOnlyForDeploymentPostprocessing = 0;
@@ -816,6 +817,7 @@
 				BE8BD8F406359F6000D3F20B,
 				BE8BD90A0635CC2F00D3F20B,
 				842F72DD06405FDF00CC271B,
+				9342E3EA0646C8FF00004B05,
 			);
 			isa = PBXSourcesBuildPhase;
 			runOnlyForDeploymentPostprocessing = 0;
@@ -1780,6 +1782,34 @@
 			settings = {
 			};
 		};
+		9342E3E80646C8FF00004B05 = {
+			fileEncoding = 4;
+			isa = PBXFileReference;
+			lastKnownFileType = sourcecode.cpp.cpp;
+			path = khtml_text_operations.cpp;
+			refType = 4;
+			sourceTree = "<group>";
+		};
+		9342E3E90646C8FF00004B05 = {
+			fileEncoding = 4;
+			isa = PBXFileReference;
+			lastKnownFileType = sourcecode.c.h;
+			path = khtml_text_operations.h;
+			refType = 4;
+			sourceTree = "<group>";
+		};
+		9342E3EA0646C8FF00004B05 = {
+			fileRef = 9342E3E80646C8FF00004B05;
+			isa = PBXBuildFile;
+			settings = {
+			};
+		};
+		9342E3EB0646C8FF00004B05 = {
+			fileRef = 9342E3E90646C8FF00004B05;
+			isa = PBXBuildFile;
+			settings = {
+			};
+		};
 		934E43780414294A008635CE = {
 			fileEncoding = 4;
 			isa = PBXFileReference;
@@ -5533,6 +5563,8 @@
 				F523D28102DE43D7018635CA,
 				F523D28202DE43D7018635CA,
 				F523D28302DE43D7018635CA,
+				9342E3E80646C8FF00004B05,
+				9342E3E90646C8FF00004B05,
 				F523D28402DE43D7018635CA,
 				F523D28502DE43D7018635CA,
 				F523D28602DE43D7018635CA,
diff --git a/WebCore/khtml/dom/dom2_range.cpp b/WebCore/khtml/dom/dom2_range.cpp
index 271bd0d..356c08c 100644
--- a/WebCore/khtml/dom/dom2_range.cpp
+++ b/WebCore/khtml/dom/dom2_range.cpp
@@ -5,7 +5,7 @@
  * (C) 2000 Gunnstein Lye (gunnstein at netcom.no)
  * (C) 2000 Frederik Holljen (frederik.holljen at hig.no)
  * (C) 2001 Peter Kelly (pmk at post.com)
- * Copyright (C) 2003 Apple Computer, Inc.
+ * Copyright (C) 2004 Apple Computer, Inc.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Library General Public
@@ -23,11 +23,13 @@
  * Boston, MA 02111-1307, USA.
  *
  */
+
 #include "dom/dom_exception.h"
+
 #include "xml/dom_docimpl.h"
 #include "xml/dom2_rangeimpl.h"
 
-using namespace DOM;
+namespace DOM {
 
 Range::Range()
 {
@@ -400,5 +402,12 @@ void Range::throwException(int exceptioncode) const
         throw DOMException(exceptioncode);
 }
 
+bool operator==(const Range &a, const Range &b)
+{
+    return a.startContainer() == b.startContainer()
+        && a.endContainer() == b.endContainer()
+        && a.startOffset() == b.startOffset()
+        && a.endOffset() == b.endOffset();
+}
 
-
+}
diff --git a/WebCore/khtml/dom/dom2_range.h b/WebCore/khtml/dom/dom2_range.h
index bb2e526..a5ac6bd 100644
--- a/WebCore/khtml/dom/dom2_range.h
+++ b/WebCore/khtml/dom/dom2_range.h
@@ -5,6 +5,7 @@
  * (C) 2000 Gunnstein Lye (gunnstein at netcom.no)
  * (C) 2000 Frederik Holljen (frederik.holljen at hig.no)
  * (C) 2001 Peter Kelly (pmk at post.com)
+ * Copyright (C) 2004 Apple Computer, Inc.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Library General Public
@@ -472,6 +473,9 @@ private:
     void throwException(int exceptioncode) const;
 };
 
-}; // namespace
+bool operator==(const Range &, const Range &);
+inline bool operator!=(const Range &a, const Range &b) { return !(a == b); }
+
+} // namespace
 
 #endif
diff --git a/WebCore/khtml/editing/SelectionController.cpp b/WebCore/khtml/editing/SelectionController.cpp
index 6f1774f..086487a 100644
--- a/WebCore/khtml/editing/SelectionController.cpp
+++ b/WebCore/khtml/editing/SelectionController.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2003 Apple Computer, Inc.  All rights reserved.
+ * Copyright (C) 2004 Apple Computer, Inc.  All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -77,14 +77,24 @@ Selection::Selection()
 Selection::Selection(const Position &pos)
 {
     init();
-	assignBaseAndExtent(pos, pos);
+    assignBaseAndExtent(pos, pos);
+    validate();
+}
+
+Selection::Selection(const Range &r)
+{
+    const Position start(r.startContainer().handle(), r.startOffset());
+    const Position end(r.endContainer().handle(), r.endOffset());
+
+    init();
+    assignBaseAndExtent(start, end);
     validate();
 }
 
 Selection::Selection(const Position &base, const Position &extent)
 {
     init();
-	assignBaseAndExtent(base, extent);
+    assignBaseAndExtent(base, extent);
     validate();
 }
 
@@ -92,8 +102,8 @@ Selection::Selection(const Selection &o)
 {
     init();
     
-	assignBaseAndExtent(o.base(), o.extent());
-	assignStartAndEnd(o.start(), o.end());
+    assignBaseAndExtent(o.base(), o.extent());
+    assignStartAndEnd(o.start(), o.end());
 
     m_state = o.m_state;
     m_affinity = o.m_affinity;
@@ -129,8 +139,8 @@ void Selection::init()
 
 Selection &Selection::operator=(const Selection &o)
 {
-	assignBaseAndExtent(o.base(), o.extent());
-	assignStartAndEnd(o.start(), o.end());
+    assignBaseAndExtent(o.base(), o.extent());
+    assignStartAndEnd(o.start(), o.end());
 
     m_state = o.m_state;
     m_affinity = o.m_affinity;
@@ -166,22 +176,22 @@ void Selection::moveTo(const Range &r)
 {
     Position start(r.startContainer().handle(), r.startOffset());
     Position end(r.endContainer().handle(), r.endOffset());
-	moveTo(start, end);
+    moveTo(start, end);
 }
 
 void Selection::moveTo(const Selection &o)
 {
-	moveTo(o.start(), o.end());
+    moveTo(o.start(), o.end());
 }
 
 void Selection::moveTo(const Position &pos)
 {
-	moveTo(pos, pos);
+    moveTo(pos, pos);
 }
 
 void Selection::moveTo(const Position &base, const Position &extent)
 {
-	assignBaseAndExtent(base, extent);
+    assignBaseAndExtent(base, extent);
     validate();
 }
 
@@ -337,8 +347,8 @@ int Selection::xPosForVerticalArrowNavigation(EPositionType type, bool recalc) c
 
 void Selection::clear()
 {
-	assignBaseAndExtent(emptyPosition(), emptyPosition());
-	validate();
+    assignBaseAndExtent(emptyPosition(), emptyPosition());
+    validate();
 }
 
 void Selection::setBase(const Position &pos)
@@ -486,13 +496,13 @@ void Selection::validate(ETextGranularity granularity)
     }
 
     // make sure we do not have a dangling start or end
-	if (base().isEmpty() && extent().isEmpty()) {
+    if (base().isEmpty() && extent().isEmpty()) {
         assignStartAndEnd(emptyPosition(), emptyPosition());
         m_baseIsStart = true;
     }
-	else if (base().isEmpty() || extent().isEmpty()) {
+    else if (base().isEmpty() || extent().isEmpty()) {
         m_baseIsStart = true;
-	}
+    }
     else {
         // adjust m_baseIsStart as needed
         if (base().node() == extent().node()) {
@@ -572,13 +582,13 @@ void Selection::validate(ETextGranularity granularity)
     }
 #endif  // APPLE_CHANGES
 
-	// adjust the state
-	if (start().isEmpty() && end().isEmpty())
-		m_state = NONE;
-	else if (start() == end())
-		m_state = CARET;
-	else
-		m_state = RANGE;
+    // adjust the state
+    if (start().isEmpty() && end().isEmpty())
+        m_state = NONE;
+    else if (start() == end())
+        m_state = CARET;
+    else
+        m_state = RANGE;
 
     m_needsCaretLayout = true;
     
@@ -618,13 +628,13 @@ bool Selection::moveToRenderedContent()
 
 bool Selection::nodeIsBeforeNode(NodeImpl *n1, NodeImpl *n2) 
 {
-	if (!n1 || !n2) 
-		return true;
- 
- 	if (n1 == n2)
- 		return true;
- 
- 	bool result = false;
+    if (!n1 || !n2) 
+        return true;
+
+    if (n1 == n2)
+        return true;
+
+    bool result = false;
     int n1Depth = 0;
     int n2Depth = 0;
 
@@ -666,7 +676,7 @@ bool Selection::nodeIsBeforeNode(NodeImpl *n1, NodeImpl *n2)
         }
         n = n->nextSibling();
     }
-	return result;
+    return result;
 }
 
 #if APPLE_CHANGES
diff --git a/WebCore/khtml/editing/SelectionController.h b/WebCore/khtml/editing/SelectionController.h
index 05434bc..d131d40 100644
--- a/WebCore/khtml/editing/SelectionController.h
+++ b/WebCore/khtml/editing/SelectionController.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2003 Apple Computer, Inc.  All rights reserved.
+ * Copyright (C) 2004 Apple Computer, Inc.  All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -45,10 +45,10 @@ class Range;
 class Selection
 {
 public:
-	enum EState { NONE, CARET, RANGE };
-	enum EAlter { MOVE, EXTEND };
-	enum EDirection { FORWARD, BACKWARD, RIGHT, LEFT, UP, DOWN };
-	enum ETextGranularity { CHARACTER, WORD, LINE };
+    enum EState { NONE, CARET, RANGE };
+    enum EAlter { MOVE, EXTEND };
+    enum EDirection { FORWARD, BACKWARD, RIGHT, LEFT, UP, DOWN };
+    enum ETextGranularity { CHARACTER, WORD, LINE };
 
     // These match the AppKit values for these concepts.
     // From NSTextView.h:
@@ -57,13 +57,13 @@ public:
     enum EAffinity { UPSTREAM = 0, DOWNSTREAM = 1 };
 
     Selection();
+    Selection(const Range &);
     Selection(const Position &);
     Selection(const Position &, const Position &);
     Selection(const Selection &);
-    ~Selection() {}
 
-	EState state() const { return m_state; }
-	EAffinity affinity() const { return m_affinity; }
+    EState state() const { return m_state; }
+    EAffinity affinity() const { return m_affinity; }
     void setAffinity(EAffinity);
 
     void moveTo(const Range &);
@@ -100,6 +100,8 @@ public:
     void debugRenderer(khtml::RenderObject *r, bool selected) const;
 
     Selection &operator=(const Selection &o);
+    Selection &operator=(const Range &r) { moveTo(r); return *this; }
+    Selection &operator=(const Position &r) { moveTo(r); return *this; }
     
     friend bool operator==(const Selection &a, const Selection &b);
     friend bool operator!=(const Selection &a, const Selection &b);
@@ -107,7 +109,7 @@ public:
     friend class KHTMLPart;
 
 private:
-	enum EPositionType { START, END, BASE, EXTENT };
+    enum EPositionType { START, END, BASE, EXTENT };
 
     void init();
     void validate(ETextGranularity granularity=CHARACTER);
@@ -130,16 +132,16 @@ private:
     Position m_start;             // start position for the selection
     Position m_end;               // end position for the selection
 
-	EState m_state;               // the state of the selection
-	EAffinity m_affinity;         // the upstream/downstream affinity of the selection
+    EState m_state;               // the state of the selection
+    EAffinity m_affinity;         // the upstream/downstream affinity of the selection
 
-	int m_caretX;                 // caret coordinates and size
-	int m_caretY;
-	int m_caretSize;
+    int m_caretX;                 // caret coordinates and size
+    int m_caretY;
+    int m_caretSize;
 
-	bool m_baseIsStart : 1;       // true if base node is before the extent node
-	bool m_needsCaretLayout : 1;  // true if the caret position needs to be calculated
-	bool m_modifyBiasSet : 1;     // true if the selection has been horizontally 
+    bool m_baseIsStart : 1;       // true if base node is before the extent node
+    bool m_needsCaretLayout : 1;  // true if the caret position needs to be calculated
+    bool m_modifyBiasSet : 1;     // true if the selection has been horizontally 
                                   // modified with EAlter::EXTEND
 };
 
diff --git a/WebCore/khtml/editing/selection.cpp b/WebCore/khtml/editing/selection.cpp
index 6f1774f..086487a 100644
--- a/WebCore/khtml/editing/selection.cpp
+++ b/WebCore/khtml/editing/selection.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2003 Apple Computer, Inc.  All rights reserved.
+ * Copyright (C) 2004 Apple Computer, Inc.  All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -77,14 +77,24 @@ Selection::Selection()
 Selection::Selection(const Position &pos)
 {
     init();
-	assignBaseAndExtent(pos, pos);
+    assignBaseAndExtent(pos, pos);
+    validate();
+}
+
+Selection::Selection(const Range &r)
+{
+    const Position start(r.startContainer().handle(), r.startOffset());
+    const Position end(r.endContainer().handle(), r.endOffset());
+
+    init();
+    assignBaseAndExtent(start, end);
     validate();
 }
 
 Selection::Selection(const Position &base, const Position &extent)
 {
     init();
-	assignBaseAndExtent(base, extent);
+    assignBaseAndExtent(base, extent);
     validate();
 }
 
@@ -92,8 +102,8 @@ Selection::Selection(const Selection &o)
 {
     init();
     
-	assignBaseAndExtent(o.base(), o.extent());
-	assignStartAndEnd(o.start(), o.end());
+    assignBaseAndExtent(o.base(), o.extent());
+    assignStartAndEnd(o.start(), o.end());
 
     m_state = o.m_state;
     m_affinity = o.m_affinity;
@@ -129,8 +139,8 @@ void Selection::init()
 
 Selection &Selection::operator=(const Selection &o)
 {
-	assignBaseAndExtent(o.base(), o.extent());
-	assignStartAndEnd(o.start(), o.end());
+    assignBaseAndExtent(o.base(), o.extent());
+    assignStartAndEnd(o.start(), o.end());
 
     m_state = o.m_state;
     m_affinity = o.m_affinity;
@@ -166,22 +176,22 @@ void Selection::moveTo(const Range &r)
 {
     Position start(r.startContainer().handle(), r.startOffset());
     Position end(r.endContainer().handle(), r.endOffset());
-	moveTo(start, end);
+    moveTo(start, end);
 }
 
 void Selection::moveTo(const Selection &o)
 {
-	moveTo(o.start(), o.end());
+    moveTo(o.start(), o.end());
 }
 
 void Selection::moveTo(const Position &pos)
 {
-	moveTo(pos, pos);
+    moveTo(pos, pos);
 }
 
 void Selection::moveTo(const Position &base, const Position &extent)
 {
-	assignBaseAndExtent(base, extent);
+    assignBaseAndExtent(base, extent);
     validate();
 }
 
@@ -337,8 +347,8 @@ int Selection::xPosForVerticalArrowNavigation(EPositionType type, bool recalc) c
 
 void Selection::clear()
 {
-	assignBaseAndExtent(emptyPosition(), emptyPosition());
-	validate();
+    assignBaseAndExtent(emptyPosition(), emptyPosition());
+    validate();
 }
 
 void Selection::setBase(const Position &pos)
@@ -486,13 +496,13 @@ void Selection::validate(ETextGranularity granularity)
     }
 
     // make sure we do not have a dangling start or end
-	if (base().isEmpty() && extent().isEmpty()) {
+    if (base().isEmpty() && extent().isEmpty()) {
         assignStartAndEnd(emptyPosition(), emptyPosition());
         m_baseIsStart = true;
     }
-	else if (base().isEmpty() || extent().isEmpty()) {
+    else if (base().isEmpty() || extent().isEmpty()) {
         m_baseIsStart = true;
-	}
+    }
     else {
         // adjust m_baseIsStart as needed
         if (base().node() == extent().node()) {
@@ -572,13 +582,13 @@ void Selection::validate(ETextGranularity granularity)
     }
 #endif  // APPLE_CHANGES
 
-	// adjust the state
-	if (start().isEmpty() && end().isEmpty())
-		m_state = NONE;
-	else if (start() == end())
-		m_state = CARET;
-	else
-		m_state = RANGE;
+    // adjust the state
+    if (start().isEmpty() && end().isEmpty())
+        m_state = NONE;
+    else if (start() == end())
+        m_state = CARET;
+    else
+        m_state = RANGE;
 
     m_needsCaretLayout = true;
     
@@ -618,13 +628,13 @@ bool Selection::moveToRenderedContent()
 
 bool Selection::nodeIsBeforeNode(NodeImpl *n1, NodeImpl *n2) 
 {
-	if (!n1 || !n2) 
-		return true;
- 
- 	if (n1 == n2)
- 		return true;
- 
- 	bool result = false;
+    if (!n1 || !n2) 
+        return true;
+
+    if (n1 == n2)
+        return true;
+
+    bool result = false;
     int n1Depth = 0;
     int n2Depth = 0;
 
@@ -666,7 +676,7 @@ bool Selection::nodeIsBeforeNode(NodeImpl *n1, NodeImpl *n2)
         }
         n = n->nextSibling();
     }
-	return result;
+    return result;
 }
 
 #if APPLE_CHANGES
diff --git a/WebCore/khtml/editing/selection.h b/WebCore/khtml/editing/selection.h
index 05434bc..d131d40 100644
--- a/WebCore/khtml/editing/selection.h
+++ b/WebCore/khtml/editing/selection.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2003 Apple Computer, Inc.  All rights reserved.
+ * Copyright (C) 2004 Apple Computer, Inc.  All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -45,10 +45,10 @@ class Range;
 class Selection
 {
 public:
-	enum EState { NONE, CARET, RANGE };
-	enum EAlter { MOVE, EXTEND };
-	enum EDirection { FORWARD, BACKWARD, RIGHT, LEFT, UP, DOWN };
-	enum ETextGranularity { CHARACTER, WORD, LINE };
+    enum EState { NONE, CARET, RANGE };
+    enum EAlter { MOVE, EXTEND };
+    enum EDirection { FORWARD, BACKWARD, RIGHT, LEFT, UP, DOWN };
+    enum ETextGranularity { CHARACTER, WORD, LINE };
 
     // These match the AppKit values for these concepts.
     // From NSTextView.h:
@@ -57,13 +57,13 @@ public:
     enum EAffinity { UPSTREAM = 0, DOWNSTREAM = 1 };
 
     Selection();
+    Selection(const Range &);
     Selection(const Position &);
     Selection(const Position &, const Position &);
     Selection(const Selection &);
-    ~Selection() {}
 
-	EState state() const { return m_state; }
-	EAffinity affinity() const { return m_affinity; }
+    EState state() const { return m_state; }
+    EAffinity affinity() const { return m_affinity; }
     void setAffinity(EAffinity);
 
     void moveTo(const Range &);
@@ -100,6 +100,8 @@ public:
     void debugRenderer(khtml::RenderObject *r, bool selected) const;
 
     Selection &operator=(const Selection &o);
+    Selection &operator=(const Range &r) { moveTo(r); return *this; }
+    Selection &operator=(const Position &r) { moveTo(r); return *this; }
     
     friend bool operator==(const Selection &a, const Selection &b);
     friend bool operator!=(const Selection &a, const Selection &b);
@@ -107,7 +109,7 @@ public:
     friend class KHTMLPart;
 
 private:
-	enum EPositionType { START, END, BASE, EXTENT };
+    enum EPositionType { START, END, BASE, EXTENT };
 
     void init();
     void validate(ETextGranularity granularity=CHARACTER);
@@ -130,16 +132,16 @@ private:
     Position m_start;             // start position for the selection
     Position m_end;               // end position for the selection
 
-	EState m_state;               // the state of the selection
-	EAffinity m_affinity;         // the upstream/downstream affinity of the selection
+    EState m_state;               // the state of the selection
+    EAffinity m_affinity;         // the upstream/downstream affinity of the selection
 
-	int m_caretX;                 // caret coordinates and size
-	int m_caretY;
-	int m_caretSize;
+    int m_caretX;                 // caret coordinates and size
+    int m_caretY;
+    int m_caretSize;
 
-	bool m_baseIsStart : 1;       // true if base node is before the extent node
-	bool m_needsCaretLayout : 1;  // true if the caret position needs to be calculated
-	bool m_modifyBiasSet : 1;     // true if the selection has been horizontally 
+    bool m_baseIsStart : 1;       // true if base node is before the extent node
+    bool m_needsCaretLayout : 1;  // true if the caret position needs to be calculated
+    bool m_modifyBiasSet : 1;     // true if the selection has been horizontally 
                                   // modified with EAlter::EXTEND
 };
 
diff --git a/WebCore/khtml/editing/visible_text.cpp b/WebCore/khtml/editing/visible_text.cpp
new file mode 100644
index 0000000..98f2e44
--- /dev/null
+++ b/WebCore/khtml/editing/visible_text.cpp
@@ -0,0 +1,747 @@
+/*
+ * Copyright (C) 2004 Apple Computer, Inc.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+ */
+
+#include "khtml_text_operations.h"
+
+#include <misc/htmltags.h>
+#include <rendering/render_text.h>
+#include <xml/dom_nodeimpl.h>
+
+using DOM::DOMString;
+using DOM::Node;
+using DOM::NodeImpl;
+using DOM::Range;
+
+namespace khtml {
+
+const unsigned short nonBreakingSpace = 0xA0;
+
+// Iterates through the DOM range, returning all the text, and 0-length boundaries
+// at points where replaced elements break up the text flow.
+class TextIterator
+{
+public:
+    TextIterator();
+    explicit TextIterator(const DOM::Range &);
+
+    bool atEnd() const { return !m_positionNode; }
+    void advance();
+
+    long textLength() const { return m_textLength; }
+    const QChar *textCharacters() const { return m_textCharacters; }
+
+    DOM::Range position() const;
+
+private:
+    void exitNode();
+    bool handleTextNode();
+    bool handleReplacedElement();
+    bool handleNonTextNode();
+    void handleTextBox();
+    void emitCharacter(QChar, DOM::NodeImpl *textNode, long textStartOffset, long textEndOffset);
+
+    // Current position, not necessarily of the text being returned, but position
+    // as we walk through the DOM tree.
+    DOM::NodeImpl *m_node;
+    long m_offset;
+    bool m_handledNode;
+    bool m_handledChildren;
+
+    // End of the range.
+    DOM::NodeImpl *m_endNode;
+    long m_endOffset;
+
+    // The current text and its position, in the form to be returned from the iterator.
+    DOM::NodeImpl *m_positionNode;
+    long m_positionStartOffset;
+    long m_positionEndOffset;
+    const QChar *m_textCharacters;
+    long m_textLength;
+
+    // Used when there is still some pending text from the current node; when these
+    // are false and 0, we go back to normal iterating.
+    bool m_needAnotherNewline;
+    InlineTextBox *m_textBox;
+
+    // Used to do the whitespace collapsing logic.
+    DOM::NodeImpl *m_lastTextNode;    
+    bool m_lastTextNodeEndedWithCollapsedSpace;
+    QChar m_lastCharacter;
+
+    // Used for whitespace characters that aren't in the DOM, so we can point at them.
+    QChar m_singleCharacterBuffer;
+};
+
+// Builds on the text iterator, adding a character position so we can walk one
+// character at a time, or faster, as needed. Useful for searching.
+class CharacterIterator {
+public:
+    CharacterIterator();
+    explicit CharacterIterator(const DOM::Range &r);
+
+    void advance(long numCharacters);
+
+    bool atBreak() const { return m_atBreak; }
+    bool atEnd() const { return m_textIterator.atEnd(); }
+
+    long numCharacters() const { return m_textIterator.textLength() - m_runOffset; }
+    const QChar *characters() const { return m_textIterator.textCharacters() + m_runOffset; }
+
+    long characterOffset() const { return m_offset; }
+    Range position() const;
+
+private:
+    long m_offset;
+    long m_runOffset;
+    bool m_atBreak;
+
+    TextIterator m_textIterator;
+};
+
+// Buffer that knows how to compare with a search target.
+// Keeps enough of the previous text to be able to search in the future,
+// but no more.
+class CircularSearchBuffer {
+public:
+    CircularSearchBuffer(const QString &target, bool isCaseSensitive);
+    ~CircularSearchBuffer() { free(m_buffer); }
+
+    void clear() { m_cursor = m_buffer; m_bufferFull = false; }
+    void append(long length, const QChar *characters);
+    void append(const QChar &);
+
+    long neededCharacters() const;
+    bool isMatch() const;
+    long length() const { return m_target.length(); }
+
+private:
+    QString m_target;
+    bool m_isCaseSensitive;
+
+    QChar *m_buffer;
+    QChar *m_cursor;
+    bool m_bufferFull;
+
+    CircularSearchBuffer(const CircularSearchBuffer&);
+    CircularSearchBuffer &operator=(const CircularSearchBuffer&);
+};
+
+TextIterator::TextIterator() : m_positionNode(0)
+{
+}
+
+inline bool offsetInCharacters(unsigned short type)
+{
+    switch (type) {
+        case Node::ATTRIBUTE_NODE:
+        case Node::DOCUMENT_FRAGMENT_NODE:
+        case Node::DOCUMENT_NODE:
+        case Node::ELEMENT_NODE:
+        case Node::ENTITY_REFERENCE_NODE:
+            return false;
+
+        case Node::CDATA_SECTION_NODE:
+        case Node::COMMENT_NODE:
+        case Node::PROCESSING_INSTRUCTION_NODE:
+        case Node::TEXT_NODE:
+            return true;
+
+        case Node::DOCUMENT_TYPE_NODE:
+        case Node::ENTITY_NODE:
+        case Node::NOTATION_NODE:
+            assert(false); // should never be reached
+            return false;
+    }
+
+    assert(false); // should never be reached
+    return false;
+}
+
+TextIterator::TextIterator(const Range &r)
+{
+    if (r.isNull()) {
+        m_positionNode = 0;
+        return;
+    }
+
+    NodeImpl *startNode = r.startContainer().handle();
+    NodeImpl *endNode = r.endContainer().handle();
+    long startOffset = r.startOffset();
+    long endOffset = r.endOffset();
+
+    if (!offsetInCharacters(startNode->nodeType())) {
+        if (startOffset >= 0 && startOffset < static_cast<long>(startNode->childNodeCount())) {
+            startNode = startNode->childNode(startOffset);
+            startOffset = 0;
+        }
+    }
+    if (!offsetInCharacters(endNode->nodeType())) {
+        if (endOffset > 0 && endOffset <= static_cast<long>(endNode->childNodeCount())) {
+            endNode = endNode->childNode(endOffset - 1);
+            endOffset = LONG_MAX;
+        }
+    }
+
+    m_node = startNode;
+    m_offset = startOffset;
+    m_handledNode = false;
+    m_handledChildren = false;
+
+    m_endNode = endNode;
+    m_endOffset = endOffset;
+
+    m_needAnotherNewline = false;
+    m_textBox = 0;
+
+    m_lastTextNode = 0;
+    m_lastTextNodeEndedWithCollapsedSpace = false;
+    m_lastCharacter = '\n';
+
+#ifndef NDEBUG
+    // Need this just because of the assert.
+    m_positionNode = startNode;
+#endif
+
+    advance();
+}
+
+void TextIterator::advance()
+{
+    assert(m_positionNode);
+
+    m_positionNode = 0;
+    m_textLength = 0;
+
+    if (m_needAnotherNewline) {
+        // Emit the newline, with position a collapsed range at the end of current node.
+        long offset = m_node->nodeIndex();
+        emitCharacter('\n', m_node->parentNode(), offset + 1, offset + 1);
+        m_needAnotherNewline = false;
+        return;
+    }
+
+    if (m_textBox) {
+        handleTextBox();
+        if (m_positionNode) {
+            return;
+        }
+    }
+
+    while (m_node) {
+        if (!m_handledNode) {
+            RenderObject *renderer = m_node->renderer();
+            if (renderer && renderer->isText() && m_node->nodeType() == Node::TEXT_NODE) {
+                // FIXME: What about CDATA_SECTION_NODE?
+                if (renderer->style()->visibility() == VISIBLE) {
+                    m_handledNode = handleTextNode();
+                }
+            } else if (renderer && (renderer->isImage() || renderer->isWidget())) {
+                if (renderer->style()->visibility() == VISIBLE) {
+                    m_handledNode = handleReplacedElement();
+                }
+            } else {
+                m_handledNode = handleNonTextNode();
+            }
+            if (m_positionNode) {
+                return;
+            }
+        }
+
+        NodeImpl *next = m_handledChildren ? 0 : m_node->firstChild();
+        m_offset = 0;
+        if (!next && m_node != m_endNode) {
+            next = m_node->nextSibling();
+            while (!next && m_node->parentNode()) {
+                m_node = m_node->parentNode();
+                exitNode();
+                if (m_positionNode) {
+                    m_handledNode = true;
+                    m_handledChildren = true;
+                    return;
+                }
+                if (m_node == m_endNode) {
+                    break;
+                }
+                next = m_node->nextSibling();
+            }
+        }
+
+        m_node = next;
+        m_handledNode = false;
+        m_handledChildren = false;
+
+        if (m_positionNode) {
+            return;
+        }
+    }
+}
+
+bool TextIterator::handleTextNode()
+{
+    m_lastTextNode = m_node;
+
+    RenderText *renderer = static_cast<RenderText *>(m_node->renderer());
+    DOMString str = m_node->nodeValue();
+
+    if (renderer->style()->whiteSpace() == khtml::PRE) {
+        long runStart = m_offset;
+        if (m_lastTextNodeEndedWithCollapsedSpace) {
+            emitCharacter(' ', m_node, runStart, runStart);
+            return false;
+        }
+        long strLength = str.length();
+        long end = (m_node == m_endNode) ? m_endOffset : LONG_MAX;
+        long runEnd = kMin(strLength, end);
+
+        m_positionNode = m_node;
+        m_positionStartOffset = runStart;
+        m_positionEndOffset = runEnd;
+        m_textCharacters = str.unicode() + runStart;
+        m_textLength = runEnd - runStart;
+
+        m_lastCharacter = str[runEnd - 1];
+
+        return true;
+    }
+
+    if (!renderer->firstTextBox() && str.length() > 0) {
+        m_lastTextNodeEndedWithCollapsedSpace = true; // entire block is collapsed space
+        return true;
+    }
+
+    m_textBox = renderer->firstTextBox();
+    handleTextBox();
+    return true;
+}
+
+void TextIterator::handleTextBox()
+{    
+    RenderText *renderer = static_cast<RenderText *>(m_node->renderer());
+    DOMString str = m_node->nodeValue();
+    long start = m_offset;
+    long end = (m_node == m_endNode) ? m_endOffset : LONG_MAX;
+    for (; m_textBox; m_textBox = m_textBox->nextTextBox()) {
+        long textBoxStart = m_textBox->m_start;
+        long runStart = kMax(textBoxStart, start);
+
+        // Check for collapsed space at the start of this run.
+        bool needSpace = m_lastTextNodeEndedWithCollapsedSpace
+            || (m_textBox == renderer->firstTextBox() && textBoxStart == runStart && runStart > 0);
+        if (needSpace && !m_lastCharacter.isSpace()) {
+            emitCharacter(' ', m_node, runStart, runStart);
+            return;
+        }
+
+        long textBoxEnd = textBoxStart + m_textBox->m_len;
+        long runEnd = kMin(textBoxEnd, end);
+
+        if (runStart < runEnd) {
+            // Handle either a single newline character (which becomes a space),
+            // or a run of characters that does not include a newline.
+            // This effectively translates newlines to spaces without copying the text.
+            if (str[runStart] == '\n') {
+                emitCharacter(' ', m_node, runStart, runStart + 1);
+                m_offset = runStart + 1;
+            } else {
+                long subrunEnd = str.find('\n', runStart);
+                if (subrunEnd == -1 || subrunEnd > runEnd) {
+                    subrunEnd = runEnd;
+                }
+
+                m_offset = subrunEnd;
+
+                m_positionNode = m_node;
+                m_positionStartOffset = runStart;
+                m_positionEndOffset = subrunEnd;
+                m_textCharacters = str.unicode() + runStart;
+                m_textLength = subrunEnd - runStart;
+
+                m_lastCharacter = str[subrunEnd - 1];
+            }
+
+            // If we are doing a subrun that doesn't go to the end of the text box,
+            // come back again to finish handling this text box; don't advance to the next one.
+            if (m_positionEndOffset < runEnd) {
+                return;
+            }
+
+            // Advance to the next text box.
+            InlineTextBox *nextTextBox = m_textBox->nextTextBox();
+            long nextRunStart = nextTextBox ? nextTextBox->m_start : str.length();
+            if (nextRunStart > runEnd) {
+                m_lastTextNodeEndedWithCollapsedSpace = true; // collapsed space between runs or at the end
+            }
+            m_textBox = nextTextBox;
+            return;
+        }
+    }
+}
+
+bool TextIterator::handleReplacedElement()
+{
+    if (m_lastTextNodeEndedWithCollapsedSpace) {
+        long offset = m_lastTextNode->nodeIndex();
+        emitCharacter(' ', m_lastTextNode->parentNode(), offset + 1, offset + 1);
+        return false;
+    }
+
+    long offset = m_node->nodeIndex();
+
+    m_positionNode = m_node->parentNode();
+    m_positionStartOffset = offset;
+    m_positionEndOffset = offset + 1;
+
+    m_textCharacters = 0;
+    m_textLength = 0;
+
+    m_lastCharacter = 0;
+
+    return true;
+}
+
+bool TextIterator::handleNonTextNode()
+{
+    switch (m_node->id()) {
+        case ID_BR: {
+            long offset = m_node->nodeIndex();
+            emitCharacter('\n', m_node->parentNode(), offset, offset + 1);
+            break;
+        }
+
+        case ID_TD:
+        case ID_TH:
+            if (m_lastCharacter != '\n' && m_lastTextNode) {
+                long offset = m_lastTextNode->nodeIndex();
+                emitCharacter('\t', m_lastTextNode->parentNode(), offset, offset + 1);
+            }
+            break;
+
+        case ID_BLOCKQUOTE:
+        case ID_DD:
+        case ID_DIV:
+        case ID_DL:
+        case ID_DT:
+        case ID_H1:
+        case ID_H2:
+        case ID_H3:
+        case ID_H4:
+        case ID_H5:
+        case ID_H6:
+        case ID_HR:
+        case ID_LI:
+        case ID_OL:
+        case ID_P:
+        case ID_PRE:
+        case ID_TR:
+        case ID_UL:
+            if (m_lastCharacter != '\n' && m_lastTextNode) {
+                long offset = m_lastTextNode->nodeIndex();
+                emitCharacter('\n', m_lastTextNode->parentNode(), offset, offset + 1);
+            }
+            break;
+    }
+
+    return true;
+}
+
+void TextIterator::exitNode()
+{
+    bool endLine = false;
+    bool addNewline = false;
+
+    switch (m_node->id()) {
+        case ID_BLOCKQUOTE:
+        case ID_DD:
+        case ID_DIV:
+        case ID_DL:
+        case ID_DT:
+        case ID_HR:
+        case ID_LI:
+        case ID_OL:
+        case ID_PRE:
+        case ID_TR:
+        case ID_UL:
+            endLine = true;
+            break;
+
+        case ID_H1:
+        case ID_H2:
+        case ID_H3:
+        case ID_H4:
+        case ID_H5:
+        case ID_H6:
+        case ID_P:
+            endLine = true;
+            addNewline = true;
+            break;
+    }
+
+    if (endLine && m_lastCharacter != '\n' && m_lastTextNode) {
+        long offset = m_lastTextNode->nodeIndex();
+        emitCharacter('\n', m_lastTextNode->parentNode(), offset, offset + 1);
+        m_needAnotherNewline = addNewline;
+    } else if (addNewline && m_lastTextNode) {
+        long offset = m_node->childNodeCount();
+        emitCharacter('\n', m_node, offset, offset);
+    }
+}
+
+void TextIterator::emitCharacter(QChar c, NodeImpl *textNode, long textStartOffset, long textEndOffset)
+{
+    m_singleCharacterBuffer = c;
+    m_positionNode = textNode;
+    m_positionStartOffset = textStartOffset;
+    m_positionEndOffset = textEndOffset;
+    m_textCharacters = &m_singleCharacterBuffer;
+    m_textLength = 1;
+
+    m_lastTextNodeEndedWithCollapsedSpace = false;
+    m_lastCharacter = c;
+}
+
+Range TextIterator::position() const
+{
+    assert(m_positionNode);
+    return Range(m_positionNode, m_positionStartOffset, m_positionNode, m_positionEndOffset);
+}
+
+CharacterIterator::CharacterIterator()
+    : m_offset(0), m_runOffset(0), m_atBreak(true)
+{
+}
+
+CharacterIterator::CharacterIterator(const Range &r)
+    : m_offset(0), m_runOffset(0), m_atBreak(true), m_textIterator(r)
+{
+    while (!atEnd() && m_textIterator.textLength() == 0) {
+        m_textIterator.advance();
+    }
+}
+
+Range CharacterIterator::position() const
+{
+    Range r = m_textIterator.position();
+    if (m_textIterator.textLength() <= 1) {
+        assert(m_runOffset == 0);
+    } else {
+        Node n = r.startContainer();
+        assert(n == r.endContainer());
+        long offset = r.startOffset() + m_runOffset;
+        r.setStart(n, offset);
+        r.setEnd(n, offset + 1);
+    }
+    return r;
+}
+
+void CharacterIterator::advance(long count)
+{
+    assert(!atEnd());
+
+    m_atBreak = false;
+
+    long remaining = m_textIterator.textLength() - m_runOffset;
+    if (count < remaining) {
+        m_runOffset += count;
+        m_offset += count;
+        return;
+    }
+
+    count -= remaining;
+    m_offset += remaining;
+    for (m_textIterator.advance(); !atEnd(); m_textIterator.advance()) {
+        long runLength = m_textIterator.textLength();
+        if (runLength == 0) {
+            m_atBreak = true;
+        } else {
+            if (count < runLength) {
+                m_runOffset = count;
+                m_offset += count;
+                return;
+            }
+            count -= runLength;
+            m_offset += runLength;
+        }
+    }
+
+    m_atBreak = true;
+    m_runOffset = 0;
+}
+
+CircularSearchBuffer::CircularSearchBuffer(const QString &s, bool isCaseSensitive)
+    : m_target(s)
+{
+    assert(!s.isEmpty());
+
+    if (!isCaseSensitive) {
+        m_target = s.lower();
+    }
+    m_target.replace(nonBreakingSpace, ' ');
+    m_isCaseSensitive = isCaseSensitive;
+
+    m_buffer = static_cast<QChar *>(malloc(s.length() * sizeof(QChar)));
+    m_cursor = m_buffer;
+    m_bufferFull = false;
+}
+
+void CircularSearchBuffer::append(const QChar &c)
+{
+    if (m_isCaseSensitive) {
+        *m_cursor++ = c.unicode() == nonBreakingSpace ? ' ' : c.unicode();
+    } else {
+        *m_cursor++ = c.unicode() == nonBreakingSpace ? ' ' : c.lower().unicode();
+    }
+    if (m_cursor == m_buffer + length()) {
+        m_cursor = m_buffer;
+        m_bufferFull = true;
+    }
+}
+
+// This function can only be used when the buffer is not yet full,
+// and when then count is small enough to fit in the buffer.
+// No need for a more general version for the search algorithm.
+void CircularSearchBuffer::append(long count, const QChar *characters)
+{
+    long tailSpace = m_buffer + length() - m_cursor;
+
+    assert(!m_bufferFull);
+    assert(count <= tailSpace);
+
+    if (m_isCaseSensitive) {
+        for (long i = 0; i != count; ++i) {
+            QChar c = characters[i];
+            m_cursor[i] = c.unicode() == nonBreakingSpace ? ' ' : c.unicode();
+        }
+    } else {
+        for (long i = 0; i != count; ++i) {
+            QChar c = characters[i];
+            m_cursor[i] = c.unicode() == nonBreakingSpace ? ' ' : c.lower().unicode();
+        }
+    }
+    if (count < tailSpace) {
+        m_cursor += count;
+    } else {
+        m_bufferFull = true;
+        m_cursor = m_buffer;
+    }
+}
+
+long CircularSearchBuffer::neededCharacters() const
+{
+    return m_bufferFull ? 0 : m_buffer + length() - m_cursor;
+}
+
+bool CircularSearchBuffer::isMatch() const
+{
+    assert(m_bufferFull);
+
+    long headSpace = m_cursor - m_buffer;
+    long tailSpace = length() - headSpace;
+    return memcmp(m_cursor, m_target.unicode(), tailSpace * sizeof(QChar)) == 0
+        && memcmp(m_buffer, m_target.unicode() + tailSpace, headSpace * sizeof(QChar)) == 0;
+}
+
+QString plainText(const Range &r)
+{
+    // Allocate string at the right size, rather than building it up by successive append calls.
+    long length = 0;
+    for (TextIterator it(r); !it.atEnd(); it.advance()) {
+        length += it.textLength();
+    }
+    QString result("");
+    result.reserve(length);
+    for (TextIterator it(r); !it.atEnd(); it.advance()) {
+        result.append(it.textCharacters(), it.textLength());
+    }
+    return result;
+}
+
+Range findPlainText(const Range &r, const QString &s, bool forward, bool caseSensitive)
+{
+    // FIXME: Can we do Boyer-Moore or equivalent instead for speed?
+
+    // FIXME: This code does not allow \n at the moment because of issues with <br>.
+    // Once we fix those, we can remove this check.
+    if (s.isEmpty() || s.find('\n') != -1) {
+        Range result = r;
+        result.collapse(forward);
+        return result;
+    }
+
+    CircularSearchBuffer buffer(s, caseSensitive);
+
+    bool found = false;
+    CharacterIterator rangeEnd;
+
+    {
+        CharacterIterator it(r);
+        while (!it.atEnd()) {
+            // Fill the buffer.
+            while (long needed = buffer.neededCharacters()) {
+                long available = it.numCharacters();
+                long runLength = kMin(needed, available);
+                buffer.append(runLength, it.characters());
+                it.advance(runLength);
+                if (it.atBreak()) {
+                    if (it.atEnd()) {
+                        goto done;
+                    }
+                    buffer.clear();
+                }
+            }
+
+            // Do the search.
+            do {
+                if (buffer.isMatch()) {
+                    // Compute the range for the result.
+                    found = true;
+                    rangeEnd = it;
+                    // If searching forward, stop on the first match.
+                    // If searching backward, don't stop, so we end up with the last match.
+                    if (forward) {
+                        goto done;
+                    }
+                }
+                buffer.append(it.characters()[0]);
+                it.advance(1);
+            } while (!it.atBreak());
+            buffer.clear();
+        }
+    }
+
+done:
+    Range result = r;
+    if (!found) {
+        result.collapse(!forward);
+    } else {
+        CharacterIterator it(r);
+        it.advance(rangeEnd.characterOffset() - buffer.length());
+        result.setStart(it.position().startContainer(), it.position().startOffset());
+        it.advance(buffer.length() - 1);
+        result.setEnd(it.position().endContainer(), it.position().endOffset());
+    }
+    return result;
+}
+
+}
diff --git a/JavaScriptCore/bindings/objc/objc_header.h b/WebCore/khtml/editing/visible_text.h
similarity index 69%
copy from JavaScriptCore/bindings/objc/objc_header.h
copy to WebCore/khtml/editing/visible_text.h
index 2dd7a26..3e8a705 100644
--- a/JavaScriptCore/bindings/objc/objc_header.h
+++ b/WebCore/khtml/editing/visible_text.h
@@ -22,26 +22,22 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
  */
-#ifndef _BINDINGS_OBJC_HEADER_H_
-#define _BINDINGS_OBJC_HEADER_H_
 
-#ifdef __OBJC__
-#include <objc/objc-class.h>
-#include <objc/objc-runtime.h>
+#ifndef __khtml_text_iterator_h__
+#define __khtml_text_iterator_h__
 
-typedef struct objc_class *ClassStructPtr;
-typedef struct objc_object *ObjectStructPtr;
+#include <dom/dom2_range.h>
 
- at class NSMethodSignature;
+// FIXME: This class should probably use the render tree and not the DOM tree, since elements could
+// be hidden using CSS, or additional generated content could be added.  For now, we just make sure
+// text objects walk their renderers' InlineTextBox objects, so that we at least get the whitespace 
+// stripped out properly and obey CSS visibility for text runs.
 
-#else
+namespace khtml {
 
-typedef struct objc_ivar {} *Ivar;
-typedef struct objc_class {} *ClassStructPtr;
-typedef struct objc_object {} *ObjectStructPtr;
+QString plainText(const DOM::Range &);
+DOM::Range findPlainText(const DOM::Range &, const QString &, bool forward, bool caseSensitive);
 
-class NSMethodSignature;
+}
 
 #endif
-
-#endif
\ No newline at end of file
diff --git a/WebCore/khtml/khtml_part.cpp b/WebCore/khtml/khtml_part.cpp
index 1383c3e..afb036a 100644
--- a/WebCore/khtml/khtml_part.cpp
+++ b/WebCore/khtml/khtml_part.cpp
@@ -7,7 +7,7 @@
  *                     2000 Simon Hausmann <hausmann at kde.org>
  *                     2000 Stefan Schimanski <1Stein at gmx.de>
  *                     2001 George Staikos <staikos at kde.org>
- * Copyright (C) 2003 Apple Computer, Inc.
+ * Copyright (C) 2004 Apple Computer, Inc.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Library General Public
@@ -53,6 +53,8 @@
 #include "xml/xml_tokenizer.h"
 #include "css/cssstyleselector.h"
 #include "css/csshelper.h"
+#include "misc/khtml_text_operations.h"
+
 using namespace DOM;
 
 #include "khtmlview.h"
@@ -95,7 +97,7 @@ using namespace DOM;
 
 #include "khtmlpart_p.h"
 
-#ifdef APPLE_CHANGES
+#if APPLE_CHANGES
 #include <CoreServices/CoreServices.h>
 #endif
 
@@ -104,6 +106,7 @@ using khtml::DeleteSelectionCommand;
 using khtml::EditCommand;
 using khtml::InlineTextBox;
 using khtml::PasteMarkupCommand;
+using khtml::plainText;
 using khtml::RenderObject;
 using khtml::RenderText;
 using khtml::Tokenizer;
@@ -274,7 +277,9 @@ void KHTMLPart::init( KHTMLView *view, GUIProfile prof )
   connect( khtml::Cache::loader(), SIGNAL( requestFailed( khtml::DocLoader*, khtml::CachedObject *) ),
            this, SLOT( slotLoaderRequestDone( khtml::DocLoader*, khtml::CachedObject *) ) );
 
+#if !APPLE_CHANGES
   findTextBegin(); //reset find variables
+#endif
 
   connect( &d->m_redirectionTimer, SIGNAL( timeout() ),
            this, SLOT( slotRedirect() ) );
@@ -977,10 +982,10 @@ void KHTMLPart::clear()
         (*it).m_run->abort();
     }
   }
-#endif
 
 
   findTextBegin(); // resets d->m_findNode and d->m_findPos
+#endif
 
   d->m_mousePressNode = DOM::Node();
 
@@ -1058,9 +1063,11 @@ void KHTMLPart::clear()
   connect( kapp->clipboard(), SIGNAL( selectionChanged()), SLOT( slotClearSelection()));
 #endif
 
+#if !APPLE_CHANGES
   d->m_totalObjectCount = 0;
   d->m_loadedObjects = 0;
   d->m_jobPercent = 0;
+#endif
 
   if ( !d->m_haveEncoding )
     d->m_encoding = QString::null;
@@ -2165,6 +2172,8 @@ bool KHTMLPart::inEditMode() const
     return editMode() == FlagEnabled;
 }
 
+#if !APPLE_CHANGES
+
 void KHTMLPart::findTextBegin(NodeImpl *startNode, int startPos)
 {
     d->m_findPos = startPos;
@@ -2203,7 +2212,6 @@ bool KHTMLPart::findTextNext( const QString &str, bool forward, bool caseSensiti
             QConstString s(t->s, t->l);
 
             int matchLen = 0;
-#if !APPLE_CHANGES
             if ( isRegExp ) {
               QRegExp matcher( str );
               matcher.setCaseSensitive( caseSensitive );
@@ -2215,31 +2223,13 @@ bool KHTMLPart::findTextNext( const QString &str, bool forward, bool caseSensiti
               d->m_findPos = s.string().find(str, d->m_findPos+1, caseSensitive);
               matchLen = str.length();
             }
-#else
-            if (forward) {
-                d->m_findPos = s.string().find(str, d->m_findPos+1, caseSensitive);
-            } else {
-                if (d->m_findPos == -1) {
-                    // search from end of node
-                    d->m_findPos = s.string().findRev(str, -1, caseSensitive);
-                } else if (d->m_findPos != 0) {
-                    d->m_findPos = s.string().findRev(str, d->m_findPos-1, caseSensitive);
-                } else {
-                    // already at start of this node, on to the next node
-                    d->m_findPos = -1;
-                }
-            }
-            matchLen = str.length();
-#endif
 
             if(d->m_findPos != -1)
             {
-#if !APPLE_CHANGES
                 int x = 0, y = 0;
                 static_cast<khtml::RenderText *>(d->m_findNode->renderer())
                   ->posOfChar(d->m_findPos, x, y);
                 d->m_view->setContentsPos(x-50, y-50);
-#endif
                 Position p1(d->m_findNode, d->m_findPos);
                 Position p2(d->m_findNode, d->m_findPos + matchLen);
                 setSelection(Selection(p1, p2));
@@ -2282,188 +2272,11 @@ bool KHTMLPart::findTextNext( const QString &str, bool forward, bool caseSensiti
     }
 }
 
+#endif // APPLE_CHANGES
+
 QString KHTMLPart::text(const DOM::Range &r) const
 {
-  // FIXME: This whole function should use the render tree and not the DOM tree, since elements could
-  // be hidden using CSS, or additional generated content could be added.  For now, we just make sure
-  // text objects walk their renderers' InlineTextBox objects, so that we at least get the whitespace 
-  // stripped out properly and obey CSS visibility for text runs.
-
-  if (r.isNull())
-    return QString();
-
-  bool hasNewLine = true;
-  bool addedSpace = true;
-  bool needSpace = false;
-  QString text;
-  DOM::Node startNode = r.startContainer();
-  DOM::Node endNode = r.endContainer();
-  int startOffset = r.startOffset();
-  int endOffset = r.endOffset();
-  if (!startNode.isNull() && startNode.nodeType() == Node::ELEMENT_NODE) {
-      if (startOffset >= 0 && startOffset < (int)startNode.childNodes().length()) {
-          startNode = startNode.childNodes().item(r.startOffset());
-          startOffset = -1;
-      }
-  }
-  if (!endNode.isNull() && endNode.nodeType() == Node::ELEMENT_NODE) {
-      if (endOffset > 0 && endOffset <= (int)endNode.childNodes().length()) {
-          endNode = endNode.childNodes().item(endOffset - 1);
-          endOffset = -1;
-      }
-  }
-
-  DOM::Node n = startNode;
-  while(!n.isNull()) {
-      if(n.nodeType() == DOM::Node::TEXT_NODE) {
-          if (hasNewLine) {
-              addedSpace = true;
-              needSpace = false;
-              hasNewLine = false;
-          }
-          QString str = n.nodeValue().string();
-          int start = (n == startNode) ? startOffset : -1;
-          int end = (n == endNode) ? endOffset : -1;
-          RenderObject* renderer = n.handle()->renderer();
-          if (renderer && renderer->isText()) {
-              if (renderer->style()->whiteSpace() == khtml::PRE) {
-                  if (needSpace && !addedSpace)
-                      text += ' ';
-                  int runStart = (start == -1) ? 0 : start;
-                  int runEnd = (end == -1) ? str.length() : end;
-                  text += str.mid(runStart, runEnd-runStart);
-                  needSpace = false;
-                  addedSpace = str[runEnd-1].direction() == QChar::DirWS;
-              }
-              else {
-                  RenderText* textObj = static_cast<RenderText*>(n.handle()->renderer());
-                  if (!textObj->firstTextBox() && str.length() > 0) {
-                      // We have no runs, but we do have a length.  This means we must be
-                      // whitespace that collapsed away at the end of a line.
-                      needSpace = true;
-                  }
-                  else {
-                      for (InlineTextBox* box = textObj->firstTextBox(); box; box = box->nextTextBox()) {
-                          int runStart = (start == -1) ? box->m_start : start;
-                          int runEnd = (end == -1) ? box->m_start + box->m_len : end;
-                          runEnd = QMIN(runEnd, box->m_start + box->m_len);
-                          if (runStart >= box->m_start &&
-                              runStart < box->m_start + box->m_len) {
-                              if (box == textObj->firstTextBox() && box->m_start == runStart && runStart > 0)
-                                  needSpace = true; // collapsed space at the start
-                              if (needSpace && !addedSpace)
-                                  text += ' ';
-                              QString runText = str.mid(runStart, runEnd - runStart);
-                              runText.replace('\n', ' ');
-                              text += runText;
-                              int nextRunStart = box->nextTextBox() ? box->nextTextBox()->m_start : str.length(); // collapsed space between runs or at the end
-                              needSpace = nextRunStart > runEnd; // collapsed space between runs or at the end
-                              addedSpace = str[runEnd-1].direction() == QChar::DirWS;
-                              start = -1;
-                          }
-                          if (end != -1 && runEnd >= end)
-                              break;
-                      }
-                  }
-              }
-          }
-      }
-      else {
-        // This is our simple HTML -> ASCII transformation:
-        unsigned short id = n.elementId();
-        switch(id) {
-          case ID_BR:
-            text += "\n";
-            hasNewLine = true;
-            break;
-
-          case ID_TD:
-          case ID_TH:
-          case ID_HR:
-          case ID_OL:
-          case ID_UL:
-          case ID_LI:
-          case ID_DD:
-          case ID_DL:
-          case ID_DT:
-          case ID_PRE:
-          case ID_BLOCKQUOTE:
-          case ID_DIV:
-            if (!hasNewLine)
-               text += "\n";
-            hasNewLine = true;
-            break;
-          case ID_P:
-          case ID_TR:
-          case ID_H1:
-          case ID_H2:
-          case ID_H3:
-          case ID_H4:
-          case ID_H5:
-          case ID_H6:
-            if (!hasNewLine)
-               text += "\n";
-            text += "\n";
-            hasNewLine = true;
-            break;
-        }
-      }
-      if(n == endNode) break;
-      DOM::Node next = n.firstChild();
-      if(next.isNull()) next = n.nextSibling();
-      while( next.isNull() && !n.parentNode().isNull() ) {
-        n = n.parentNode();
-        if(n == endNode) break;
-        next = n.nextSibling();
-        unsigned short id = n.elementId();
-        switch(id) {
-          case ID_TD:
-          case ID_TH:
-          case ID_HR:
-          case ID_OL:
-          case ID_UL:
-          case ID_LI:
-          case ID_DD:
-          case ID_DL:
-          case ID_DT:
-          case ID_PRE:
-          case ID_BLOCKQUOTE:
-          case ID_DIV:
-            if (!hasNewLine)
-               text += "\n";
-            hasNewLine = true;
-            break;
-          case ID_P:
-          case ID_TR:
-          case ID_H1:
-          case ID_H2:
-          case ID_H3:
-          case ID_H4:
-          case ID_H5:
-          case ID_H6:
-            if (!hasNewLine)
-               text += "\n";
-            // An extra newline is needed at the start, not the end, of these types of tags,
-            // so don't add another here.
-            hasNewLine = true;
-            break;
-        }
-      }
-
-      n = next;
-    }
-    int start = 0;
-    int end = text.length();
-
-    // Strip leading LFs
-    while ((start < end) && (text[start] == '\n'))
-       start++;
-
-    // Strip excessive trailing LFs
-    while ((start < (end-1)) && (text[end-1] == '\n') && (text[end-2] == '\n'))
-       end--;
-       
-    return text.mid(start, end-start);
+    return plainText(r);
 }
 
 QString KHTMLPart::selectedText() const
@@ -2557,7 +2370,7 @@ void KHTMLPart::setFocusNodeIfNeeded(const Selection &s)
         return;
 
     NodeImpl *n = s.start().node();
-    NodeImpl *target = n->isContentEditable() ? n : 0;
+    NodeImpl *target = (n && n->isContentEditable()) ? n : 0;
     if (!target) {
         while (n && n != s.end().node()) {
             if (n->isContentEditable()) {
diff --git a/WebCore/khtml/khtmlpart_p.h b/WebCore/khtml/khtmlpart_p.h
index 5407e85..bb4d65f 100644
--- a/WebCore/khtml/khtmlpart_p.h
+++ b/WebCore/khtml/khtmlpart_p.h
@@ -9,7 +9,7 @@
  *                     2000-2001 Simon Hausmann <hausmann at kde.org>
  *                     2000-2001 Dirk Mueller <mueller at kde.org>
  *                     2000 Stefan Schimanski <1Stein at gmx.de>
- * Copyright (C) 2003 Apple Computer, Inc.
+ * Copyright (C) 2004 Apple Computer, Inc.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Library General Public
@@ -123,10 +123,10 @@ public:
     m_bDnd = true;
 #if !APPLE_CHANGES
     m_linkCursor = KCursor::handCursor();
-#endif
     m_loadedObjects = 0;
     m_totalObjectCount = 0;
     m_jobPercent = 0;
+#endif
     m_haveEncoding = false;
     m_activeFrame = 0L;
 #if !APPLE_CHANGES
@@ -322,8 +322,10 @@ public:
 
   int m_zoomFactor;
 
+#if !APPLE_CHANGES
   int m_findPos;
   DOM::NodeImpl *m_findNode;
+#endif
 
   QString m_strSelectedURL;
   QString m_strSelectedURLTarget;
@@ -348,8 +350,10 @@ public:
   DOM::Selection::ETextGranularity m_textElement;
   bool m_mouseMovedSinceLastMousePress:1;
 #endif
+#if !APPLE_CHANGES
   QString m_overURL;
   QString m_overURLTarget;
+#endif
 
   DOM::Selection m_selection;
   int m_caretBlinkTimer;
@@ -377,14 +381,12 @@ public:
 
 #if !APPLE_CHANGES
   QCursor m_linkCursor;
-#endif
   QTimer m_scrollTimer;
 
   unsigned long m_loadedObjects;
   unsigned long m_totalObjectCount;
   unsigned int m_jobPercent;
 
-#if !APPLE_CHANGES
   KHTMLFind *m_findDialog;
 
   struct findState
diff --git a/WebCore/khtml/misc/khtml_text_operations.cpp b/WebCore/khtml/misc/khtml_text_operations.cpp
new file mode 100644
index 0000000..98f2e44
--- /dev/null
+++ b/WebCore/khtml/misc/khtml_text_operations.cpp
@@ -0,0 +1,747 @@
+/*
+ * Copyright (C) 2004 Apple Computer, Inc.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+ */
+
+#include "khtml_text_operations.h"
+
+#include <misc/htmltags.h>
+#include <rendering/render_text.h>
+#include <xml/dom_nodeimpl.h>
+
+using DOM::DOMString;
+using DOM::Node;
+using DOM::NodeImpl;
+using DOM::Range;
+
+namespace khtml {
+
+const unsigned short nonBreakingSpace = 0xA0;
+
+// Iterates through the DOM range, returning all the text, and 0-length boundaries
+// at points where replaced elements break up the text flow.
+class TextIterator
+{
+public:
+    TextIterator();
+    explicit TextIterator(const DOM::Range &);
+
+    bool atEnd() const { return !m_positionNode; }
+    void advance();
+
+    long textLength() const { return m_textLength; }
+    const QChar *textCharacters() const { return m_textCharacters; }
+
+    DOM::Range position() const;
+
+private:
+    void exitNode();
+    bool handleTextNode();
+    bool handleReplacedElement();
+    bool handleNonTextNode();
+    void handleTextBox();
+    void emitCharacter(QChar, DOM::NodeImpl *textNode, long textStartOffset, long textEndOffset);
+
+    // Current position, not necessarily of the text being returned, but position
+    // as we walk through the DOM tree.
+    DOM::NodeImpl *m_node;
+    long m_offset;
+    bool m_handledNode;
+    bool m_handledChildren;
+
+    // End of the range.
+    DOM::NodeImpl *m_endNode;
+    long m_endOffset;
+
+    // The current text and its position, in the form to be returned from the iterator.
+    DOM::NodeImpl *m_positionNode;
+    long m_positionStartOffset;
+    long m_positionEndOffset;
+    const QChar *m_textCharacters;
+    long m_textLength;
+
+    // Used when there is still some pending text from the current node; when these
+    // are false and 0, we go back to normal iterating.
+    bool m_needAnotherNewline;
+    InlineTextBox *m_textBox;
+
+    // Used to do the whitespace collapsing logic.
+    DOM::NodeImpl *m_lastTextNode;    
+    bool m_lastTextNodeEndedWithCollapsedSpace;
+    QChar m_lastCharacter;
+
+    // Used for whitespace characters that aren't in the DOM, so we can point at them.
+    QChar m_singleCharacterBuffer;
+};
+
+// Builds on the text iterator, adding a character position so we can walk one
+// character at a time, or faster, as needed. Useful for searching.
+class CharacterIterator {
+public:
+    CharacterIterator();
+    explicit CharacterIterator(const DOM::Range &r);
+
+    void advance(long numCharacters);
+
+    bool atBreak() const { return m_atBreak; }
+    bool atEnd() const { return m_textIterator.atEnd(); }
+
+    long numCharacters() const { return m_textIterator.textLength() - m_runOffset; }
+    const QChar *characters() const { return m_textIterator.textCharacters() + m_runOffset; }
+
+    long characterOffset() const { return m_offset; }
+    Range position() const;
+
+private:
+    long m_offset;
+    long m_runOffset;
+    bool m_atBreak;
+
+    TextIterator m_textIterator;
+};
+
+// Buffer that knows how to compare with a search target.
+// Keeps enough of the previous text to be able to search in the future,
+// but no more.
+class CircularSearchBuffer {
+public:
+    CircularSearchBuffer(const QString &target, bool isCaseSensitive);
+    ~CircularSearchBuffer() { free(m_buffer); }
+
+    void clear() { m_cursor = m_buffer; m_bufferFull = false; }
+    void append(long length, const QChar *characters);
+    void append(const QChar &);
+
+    long neededCharacters() const;
+    bool isMatch() const;
+    long length() const { return m_target.length(); }
+
+private:
+    QString m_target;
+    bool m_isCaseSensitive;
+
+    QChar *m_buffer;
+    QChar *m_cursor;
+    bool m_bufferFull;
+
+    CircularSearchBuffer(const CircularSearchBuffer&);
+    CircularSearchBuffer &operator=(const CircularSearchBuffer&);
+};
+
+TextIterator::TextIterator() : m_positionNode(0)
+{
+}
+
+inline bool offsetInCharacters(unsigned short type)
+{
+    switch (type) {
+        case Node::ATTRIBUTE_NODE:
+        case Node::DOCUMENT_FRAGMENT_NODE:
+        case Node::DOCUMENT_NODE:
+        case Node::ELEMENT_NODE:
+        case Node::ENTITY_REFERENCE_NODE:
+            return false;
+
+        case Node::CDATA_SECTION_NODE:
+        case Node::COMMENT_NODE:
+        case Node::PROCESSING_INSTRUCTION_NODE:
+        case Node::TEXT_NODE:
+            return true;
+
+        case Node::DOCUMENT_TYPE_NODE:
+        case Node::ENTITY_NODE:
+        case Node::NOTATION_NODE:
+            assert(false); // should never be reached
+            return false;
+    }
+
+    assert(false); // should never be reached
+    return false;
+}
+
+TextIterator::TextIterator(const Range &r)
+{
+    if (r.isNull()) {
+        m_positionNode = 0;
+        return;
+    }
+
+    NodeImpl *startNode = r.startContainer().handle();
+    NodeImpl *endNode = r.endContainer().handle();
+    long startOffset = r.startOffset();
+    long endOffset = r.endOffset();
+
+    if (!offsetInCharacters(startNode->nodeType())) {
+        if (startOffset >= 0 && startOffset < static_cast<long>(startNode->childNodeCount())) {
+            startNode = startNode->childNode(startOffset);
+            startOffset = 0;
+        }
+    }
+    if (!offsetInCharacters(endNode->nodeType())) {
+        if (endOffset > 0 && endOffset <= static_cast<long>(endNode->childNodeCount())) {
+            endNode = endNode->childNode(endOffset - 1);
+            endOffset = LONG_MAX;
+        }
+    }
+
+    m_node = startNode;
+    m_offset = startOffset;
+    m_handledNode = false;
+    m_handledChildren = false;
+
+    m_endNode = endNode;
+    m_endOffset = endOffset;
+
+    m_needAnotherNewline = false;
+    m_textBox = 0;
+
+    m_lastTextNode = 0;
+    m_lastTextNodeEndedWithCollapsedSpace = false;
+    m_lastCharacter = '\n';
+
+#ifndef NDEBUG
+    // Need this just because of the assert.
+    m_positionNode = startNode;
+#endif
+
+    advance();
+}
+
+void TextIterator::advance()
+{
+    assert(m_positionNode);
+
+    m_positionNode = 0;
+    m_textLength = 0;
+
+    if (m_needAnotherNewline) {
+        // Emit the newline, with position a collapsed range at the end of current node.
+        long offset = m_node->nodeIndex();
+        emitCharacter('\n', m_node->parentNode(), offset + 1, offset + 1);
+        m_needAnotherNewline = false;
+        return;
+    }
+
+    if (m_textBox) {
+        handleTextBox();
+        if (m_positionNode) {
+            return;
+        }
+    }
+
+    while (m_node) {
+        if (!m_handledNode) {
+            RenderObject *renderer = m_node->renderer();
+            if (renderer && renderer->isText() && m_node->nodeType() == Node::TEXT_NODE) {
+                // FIXME: What about CDATA_SECTION_NODE?
+                if (renderer->style()->visibility() == VISIBLE) {
+                    m_handledNode = handleTextNode();
+                }
+            } else if (renderer && (renderer->isImage() || renderer->isWidget())) {
+                if (renderer->style()->visibility() == VISIBLE) {
+                    m_handledNode = handleReplacedElement();
+                }
+            } else {
+                m_handledNode = handleNonTextNode();
+            }
+            if (m_positionNode) {
+                return;
+            }
+        }
+
+        NodeImpl *next = m_handledChildren ? 0 : m_node->firstChild();
+        m_offset = 0;
+        if (!next && m_node != m_endNode) {
+            next = m_node->nextSibling();
+            while (!next && m_node->parentNode()) {
+                m_node = m_node->parentNode();
+                exitNode();
+                if (m_positionNode) {
+                    m_handledNode = true;
+                    m_handledChildren = true;
+                    return;
+                }
+                if (m_node == m_endNode) {
+                    break;
+                }
+                next = m_node->nextSibling();
+            }
+        }
+
+        m_node = next;
+        m_handledNode = false;
+        m_handledChildren = false;
+
+        if (m_positionNode) {
+            return;
+        }
+    }
+}
+
+bool TextIterator::handleTextNode()
+{
+    m_lastTextNode = m_node;
+
+    RenderText *renderer = static_cast<RenderText *>(m_node->renderer());
+    DOMString str = m_node->nodeValue();
+
+    if (renderer->style()->whiteSpace() == khtml::PRE) {
+        long runStart = m_offset;
+        if (m_lastTextNodeEndedWithCollapsedSpace) {
+            emitCharacter(' ', m_node, runStart, runStart);
+            return false;
+        }
+        long strLength = str.length();
+        long end = (m_node == m_endNode) ? m_endOffset : LONG_MAX;
+        long runEnd = kMin(strLength, end);
+
+        m_positionNode = m_node;
+        m_positionStartOffset = runStart;
+        m_positionEndOffset = runEnd;
+        m_textCharacters = str.unicode() + runStart;
+        m_textLength = runEnd - runStart;
+
+        m_lastCharacter = str[runEnd - 1];
+
+        return true;
+    }
+
+    if (!renderer->firstTextBox() && str.length() > 0) {
+        m_lastTextNodeEndedWithCollapsedSpace = true; // entire block is collapsed space
+        return true;
+    }
+
+    m_textBox = renderer->firstTextBox();
+    handleTextBox();
+    return true;
+}
+
+void TextIterator::handleTextBox()
+{    
+    RenderText *renderer = static_cast<RenderText *>(m_node->renderer());
+    DOMString str = m_node->nodeValue();
+    long start = m_offset;
+    long end = (m_node == m_endNode) ? m_endOffset : LONG_MAX;
+    for (; m_textBox; m_textBox = m_textBox->nextTextBox()) {
+        long textBoxStart = m_textBox->m_start;
+        long runStart = kMax(textBoxStart, start);
+
+        // Check for collapsed space at the start of this run.
+        bool needSpace = m_lastTextNodeEndedWithCollapsedSpace
+            || (m_textBox == renderer->firstTextBox() && textBoxStart == runStart && runStart > 0);
+        if (needSpace && !m_lastCharacter.isSpace()) {
+            emitCharacter(' ', m_node, runStart, runStart);
+            return;
+        }
+
+        long textBoxEnd = textBoxStart + m_textBox->m_len;
+        long runEnd = kMin(textBoxEnd, end);
+
+        if (runStart < runEnd) {
+            // Handle either a single newline character (which becomes a space),
+            // or a run of characters that does not include a newline.
+            // This effectively translates newlines to spaces without copying the text.
+            if (str[runStart] == '\n') {
+                emitCharacter(' ', m_node, runStart, runStart + 1);
+                m_offset = runStart + 1;
+            } else {
+                long subrunEnd = str.find('\n', runStart);
+                if (subrunEnd == -1 || subrunEnd > runEnd) {
+                    subrunEnd = runEnd;
+                }
+
+                m_offset = subrunEnd;
+
+                m_positionNode = m_node;
+                m_positionStartOffset = runStart;
+                m_positionEndOffset = subrunEnd;
+                m_textCharacters = str.unicode() + runStart;
+                m_textLength = subrunEnd - runStart;
+
+                m_lastCharacter = str[subrunEnd - 1];
+            }
+
+            // If we are doing a subrun that doesn't go to the end of the text box,
+            // come back again to finish handling this text box; don't advance to the next one.
+            if (m_positionEndOffset < runEnd) {
+                return;
+            }
+
+            // Advance to the next text box.
+            InlineTextBox *nextTextBox = m_textBox->nextTextBox();
+            long nextRunStart = nextTextBox ? nextTextBox->m_start : str.length();
+            if (nextRunStart > runEnd) {
+                m_lastTextNodeEndedWithCollapsedSpace = true; // collapsed space between runs or at the end
+            }
+            m_textBox = nextTextBox;
+            return;
+        }
+    }
+}
+
+bool TextIterator::handleReplacedElement()
+{
+    if (m_lastTextNodeEndedWithCollapsedSpace) {
+        long offset = m_lastTextNode->nodeIndex();
+        emitCharacter(' ', m_lastTextNode->parentNode(), offset + 1, offset + 1);
+        return false;
+    }
+
+    long offset = m_node->nodeIndex();
+
+    m_positionNode = m_node->parentNode();
+    m_positionStartOffset = offset;
+    m_positionEndOffset = offset + 1;
+
+    m_textCharacters = 0;
+    m_textLength = 0;
+
+    m_lastCharacter = 0;
+
+    return true;
+}
+
+bool TextIterator::handleNonTextNode()
+{
+    switch (m_node->id()) {
+        case ID_BR: {
+            long offset = m_node->nodeIndex();
+            emitCharacter('\n', m_node->parentNode(), offset, offset + 1);
+            break;
+        }
+
+        case ID_TD:
+        case ID_TH:
+            if (m_lastCharacter != '\n' && m_lastTextNode) {
+                long offset = m_lastTextNode->nodeIndex();
+                emitCharacter('\t', m_lastTextNode->parentNode(), offset, offset + 1);
+            }
+            break;
+
+        case ID_BLOCKQUOTE:
+        case ID_DD:
+        case ID_DIV:
+        case ID_DL:
+        case ID_DT:
+        case ID_H1:
+        case ID_H2:
+        case ID_H3:
+        case ID_H4:
+        case ID_H5:
+        case ID_H6:
+        case ID_HR:
+        case ID_LI:
+        case ID_OL:
+        case ID_P:
+        case ID_PRE:
+        case ID_TR:
+        case ID_UL:
+            if (m_lastCharacter != '\n' && m_lastTextNode) {
+                long offset = m_lastTextNode->nodeIndex();
+                emitCharacter('\n', m_lastTextNode->parentNode(), offset, offset + 1);
+            }
+            break;
+    }
+
+    return true;
+}
+
+void TextIterator::exitNode()
+{
+    bool endLine = false;
+    bool addNewline = false;
+
+    switch (m_node->id()) {
+        case ID_BLOCKQUOTE:
+        case ID_DD:
+        case ID_DIV:
+        case ID_DL:
+        case ID_DT:
+        case ID_HR:
+        case ID_LI:
+        case ID_OL:
+        case ID_PRE:
+        case ID_TR:
+        case ID_UL:
+            endLine = true;
+            break;
+
+        case ID_H1:
+        case ID_H2:
+        case ID_H3:
+        case ID_H4:
+        case ID_H5:
+        case ID_H6:
+        case ID_P:
+            endLine = true;
+            addNewline = true;
+            break;
+    }
+
+    if (endLine && m_lastCharacter != '\n' && m_lastTextNode) {
+        long offset = m_lastTextNode->nodeIndex();
+        emitCharacter('\n', m_lastTextNode->parentNode(), offset, offset + 1);
+        m_needAnotherNewline = addNewline;
+    } else if (addNewline && m_lastTextNode) {
+        long offset = m_node->childNodeCount();
+        emitCharacter('\n', m_node, offset, offset);
+    }
+}
+
+void TextIterator::emitCharacter(QChar c, NodeImpl *textNode, long textStartOffset, long textEndOffset)
+{
+    m_singleCharacterBuffer = c;
+    m_positionNode = textNode;
+    m_positionStartOffset = textStartOffset;
+    m_positionEndOffset = textEndOffset;
+    m_textCharacters = &m_singleCharacterBuffer;
+    m_textLength = 1;
+
+    m_lastTextNodeEndedWithCollapsedSpace = false;
+    m_lastCharacter = c;
+}
+
+Range TextIterator::position() const
+{
+    assert(m_positionNode);
+    return Range(m_positionNode, m_positionStartOffset, m_positionNode, m_positionEndOffset);
+}
+
+CharacterIterator::CharacterIterator()
+    : m_offset(0), m_runOffset(0), m_atBreak(true)
+{
+}
+
+CharacterIterator::CharacterIterator(const Range &r)
+    : m_offset(0), m_runOffset(0), m_atBreak(true), m_textIterator(r)
+{
+    while (!atEnd() && m_textIterator.textLength() == 0) {
+        m_textIterator.advance();
+    }
+}
+
+Range CharacterIterator::position() const
+{
+    Range r = m_textIterator.position();
+    if (m_textIterator.textLength() <= 1) {
+        assert(m_runOffset == 0);
+    } else {
+        Node n = r.startContainer();
+        assert(n == r.endContainer());
+        long offset = r.startOffset() + m_runOffset;
+        r.setStart(n, offset);
+        r.setEnd(n, offset + 1);
+    }
+    return r;
+}
+
+void CharacterIterator::advance(long count)
+{
+    assert(!atEnd());
+
+    m_atBreak = false;
+
+    long remaining = m_textIterator.textLength() - m_runOffset;
+    if (count < remaining) {
+        m_runOffset += count;
+        m_offset += count;
+        return;
+    }
+
+    count -= remaining;
+    m_offset += remaining;
+    for (m_textIterator.advance(); !atEnd(); m_textIterator.advance()) {
+        long runLength = m_textIterator.textLength();
+        if (runLength == 0) {
+            m_atBreak = true;
+        } else {
+            if (count < runLength) {
+                m_runOffset = count;
+                m_offset += count;
+                return;
+            }
+            count -= runLength;
+            m_offset += runLength;
+        }
+    }
+
+    m_atBreak = true;
+    m_runOffset = 0;
+}
+
+CircularSearchBuffer::CircularSearchBuffer(const QString &s, bool isCaseSensitive)
+    : m_target(s)
+{
+    assert(!s.isEmpty());
+
+    if (!isCaseSensitive) {
+        m_target = s.lower();
+    }
+    m_target.replace(nonBreakingSpace, ' ');
+    m_isCaseSensitive = isCaseSensitive;
+
+    m_buffer = static_cast<QChar *>(malloc(s.length() * sizeof(QChar)));
+    m_cursor = m_buffer;
+    m_bufferFull = false;
+}
+
+void CircularSearchBuffer::append(const QChar &c)
+{
+    if (m_isCaseSensitive) {
+        *m_cursor++ = c.unicode() == nonBreakingSpace ? ' ' : c.unicode();
+    } else {
+        *m_cursor++ = c.unicode() == nonBreakingSpace ? ' ' : c.lower().unicode();
+    }
+    if (m_cursor == m_buffer + length()) {
+        m_cursor = m_buffer;
+        m_bufferFull = true;
+    }
+}
+
+// This function can only be used when the buffer is not yet full,
+// and when then count is small enough to fit in the buffer.
+// No need for a more general version for the search algorithm.
+void CircularSearchBuffer::append(long count, const QChar *characters)
+{
+    long tailSpace = m_buffer + length() - m_cursor;
+
+    assert(!m_bufferFull);
+    assert(count <= tailSpace);
+
+    if (m_isCaseSensitive) {
+        for (long i = 0; i != count; ++i) {
+            QChar c = characters[i];
+            m_cursor[i] = c.unicode() == nonBreakingSpace ? ' ' : c.unicode();
+        }
+    } else {
+        for (long i = 0; i != count; ++i) {
+            QChar c = characters[i];
+            m_cursor[i] = c.unicode() == nonBreakingSpace ? ' ' : c.lower().unicode();
+        }
+    }
+    if (count < tailSpace) {
+        m_cursor += count;
+    } else {
+        m_bufferFull = true;
+        m_cursor = m_buffer;
+    }
+}
+
+long CircularSearchBuffer::neededCharacters() const
+{
+    return m_bufferFull ? 0 : m_buffer + length() - m_cursor;
+}
+
+bool CircularSearchBuffer::isMatch() const
+{
+    assert(m_bufferFull);
+
+    long headSpace = m_cursor - m_buffer;
+    long tailSpace = length() - headSpace;
+    return memcmp(m_cursor, m_target.unicode(), tailSpace * sizeof(QChar)) == 0
+        && memcmp(m_buffer, m_target.unicode() + tailSpace, headSpace * sizeof(QChar)) == 0;
+}
+
+QString plainText(const Range &r)
+{
+    // Allocate string at the right size, rather than building it up by successive append calls.
+    long length = 0;
+    for (TextIterator it(r); !it.atEnd(); it.advance()) {
+        length += it.textLength();
+    }
+    QString result("");
+    result.reserve(length);
+    for (TextIterator it(r); !it.atEnd(); it.advance()) {
+        result.append(it.textCharacters(), it.textLength());
+    }
+    return result;
+}
+
+Range findPlainText(const Range &r, const QString &s, bool forward, bool caseSensitive)
+{
+    // FIXME: Can we do Boyer-Moore or equivalent instead for speed?
+
+    // FIXME: This code does not allow \n at the moment because of issues with <br>.
+    // Once we fix those, we can remove this check.
+    if (s.isEmpty() || s.find('\n') != -1) {
+        Range result = r;
+        result.collapse(forward);
+        return result;
+    }
+
+    CircularSearchBuffer buffer(s, caseSensitive);
+
+    bool found = false;
+    CharacterIterator rangeEnd;
+
+    {
+        CharacterIterator it(r);
+        while (!it.atEnd()) {
+            // Fill the buffer.
+            while (long needed = buffer.neededCharacters()) {
+                long available = it.numCharacters();
+                long runLength = kMin(needed, available);
+                buffer.append(runLength, it.characters());
+                it.advance(runLength);
+                if (it.atBreak()) {
+                    if (it.atEnd()) {
+                        goto done;
+                    }
+                    buffer.clear();
+                }
+            }
+
+            // Do the search.
+            do {
+                if (buffer.isMatch()) {
+                    // Compute the range for the result.
+                    found = true;
+                    rangeEnd = it;
+                    // If searching forward, stop on the first match.
+                    // If searching backward, don't stop, so we end up with the last match.
+                    if (forward) {
+                        goto done;
+                    }
+                }
+                buffer.append(it.characters()[0]);
+                it.advance(1);
+            } while (!it.atBreak());
+            buffer.clear();
+        }
+    }
+
+done:
+    Range result = r;
+    if (!found) {
+        result.collapse(!forward);
+    } else {
+        CharacterIterator it(r);
+        it.advance(rangeEnd.characterOffset() - buffer.length());
+        result.setStart(it.position().startContainer(), it.position().startOffset());
+        it.advance(buffer.length() - 1);
+        result.setEnd(it.position().endContainer(), it.position().endOffset());
+    }
+    return result;
+}
+
+}
diff --git a/JavaScriptCore/bindings/objc/objc_header.h b/WebCore/khtml/misc/khtml_text_operations.h
similarity index 69%
copy from JavaScriptCore/bindings/objc/objc_header.h
copy to WebCore/khtml/misc/khtml_text_operations.h
index 2dd7a26..3e8a705 100644
--- a/JavaScriptCore/bindings/objc/objc_header.h
+++ b/WebCore/khtml/misc/khtml_text_operations.h
@@ -22,26 +22,22 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
  */
-#ifndef _BINDINGS_OBJC_HEADER_H_
-#define _BINDINGS_OBJC_HEADER_H_
 
-#ifdef __OBJC__
-#include <objc/objc-class.h>
-#include <objc/objc-runtime.h>
+#ifndef __khtml_text_iterator_h__
+#define __khtml_text_iterator_h__
 
-typedef struct objc_class *ClassStructPtr;
-typedef struct objc_object *ObjectStructPtr;
+#include <dom/dom2_range.h>
 
- at class NSMethodSignature;
+// FIXME: This class should probably use the render tree and not the DOM tree, since elements could
+// be hidden using CSS, or additional generated content could be added.  For now, we just make sure
+// text objects walk their renderers' InlineTextBox objects, so that we at least get the whitespace 
+// stripped out properly and obey CSS visibility for text runs.
 
-#else
+namespace khtml {
 
-typedef struct objc_ivar {} *Ivar;
-typedef struct objc_class {} *ClassStructPtr;
-typedef struct objc_object {} *ObjectStructPtr;
+QString plainText(const DOM::Range &);
+DOM::Range findPlainText(const DOM::Range &, const QString &, bool forward, bool caseSensitive);
 
-class NSMethodSignature;
+}
 
 #endif
-
-#endif
\ No newline at end of file
diff --git a/WebCore/khtml/xml/dom_selection.cpp b/WebCore/khtml/xml/dom_selection.cpp
index 6f1774f..086487a 100644
--- a/WebCore/khtml/xml/dom_selection.cpp
+++ b/WebCore/khtml/xml/dom_selection.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2003 Apple Computer, Inc.  All rights reserved.
+ * Copyright (C) 2004 Apple Computer, Inc.  All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -77,14 +77,24 @@ Selection::Selection()
 Selection::Selection(const Position &pos)
 {
     init();
-	assignBaseAndExtent(pos, pos);
+    assignBaseAndExtent(pos, pos);
+    validate();
+}
+
+Selection::Selection(const Range &r)
+{
+    const Position start(r.startContainer().handle(), r.startOffset());
+    const Position end(r.endContainer().handle(), r.endOffset());
+
+    init();
+    assignBaseAndExtent(start, end);
     validate();
 }
 
 Selection::Selection(const Position &base, const Position &extent)
 {
     init();
-	assignBaseAndExtent(base, extent);
+    assignBaseAndExtent(base, extent);
     validate();
 }
 
@@ -92,8 +102,8 @@ Selection::Selection(const Selection &o)
 {
     init();
     
-	assignBaseAndExtent(o.base(), o.extent());
-	assignStartAndEnd(o.start(), o.end());
+    assignBaseAndExtent(o.base(), o.extent());
+    assignStartAndEnd(o.start(), o.end());
 
     m_state = o.m_state;
     m_affinity = o.m_affinity;
@@ -129,8 +139,8 @@ void Selection::init()
 
 Selection &Selection::operator=(const Selection &o)
 {
-	assignBaseAndExtent(o.base(), o.extent());
-	assignStartAndEnd(o.start(), o.end());
+    assignBaseAndExtent(o.base(), o.extent());
+    assignStartAndEnd(o.start(), o.end());
 
     m_state = o.m_state;
     m_affinity = o.m_affinity;
@@ -166,22 +176,22 @@ void Selection::moveTo(const Range &r)
 {
     Position start(r.startContainer().handle(), r.startOffset());
     Position end(r.endContainer().handle(), r.endOffset());
-	moveTo(start, end);
+    moveTo(start, end);
 }
 
 void Selection::moveTo(const Selection &o)
 {
-	moveTo(o.start(), o.end());
+    moveTo(o.start(), o.end());
 }
 
 void Selection::moveTo(const Position &pos)
 {
-	moveTo(pos, pos);
+    moveTo(pos, pos);
 }
 
 void Selection::moveTo(const Position &base, const Position &extent)
 {
-	assignBaseAndExtent(base, extent);
+    assignBaseAndExtent(base, extent);
     validate();
 }
 
@@ -337,8 +347,8 @@ int Selection::xPosForVerticalArrowNavigation(EPositionType type, bool recalc) c
 
 void Selection::clear()
 {
-	assignBaseAndExtent(emptyPosition(), emptyPosition());
-	validate();
+    assignBaseAndExtent(emptyPosition(), emptyPosition());
+    validate();
 }
 
 void Selection::setBase(const Position &pos)
@@ -486,13 +496,13 @@ void Selection::validate(ETextGranularity granularity)
     }
 
     // make sure we do not have a dangling start or end
-	if (base().isEmpty() && extent().isEmpty()) {
+    if (base().isEmpty() && extent().isEmpty()) {
         assignStartAndEnd(emptyPosition(), emptyPosition());
         m_baseIsStart = true;
     }
-	else if (base().isEmpty() || extent().isEmpty()) {
+    else if (base().isEmpty() || extent().isEmpty()) {
         m_baseIsStart = true;
-	}
+    }
     else {
         // adjust m_baseIsStart as needed
         if (base().node() == extent().node()) {
@@ -572,13 +582,13 @@ void Selection::validate(ETextGranularity granularity)
     }
 #endif  // APPLE_CHANGES
 
-	// adjust the state
-	if (start().isEmpty() && end().isEmpty())
-		m_state = NONE;
-	else if (start() == end())
-		m_state = CARET;
-	else
-		m_state = RANGE;
+    // adjust the state
+    if (start().isEmpty() && end().isEmpty())
+        m_state = NONE;
+    else if (start() == end())
+        m_state = CARET;
+    else
+        m_state = RANGE;
 
     m_needsCaretLayout = true;
     
@@ -618,13 +628,13 @@ bool Selection::moveToRenderedContent()
 
 bool Selection::nodeIsBeforeNode(NodeImpl *n1, NodeImpl *n2) 
 {
-	if (!n1 || !n2) 
-		return true;
- 
- 	if (n1 == n2)
- 		return true;
- 
- 	bool result = false;
+    if (!n1 || !n2) 
+        return true;
+
+    if (n1 == n2)
+        return true;
+
+    bool result = false;
     int n1Depth = 0;
     int n2Depth = 0;
 
@@ -666,7 +676,7 @@ bool Selection::nodeIsBeforeNode(NodeImpl *n1, NodeImpl *n2)
         }
         n = n->nextSibling();
     }
-	return result;
+    return result;
 }
 
 #if APPLE_CHANGES
diff --git a/WebCore/khtml/xml/dom_selection.h b/WebCore/khtml/xml/dom_selection.h
index 05434bc..d131d40 100644
--- a/WebCore/khtml/xml/dom_selection.h
+++ b/WebCore/khtml/xml/dom_selection.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2003 Apple Computer, Inc.  All rights reserved.
+ * Copyright (C) 2004 Apple Computer, Inc.  All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -45,10 +45,10 @@ class Range;
 class Selection
 {
 public:
-	enum EState { NONE, CARET, RANGE };
-	enum EAlter { MOVE, EXTEND };
-	enum EDirection { FORWARD, BACKWARD, RIGHT, LEFT, UP, DOWN };
-	enum ETextGranularity { CHARACTER, WORD, LINE };
+    enum EState { NONE, CARET, RANGE };
+    enum EAlter { MOVE, EXTEND };
+    enum EDirection { FORWARD, BACKWARD, RIGHT, LEFT, UP, DOWN };
+    enum ETextGranularity { CHARACTER, WORD, LINE };
 
     // These match the AppKit values for these concepts.
     // From NSTextView.h:
@@ -57,13 +57,13 @@ public:
     enum EAffinity { UPSTREAM = 0, DOWNSTREAM = 1 };
 
     Selection();
+    Selection(const Range &);
     Selection(const Position &);
     Selection(const Position &, const Position &);
     Selection(const Selection &);
-    ~Selection() {}
 
-	EState state() const { return m_state; }
-	EAffinity affinity() const { return m_affinity; }
+    EState state() const { return m_state; }
+    EAffinity affinity() const { return m_affinity; }
     void setAffinity(EAffinity);
 
     void moveTo(const Range &);
@@ -100,6 +100,8 @@ public:
     void debugRenderer(khtml::RenderObject *r, bool selected) const;
 
     Selection &operator=(const Selection &o);
+    Selection &operator=(const Range &r) { moveTo(r); return *this; }
+    Selection &operator=(const Position &r) { moveTo(r); return *this; }
     
     friend bool operator==(const Selection &a, const Selection &b);
     friend bool operator!=(const Selection &a, const Selection &b);
@@ -107,7 +109,7 @@ public:
     friend class KHTMLPart;
 
 private:
-	enum EPositionType { START, END, BASE, EXTENT };
+    enum EPositionType { START, END, BASE, EXTENT };
 
     void init();
     void validate(ETextGranularity granularity=CHARACTER);
@@ -130,16 +132,16 @@ private:
     Position m_start;             // start position for the selection
     Position m_end;               // end position for the selection
 
-	EState m_state;               // the state of the selection
-	EAffinity m_affinity;         // the upstream/downstream affinity of the selection
+    EState m_state;               // the state of the selection
+    EAffinity m_affinity;         // the upstream/downstream affinity of the selection
 
-	int m_caretX;                 // caret coordinates and size
-	int m_caretY;
-	int m_caretSize;
+    int m_caretX;                 // caret coordinates and size
+    int m_caretY;
+    int m_caretSize;
 
-	bool m_baseIsStart : 1;       // true if base node is before the extent node
-	bool m_needsCaretLayout : 1;  // true if the caret position needs to be calculated
-	bool m_modifyBiasSet : 1;     // true if the selection has been horizontally 
+    bool m_baseIsStart : 1;       // true if base node is before the extent node
+    bool m_needsCaretLayout : 1;  // true if the caret position needs to be calculated
+    bool m_modifyBiasSet : 1;     // true if the selection has been horizontally 
                                   // modified with EAlter::EXTEND
 };
 
diff --git a/WebCore/kwq/KWQKHTMLPart.mm b/WebCore/kwq/KWQKHTMLPart.mm
index 00566d9..7bfaff6 100644
--- a/WebCore/kwq/KWQKHTMLPart.mm
+++ b/WebCore/kwq/KWQKHTMLPart.mm
@@ -25,6 +25,7 @@
 
 #import "KWQKHTMLPart.h"
 
+#import "DOMInternal.h"
 #import "KWQDOMNode.h"
 #import "KWQDummyView.h"
 #import "KWQEditCommand.h"
@@ -33,20 +34,23 @@
 #import "KWQLogging.h"
 #import "KWQPageState.h"
 #import "KWQPrinter.h"
+#import "KWQScrollBar.h"
 #import "KWQWindowWidget.h"
 #import "WebCoreBridge.h"
 #import "WebCoreViewFactory.h"
-#import "DOMInternal.h"
 #import "csshelper.h"
+#import "dom2_eventsimpl.h"
+#import "dom2_rangeimpl.h"
+#import "dom_selection.h"
 #import "html_documentimpl.h"
 #import "html_misc.h"
+#import "htmlattrs.h"
 #import "htmltokenizer.h"
+#import "khtml_text_operations.h"
 #import "khtmlpart_p.h"
 #import "khtmlview.h"
 #import "kjs_binding.h"
 #import "kjs_window.h"
-#import "misc/htmlattrs.h"
-#import "qscrollbar.h"
 #import "render_canvas.h"
 #import "render_frames.h"
 #import "render_image.h"
@@ -54,9 +58,6 @@
 #import "render_style.h"
 #import "render_table.h"
 #import "render_text.h"
-#import "xml/dom_selection.h"
-#import "xml/dom2_eventsimpl.h"
-#import "xml/dom2_rangeimpl.h"
 #import <JavaScriptCore/identifier.h>
 #import <JavaScriptCore/property_map.h>
 #import <JavaScriptCore/runtime.h>
@@ -77,12 +78,15 @@ using DOM::HTMLGenericFormElementImpl;
 using DOM::HTMLTableCellElementImpl;
 using DOM::Node;
 using DOM::NodeImpl;
+using DOM::Range;
 using DOM::RangeImpl;
 using DOM::Selection;
 
 using khtml::Cache;
 using khtml::ChildFrame;
 using khtml::Decoder;
+using khtml::findPlainText;
+using khtml::InlineTextBox;
 using khtml::MouseDoubleClickEvent;
 using khtml::MouseMoveEvent;
 using khtml::MousePressEvent;
@@ -99,7 +103,6 @@ using khtml::RenderStyle;
 using khtml::RenderTableCell;
 using khtml::RenderText;
 using khtml::RenderWidget;
-using khtml::InlineTextBox;
 using khtml::VISIBLE;
 
 using KIO::Job;
@@ -531,44 +534,40 @@ NSString *KWQKHTMLPart::matchLabelsAgainstElement(NSArray *labels, ElementImpl *
 bool KWQKHTMLPart::findString(NSString *string, bool forward, bool caseFlag, bool wrapFlag)
 {
     QString target = QString::fromNSString(string);
-    bool result;
-    // start on the correct edge of the selection, search to end
-    NodeImpl *selStart = selectionStart();
-    int selStartOffset = selectionStartOffset();
-    NodeImpl *selEnd = selectionEnd();
-    int selEndOffset = selectionEndOffset();
-    if (selStart) {
+    if (target.isEmpty()) {
+        return false;
+    }
+
+    // Start on the correct edge of the selection, search to edge of document.
+    Range searchRange(xmlDocImpl());
+    searchRange.selectNodeContents(xmlDocImpl());
+    if (selectionStart()) {
         if (forward) {
-            // point to last char of selection, find will start right afterwards
-            findTextBegin(selEnd, selEndOffset-1);
+            searchRange.setStart(selectionEnd(), selectionEndOffset());
         } else {
-            // point to first char of selection, find will start right before
-            findTextBegin(selStart, selStartOffset);
+            searchRange.setEnd(selectionStart(), selectionStartOffset());
         }
-    } else {
-        findTextBegin();
-    }
-    result = findTextNext(target, forward, caseFlag, FALSE);
-    if (!result && wrapFlag) {
-        // start back at the other end, search the rest
-        findTextBegin();
-        result = findTextNext(target, forward, caseFlag, FALSE);
-        // if we got back to the same place we started, that doesn't count as success
-        if (result
-            && selStart == selectionStart()
-            && selStartOffset == selectionStartOffset())
-        {
-            result = false;
+    }
+
+    // Do the search once, then do it a second time to handle wrapped search.
+    // Searches some or all of document twice in the failure case, but that's probably OK.
+    Range resultRange = findPlainText(searchRange, target, forward, caseFlag);
+    if (resultRange.collapsed() && wrapFlag) {
+        searchRange.selectNodeContents(xmlDocImpl());
+        resultRange = findPlainText(searchRange, target, forward, caseFlag);
+        // If we got back to the same place we started, that doesn't count as success.
+        if (resultRange == selection().toRange()) {
+            return false;
         }
     }
 
-    // khtml took care of moving the selection, but we need to move first responder too,
-    // so the selection is primary.  We also need to make the selection visible, since we
-    // cut the implementation of this in khtml_part.
-    if (result) {
-        jumpToSelection();
+    if (resultRange.collapsed()) {
+        return false;
     }
-    return result;
+
+    setSelection(resultRange);
+    jumpToSelection();
+    return true;
 }
 
 void KWQKHTMLPart::clearRecordedFormValues()
@@ -832,6 +831,8 @@ void KWQKHTMLPart::jumpToSelection()
            d->m_view->setContentsPos(x - 50, y - 50);
         }
 /*
+        Something like this would fix <rdar://problem/3154293>: "Find Next should not scroll page if the next target is already visible"
+
         I think this would be a better way to do this, to avoid needless horizontal scrolling,
         but it is not feasible until selectionRect() returns a tighter rect around the
         selected text.  Right now it works at element granularity.

-- 
WebKit Debian packaging



More information about the Pkg-webkit-commits mailing list