<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[177860] trunk/Source</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.webkit.org/projects/webkit/changeset/177860">177860</a></dd>
<dt>Author</dt> <dd>darin@apple.com</dd>
<dt>Date</dt> <dd>2015-01-02 08:33:40 -0800 (Fri, 02 Jan 2015)</dd>
</dl>

<h3>Log Message</h3>
<pre>Rework code that hides characters in password fields to streamline a little
https://bugs.webkit.org/show_bug.cgi?id=140035

Reviewed by Sam Weinig.

Source/WebCore:

* editing/InsertIntoTextNodeCommand.cpp:
(WebCore::InsertIntoTextNodeCommand::doApply): Pass the offset after the character
we want to reveal instead of the offset before. This is more future proof if we
ever want to handling surrogate pairs or combining marks, rather than hard
coding the likely incorrect rule of &quot;go back by one code unit&quot;. Also got rid of
the isSecure check here, since RenderText can do that check inside the
momentarilyRevealLastTypedCharacter instead.

* rendering/RenderText.cpp: Tweaked the SecureTextTimer class: Marked it final
and made it derive from TimerBase privately. Made the constructor explicit and
made it take a reference rather than a pointer. Use initialization in the class
to set up the variable, and use 0 as the special value instead of -1 since we
now store the offset *after* the character to be revealed.
(WebCore::secureTextTimers): Use this function and NeverDestroyed rather than
a global variable gSecureTextTimers. Also use unique_ptr for the map so we
don't have to delete explicitly any more.
(WebCore::SecureTextTimer::SecureTextTimer): Moved out of the class definition
so the class ia a little easier to read.
(WebCore::SecureTextTimer::restart): Renamed since the function name doesn't
have to describe its argument; the function is only called in one place.
(WebCore::SecureTextTimer::takeOffsetAfterLastTypedCharacter): Changed name
and made this a one-shot that always zeroes the offset.
(WebCore::SecureTextTimer::fired): Moved out of line and tweaked as above.
(WebCore::RenderText::willBeDestroyed): Simplified now that the function
secureTextTimers() always returns a map and we can just remove since the
values in the map are unique_ptr, so take care of deletion.
(WebCore::RenderText::setRenderedText): Tweaked the code that calls secureText
to make the iOS case clearer.
(WebCore::RenderText::secureText): Rewrote the function. New version no
longer relies on a special String::fill function; it's kind of strange that
String had a built in concept of replacing a string with one that has the
same length but all with a masking character. This new approach is cleaner.
I had written a version that handles surrogate pairs and combining marks,
but then instead wrote a comment explaining why that's not needed/helpful.
(WebCore::RenderText::momentarilyRevealLastTypedCharacter): Added a check so
this does nothing if we are not securing the text. Also updated logic so that
this doesn't double hash any more and updated for other changes like using
a reference instead of a pointer.

* rendering/RenderText.h: Removed the unneeded isSecure function and updated
the argument name in momentarilyRevealLastTypedCharacter since it's now the
offset after the character, not before the character.

Source/WTF:

* wtf/text/StringImpl.cpp:
(WTF::StringImpl::fill): Deleted.
* wtf/text/StringImpl.h: Deleted StringImpl::fill.
* wtf/text/WTFString.h:
(WTF::String::fill): Deleted.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkSourceWTFChangeLog">trunk/Source/WTF/ChangeLog</a></li>
<li><a href="#trunkSourceWTFwtftextStringImplcpp">trunk/Source/WTF/wtf/text/StringImpl.cpp</a></li>
<li><a href="#trunkSourceWTFwtftextStringImplh">trunk/Source/WTF/wtf/text/StringImpl.h</a></li>
<li><a href="#trunkSourceWTFwtftextWTFStringh">trunk/Source/WTF/wtf/text/WTFString.h</a></li>
<li><a href="#trunkSourceWebCoreChangeLog">trunk/Source/WebCore/ChangeLog</a></li>
<li><a href="#trunkSourceWebCoreeditingInsertIntoTextNodeCommandcpp">trunk/Source/WebCore/editing/InsertIntoTextNodeCommand.cpp</a></li>
<li><a href="#trunkSourceWebCorerenderingRenderTextcpp">trunk/Source/WebCore/rendering/RenderText.cpp</a></li>
<li><a href="#trunkSourceWebCorerenderingRenderTexth">trunk/Source/WebCore/rendering/RenderText.h</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkSourceWTFChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WTF/ChangeLog (177859 => 177860)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WTF/ChangeLog        2015-01-02 16:31:49 UTC (rev 177859)
+++ trunk/Source/WTF/ChangeLog        2015-01-02 16:33:40 UTC (rev 177860)
</span><span class="lines">@@ -1,3 +1,16 @@
</span><ins>+2015-01-01  Darin Adler  &lt;darin@apple.com&gt;
+
+        Rework code that hides characters in password fields to streamline a little
+        https://bugs.webkit.org/show_bug.cgi?id=140035
+
+        Reviewed by Sam Weinig.
+
+        * wtf/text/StringImpl.cpp:
+        (WTF::StringImpl::fill): Deleted.
+        * wtf/text/StringImpl.h: Deleted StringImpl::fill.
+        * wtf/text/WTFString.h:
+        (WTF::String::fill): Deleted.
+
</ins><span class="cx"> 2014-12-26  Dan Bernstein  &lt;mitz@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         Build fix.
</span></span></pre></div>
<a id="trunkSourceWTFwtftextStringImplcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WTF/wtf/text/StringImpl.cpp (177859 => 177860)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WTF/wtf/text/StringImpl.cpp        2015-01-02 16:31:49 UTC (rev 177859)
+++ trunk/Source/WTF/wtf/text/StringImpl.cpp        2015-01-02 16:33:40 UTC (rev 177860)
</span><span class="lines">@@ -596,22 +596,6 @@
</span><span class="cx">     return newString.releaseNonNull();
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-Ref&lt;StringImpl&gt; StringImpl::fill(UChar character)
-{
-    if (!(character &amp; ~0x7F)) {
-        LChar* data;
-        auto newImpl = createUninitialized(m_length, data);
-        for (unsigned i = 0; i &lt; m_length; ++i)
-            data[i] = character;
-        return newImpl;
-    }
-    UChar* data;
-    auto newImpl = createUninitialized(m_length, data);
-    for (unsigned i = 0; i &lt; m_length; ++i)
-        data[i] = character;
-    return newImpl;
-}
-
</del><span class="cx"> Ref&lt;StringImpl&gt; StringImpl::foldCase()
</span><span class="cx"> {
</span><span class="cx">     // FIXME: Why doesn't this function have a preflight like the one in StringImpl::lower?
</span></span></pre></div>
<a id="trunkSourceWTFwtftextStringImplh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WTF/wtf/text/StringImpl.h (177859 => 177860)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WTF/wtf/text/StringImpl.h        2015-01-02 16:31:49 UTC (rev 177859)
+++ trunk/Source/WTF/wtf/text/StringImpl.h        2015-01-02 16:33:40 UTC (rev 177860)
</span><span class="lines">@@ -639,9 +639,6 @@
</span><span class="cx">     WTF_EXPORT_STRING_API Ref&lt;StringImpl&gt; lower(const AtomicString&amp; localeIdentifier);
</span><span class="cx">     WTF_EXPORT_STRING_API Ref&lt;StringImpl&gt; upper(const AtomicString&amp; localeIdentifier);
</span><span class="cx"> 
</span><del>-    WTF_EXPORT_STRING_API Ref&lt;StringImpl&gt; fill(UChar);
-    // FIXME: Do we need fill(char) or can we just do the right thing if UChar is ASCII?
-
</del><span class="cx">     Ref&lt;StringImpl&gt; foldCase();
</span><span class="cx"> 
</span><span class="cx">     Ref&lt;StringImpl&gt; stripWhiteSpace();
</span></span></pre></div>
<a id="trunkSourceWTFwtftextWTFStringh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WTF/wtf/text/WTFString.h (177859 => 177860)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WTF/wtf/text/WTFString.h        2015-01-02 16:31:49 UTC (rev 177859)
+++ trunk/Source/WTF/wtf/text/WTFString.h        2015-01-02 16:33:40 UTC (rev 177860)
</span><span class="lines">@@ -306,8 +306,6 @@
</span><span class="cx">         return *this;
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    void fill(UChar c) { if (m_impl) m_impl = m_impl-&gt;fill(c); }
-
</del><span class="cx">     WTF_EXPORT_STRING_API void truncate(unsigned len);
</span><span class="cx">     WTF_EXPORT_STRING_API void remove(unsigned pos, int len = 1);
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceWebCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/ChangeLog (177859 => 177860)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/ChangeLog        2015-01-02 16:31:49 UTC (rev 177859)
+++ trunk/Source/WebCore/ChangeLog        2015-01-02 16:33:40 UTC (rev 177860)
</span><span class="lines">@@ -1,5 +1,55 @@
</span><span class="cx"> 2015-01-01  Darin Adler  &lt;darin@apple.com&gt;
</span><span class="cx"> 
</span><ins>+        Rework code that hides characters in password fields to streamline a little
+        https://bugs.webkit.org/show_bug.cgi?id=140035
+
+        Reviewed by Sam Weinig.
+
+        * editing/InsertIntoTextNodeCommand.cpp:
+        (WebCore::InsertIntoTextNodeCommand::doApply): Pass the offset after the character
+        we want to reveal instead of the offset before. This is more future proof if we
+        ever want to handling surrogate pairs or combining marks, rather than hard
+        coding the likely incorrect rule of &quot;go back by one code unit&quot;. Also got rid of
+        the isSecure check here, since RenderText can do that check inside the
+        momentarilyRevealLastTypedCharacter instead.
+
+        * rendering/RenderText.cpp: Tweaked the SecureTextTimer class: Marked it final
+        and made it derive from TimerBase privately. Made the constructor explicit and
+        made it take a reference rather than a pointer. Use initialization in the class
+        to set up the variable, and use 0 as the special value instead of -1 since we
+        now store the offset *after* the character to be revealed.
+        (WebCore::secureTextTimers): Use this function and NeverDestroyed rather than
+        a global variable gSecureTextTimers. Also use unique_ptr for the map so we
+        don't have to delete explicitly any more.
+        (WebCore::SecureTextTimer::SecureTextTimer): Moved out of the class definition
+        so the class ia a little easier to read.
+        (WebCore::SecureTextTimer::restart): Renamed since the function name doesn't
+        have to describe its argument; the function is only called in one place.
+        (WebCore::SecureTextTimer::takeOffsetAfterLastTypedCharacter): Changed name
+        and made this a one-shot that always zeroes the offset.
+        (WebCore::SecureTextTimer::fired): Moved out of line and tweaked as above.
+        (WebCore::RenderText::willBeDestroyed): Simplified now that the function
+        secureTextTimers() always returns a map and we can just remove since the
+        values in the map are unique_ptr, so take care of deletion.
+        (WebCore::RenderText::setRenderedText): Tweaked the code that calls secureText
+        to make the iOS case clearer.
+        (WebCore::RenderText::secureText): Rewrote the function. New version no
+        longer relies on a special String::fill function; it's kind of strange that
+        String had a built in concept of replacing a string with one that has the
+        same length but all with a masking character. This new approach is cleaner.
+        I had written a version that handles surrogate pairs and combining marks,
+        but then instead wrote a comment explaining why that's not needed/helpful.
+        (WebCore::RenderText::momentarilyRevealLastTypedCharacter): Added a check so
+        this does nothing if we are not securing the text. Also updated logic so that
+        this doesn't double hash any more and updated for other changes like using
+        a reference instead of a pointer.
+
+        * rendering/RenderText.h: Removed the unneeded isSecure function and updated
+        the argument name in momentarilyRevealLastTypedCharacter since it's now the
+        offset after the character, not before the character.
+
+2015-01-01  Darin Adler  &lt;darin@apple.com&gt;
+
</ins><span class="cx">         Modernize coding style of HTMLTreeBuilder
</span><span class="cx">         https://bugs.webkit.org/show_bug.cgi?id=140032
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceWebCoreeditingInsertIntoTextNodeCommandcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/editing/InsertIntoTextNodeCommand.cpp (177859 => 177860)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/editing/InsertIntoTextNodeCommand.cpp        2015-01-02 16:31:49 UTC (rev 177859)
+++ trunk/Source/WebCore/editing/InsertIntoTextNodeCommand.cpp        2015-01-02 16:33:40 UTC (rev 177860)
</span><span class="lines">@@ -60,9 +60,8 @@
</span><span class="cx">         return;
</span><span class="cx"> 
</span><span class="cx">     if (passwordEchoEnabled) {
</span><del>-        RenderText* renderText = m_node-&gt;renderer();
-        if (renderText &amp;&amp; renderText-&gt;isSecure())
-            renderText-&gt;momentarilyRevealLastTypedCharacter(m_offset + m_text.length() - 1);
</del><ins>+        if (RenderText* renderText = m_node-&gt;renderer())
+            renderText-&gt;momentarilyRevealLastTypedCharacter(m_offset + m_text.length());
</ins><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     m_node-&gt;insertData(m_offset, m_text, IGNORE_EXCEPTION);
</span></span></pre></div>
<a id="trunkSourceWebCorerenderingRenderTextcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/rendering/RenderText.cpp (177859 => 177860)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/rendering/RenderText.cpp        2015-01-02 16:31:49 UTC (rev 177859)
+++ trunk/Source/WebCore/rendering/RenderText.cpp        2015-01-02 16:33:40 UTC (rev 177860)
</span><span class="lines">@@ -1,7 +1,7 @@
</span><span class="cx"> /*
</span><span class="cx">  * (C) 1999 Lars Knoll (knoll@kde.org)
</span><span class="cx">  * (C) 2000 Dirk Mueller (mueller@kde.org)
</span><del>- * Copyright (C) 2004, 2005, 2006, 2007, 2013 Apple Inc. All rights reserved.
</del><ins>+ * Copyright (C) 2004-2007, 2013-2015 Apple Inc. All rights reserved.
</ins><span class="cx">  * Copyright (C) 2006 Andrew Wellington (proton@wiretapped.net)
</span><span class="cx">  * Copyright (C) 2006 Graham Dennis (graham.dennis@gmail.com)
</span><span class="cx">  *
</span><span class="lines">@@ -75,38 +75,53 @@
</span><span class="cx"> 
</span><span class="cx"> COMPILE_ASSERT(sizeof(RenderText) == sizeof(SameSizeAsRenderText), RenderText_should_stay_small);
</span><span class="cx"> 
</span><del>-class SecureTextTimer;
-typedef HashMap&lt;RenderText*, SecureTextTimer*&gt; SecureTextTimerMap;
-static SecureTextTimerMap* gSecureTextTimers = 0;
-
-class SecureTextTimer : public TimerBase {
</del><ins>+class SecureTextTimer final : private TimerBase {
+    WTF_MAKE_FAST_ALLOCATED;
</ins><span class="cx"> public:
</span><del>-    SecureTextTimer(RenderText* renderText)
-        : m_renderText(renderText)
-        , m_lastTypedCharacterOffset(-1)
-    {
-    }
</del><ins>+    explicit SecureTextTimer(RenderText&amp;);
+    void restart(unsigned offsetAfterLastTypedCharacter);
</ins><span class="cx"> 
</span><del>-    void restartWithNewText(unsigned lastTypedCharacterOffset)
-    {
-        m_lastTypedCharacterOffset = lastTypedCharacterOffset;
-        const Settings&amp; settings = m_renderText-&gt;frame().settings();
-        startOneShot(settings.passwordEchoDurationInSeconds());
-    }
-    void invalidate() { m_lastTypedCharacterOffset = -1; }
-    unsigned lastTypedCharacterOffset() { return m_lastTypedCharacterOffset; }
</del><ins>+    unsigned takeOffsetAfterLastTypedCharacter();
</ins><span class="cx"> 
</span><span class="cx"> private:
</span><del>-    virtual void fired()
-    {
-        ASSERT(gSecureTextTimers-&gt;contains(m_renderText));
-        m_renderText-&gt;setText(m_renderText-&gt;text(), true /* forcing setting text as it may be masked later */);
-    }
-
-    RenderText* m_renderText;
-    int m_lastTypedCharacterOffset;
</del><ins>+    virtual void fired() override;
+    RenderText&amp; m_renderer;
+    unsigned m_offsetAfterLastTypedCharacter { 0 };
</ins><span class="cx"> };
</span><span class="cx"> 
</span><ins>+typedef HashMap&lt;RenderText*, std::unique_ptr&lt;SecureTextTimer&gt;&gt; SecureTextTimerMap;
+
+static SecureTextTimerMap&amp; secureTextTimers()
+{
+    static NeverDestroyed&lt;SecureTextTimerMap&gt; map;
+    return map.get();
+}
+
+inline SecureTextTimer::SecureTextTimer(RenderText&amp; renderer)
+    : m_renderer(renderer)
+{
+}
+
+inline void SecureTextTimer::restart(unsigned offsetAfterLastTypedCharacter)
+{
+    m_offsetAfterLastTypedCharacter = offsetAfterLastTypedCharacter;
+    startOneShot(m_renderer.frame().settings().passwordEchoDurationInSeconds());
+}
+
+inline unsigned SecureTextTimer::takeOffsetAfterLastTypedCharacter()
+{
+    unsigned offset = m_offsetAfterLastTypedCharacter;
+    m_offsetAfterLastTypedCharacter = 0;
+    return offset;
+}
+
+void SecureTextTimer::fired()
+{
+    ASSERT(secureTextTimers().get(&amp;m_renderer) == this);
+    m_offsetAfterLastTypedCharacter = 0;
+    m_renderer.setText(m_renderer.text(), true /* forcing setting text as it may be masked later */);
+}
+
</ins><span class="cx"> static HashMap&lt;const RenderText*, String&gt;&amp; originalTextMap()
</span><span class="cx"> {
</span><span class="cx">     static NeverDestroyed&lt;HashMap&lt;const RenderText*, String&gt;&gt; map;
</span><span class="lines">@@ -265,8 +280,7 @@
</span><span class="cx"> 
</span><span class="cx"> void RenderText::willBeDestroyed()
</span><span class="cx"> {
</span><del>-    if (SecureTextTimer* secureTextTimer = gSecureTextTimers ? gSecureTextTimers-&gt;take(this) : 0)
-        delete secureTextTimer;
</del><ins>+    secureTextTimers().remove(this);
</ins><span class="cx"> 
</span><span class="cx">     removeAndDestroyTextBoxes();
</span><span class="cx">     RenderObject::willBeDestroyed();
</span><span class="lines">@@ -1044,30 +1058,28 @@
</span><span class="cx"> 
</span><span class="cx">     applyTextTransform(style(), m_text, previousCharacter());
</span><span class="cx"> 
</span><del>-    // We use the same characters here as for list markers.
-    // See the listMarkerText function in RenderListMarker.cpp.
</del><span class="cx">     switch (style().textSecurity()) {
</span><span class="cx">     case TSNONE:
</span><span class="cx">         break;
</span><ins>+#if !PLATFORM(IOS)
+    // We use the same characters here as for list markers.
+    // See the listMarkerText function in RenderListMarker.cpp.
</ins><span class="cx">     case TSCIRCLE:
</span><del>-#if PLATFORM(IOS)
-        secureText(blackCircle);
-#else
</del><span class="cx">         secureText(whiteBullet);
</span><del>-#endif
</del><span class="cx">         break;
</span><span class="cx">     case TSDISC:
</span><del>-#if PLATFORM(IOS)
-        secureText(blackCircle);
-#else
</del><span class="cx">         secureText(bullet);
</span><del>-#endif
</del><span class="cx">         break;
</span><span class="cx">     case TSSQUARE:
</span><del>-#if PLATFORM(IOS)
</del><ins>+        secureText(blackSquare);
+        break;
+#else
+    // FIXME: Why this quirk on iOS?
+    case TSCIRCLE:
+    case TSDISC:
+    case TSSQUARE:
</ins><span class="cx">         secureText(blackCircle);
</span><del>-#else
-        secureText(blackSquare);
</del><ins>+        break;
</ins><span class="cx"> #endif
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="lines">@@ -1085,26 +1097,36 @@
</span><span class="cx">     }
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-void RenderText::secureText(UChar mask)
</del><ins>+void RenderText::secureText(UChar maskingCharacter)
</ins><span class="cx"> {
</span><del>-    if (!textLength())
</del><ins>+    // This hides the text by replacing all the characters with the masking character.
+    // Offsets within the hidden text have to match offsets within the original text
+    // to handle things like carets and selection, so this won't work right if any
+    // of the characters are surrogate pairs or combining marks. Thus, this function
+    // does not attempt to handle either of those.
+
+    unsigned length = textLength();
+    if (!length)
</ins><span class="cx">         return;
</span><span class="cx"> 
</span><del>-    int lastTypedCharacterOffsetToReveal = -1;
-    String revealedText;
-    SecureTextTimer* secureTextTimer = gSecureTextTimers ? gSecureTextTimers-&gt;get(this) : nullptr;
-    if (secureTextTimer &amp;&amp; secureTextTimer-&gt;isActive()) {
-        lastTypedCharacterOffsetToReveal = secureTextTimer-&gt;lastTypedCharacterOffset();
-        if (lastTypedCharacterOffsetToReveal &gt;= 0)
-            revealedText = m_text.substring(lastTypedCharacterOffsetToReveal, 1);
</del><ins>+    UChar characterToReveal = 0;
+    unsigned revealedCharactersOffset;
+
+    if (SecureTextTimer* timer = secureTextTimers().get(this)) {
+        // We take the offset out of the timer to make this one-shot. We count on this being called only once.
+        // If it's called a second time we assume the text is different and a character should not be revealed.
+        revealedCharactersOffset = timer-&gt;takeOffsetAfterLastTypedCharacter();
+        if (revealedCharactersOffset &amp;&amp; revealedCharactersOffset &lt;= length)
+            characterToReveal = m_text[--revealedCharactersOffset];
</ins><span class="cx">     }
</span><span class="cx"> 
</span><del>-    m_text.fill(mask);
-    if (lastTypedCharacterOffsetToReveal &gt;= 0) {
-        m_text.replace(lastTypedCharacterOffsetToReveal, 1, revealedText);
-        // m_text may be updated later before timer fires. We invalidate the lastTypedCharacterOffset to avoid inconsistency.
-        secureTextTimer-&gt;invalidate();
-    }
</del><ins>+    UChar* characters;
+    m_text = String::createUninitialized(length, characters);
+
+    for (unsigned i = 0; i &lt; length; ++i)
+        characters[i] = maskingCharacter;
+    if (characterToReveal)
+        characters[revealedCharactersOffset] = characterToReveal;
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> void RenderText::setText(const String&amp; text, bool force)
</span><span class="lines">@@ -1537,17 +1559,14 @@
</span><span class="cx">     return Font::characterRangeCodePath(characters16(), length()) == Font::Simple;
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-void RenderText::momentarilyRevealLastTypedCharacter(unsigned lastTypedCharacterOffset)
</del><ins>+void RenderText::momentarilyRevealLastTypedCharacter(unsigned offsetAfterLastTypedCharacter)
</ins><span class="cx"> {
</span><del>-    if (!gSecureTextTimers)
-        gSecureTextTimers = new SecureTextTimerMap;
-
-    SecureTextTimer* secureTextTimer = gSecureTextTimers-&gt;get(this);
-    if (!secureTextTimer) {
-        secureTextTimer = new SecureTextTimer(this);
-        gSecureTextTimers-&gt;add(this, secureTextTimer);
-    }
-    secureTextTimer-&gt;restartWithNewText(lastTypedCharacterOffset);
</del><ins>+    if (style().textSecurity() == TSNONE)
+        return;
+    auto&amp; secureTextTimer = secureTextTimers().add(this, nullptr).iterator-&gt;value;
+    if (!secureTextTimer)
+        secureTextTimer = std::make_unique&lt;SecureTextTimer&gt;(*this);
+    secureTextTimer-&gt;restart(offsetAfterLastTypedCharacter);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> StringView RenderText::stringView(int start, int stop) const
</span></span></pre></div>
<a id="trunkSourceWebCorerenderingRenderTexth"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/rendering/RenderText.h (177859 => 177860)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/rendering/RenderText.h        2015-01-02 16:31:49 UTC (rev 177859)
+++ trunk/Source/WebCore/rendering/RenderText.h        2015-01-02 16:33:40 UTC (rev 177860)
</span><span class="lines">@@ -138,8 +138,7 @@
</span><span class="cx"> 
</span><span class="cx">     bool containsReversedText() const { return m_containsReversedText; }
</span><span class="cx"> 
</span><del>-    bool isSecure() const { return style().textSecurity() != TSNONE; }
-    void momentarilyRevealLastTypedCharacter(unsigned lastTypedCharacterOffset);
</del><ins>+    void momentarilyRevealLastTypedCharacter(unsigned offsetAfterLastTypedCharacter);
</ins><span class="cx"> 
</span><span class="cx">     InlineTextBox* findNextInlineTextBox(int offset, int&amp; pos) const { return m_lineBoxes.findNext(offset, pos); }
</span><span class="cx"> 
</span></span></pre>
</div>
</div>

</body>
</html>