Source/JavaScriptCore/ChangeLog

 12011-01-17 Chang Shu <chang.shu@nokia.com>
 2
 3 Reviewed by NOBODY (OOPS!).
 4
 5 Update API to support displaying last character in function makeSecure().
 6 https://bugs.webkit.org/show_bug.cgi?id=32509
 7
 8 * wtf/text/WTFString.h:
 9 (WTF::String::makeSecure):
 10
1112011-01-17 Tony Gentilcore <tonyg@chromium.org>
212
313 Reviewed by Alexey Proskuryakov.
75971

Source/JavaScriptCore/wtf/text/WTFString.h

@@public:
213213
214214 void makeLower() { if (m_impl) m_impl = m_impl->lower(); }
215215 void makeUpper() { if (m_impl) m_impl = m_impl->upper(); }
216  void makeSecure(UChar aChar) { if (m_impl) m_impl = m_impl->secure(aChar); }
 216 void makeSecure(UChar aChar, bool shouldSecureLastCharacter)
 217 {
 218 if (m_impl)
 219 m_impl = m_impl->secure(aChar, shouldSecureLastCharacter ? StringImpl::ObscureLastCharacter : StringImpl::DisplayLastCharacter);
 220 }
217221
218222 void truncate(unsigned len);
219223 void remove(unsigned pos, int len = 1);
75971

Source/WebCore/ChangeLog

 12011-01-17 Chang Shu <chang.shu@nokia.com>
 2
 3 Reviewed by NOBODY (OOPS!).
 4
 5 Added support for revealing last character in password input for a short period of time.
 6 Code change was based on Apple's iOS code and Samuel Nevala's work.
 7 https://bugs.webkit.org/show_bug.cgi?id=32509
 8
 9 Test: editing/input/secure-text.html
 10
 11 * dom/CharacterData.cpp:
 12 (WebCore::CharacterData::insertData):
 13 * dom/CharacterData.h:
 14 * editing/InsertIntoTextNodeCommand.cpp:
 15 (WebCore::InsertIntoTextNodeCommand::doApply):
 16 * rendering/RenderText.cpp:
 17 (WebCore::RenderText::RenderText):
 18 (WebCore::RenderText::setTextInternal):
 19 (WebCore::RenderText::momentarilyRevealLastCharacter):
 20 (WebCore::RenderText::secureLastCharacter):
 21 * rendering/RenderText.h:
 22 (WebCore::RenderText::isSecure):
 23
1242011-01-17 Tony Gentilcore <tonyg@chromium.org>
225
326 Reviewed by Alexey Proskuryakov.
75971

Source/WebCore/dom/CharacterData.cpp

@@void CharacterData::appendData(const Str
7676 // FIXME: Should we call textInserted here?
7777}
7878
79 void CharacterData::insertData(unsigned offset, const String& data, ExceptionCode& ec)
 79void CharacterData::insertData(unsigned offset, const String& data, ExceptionCode& ec, bool canShowLastCharacterIfSecure)
8080{
8181 checkCharDataOperation(offset, ec);
8282 if (ec)
8383 return;
8484
 85 bool atEnd = (offset == m_data->length());
8586 String newStr = m_data;
8687 newStr.insert(data, offset);
8788
 89 if (atEnd && canShowLastCharacterIfSecure && renderer()) {
 90 RenderText* renderText = toRenderText(renderer());
 91 if (renderText->isSecure())
 92 renderText->momentarilyRevealLastCharacter();
 93 }
 94
8895 setDataAndUpdate(newStr.impl(), offset, 0, data.length());
8996
9097 document()->textInserted(this, offset, data.length());
75971

Source/WebCore/dom/CharacterData.h

@@public:
3434 unsigned length() const { return m_data->length(); }
3535 String substringData(unsigned offset, unsigned count, ExceptionCode&);
3636 void appendData(const String&, ExceptionCode&);
37  void insertData(unsigned offset, const String&, ExceptionCode&);
 37 void insertData(unsigned offset, const String&, ExceptionCode&, bool canShowLastCharacterIfSecure = false);
3838 void deleteData(unsigned offset, unsigned count, ExceptionCode&);
3939 void replaceData(unsigned offset, unsigned count, const String&, ExceptionCode&);
4040
75971

Source/WebCore/editing/InsertIntoTextNodeCommand.cpp

2727#include "InsertIntoTextNodeCommand.h"
2828
2929#include "AXObjectCache.h"
 30#include "Frame.h"
3031#include "Text.h"
3132
3233namespace WebCore {

@@void InsertIntoTextNodeCommand::doApply(
4849 return;
4950
5051 ExceptionCode ec;
51  m_node->insertData(m_offset, m_text, ec);
 52 bool canShowLastCharacterIfSecure = (m_node->document()->frame() && m_node->document()->frame()->editor()->ignoreCompositionSelectionChange());
 53 m_node->insertData(m_offset, m_text, ec, canShowLastCharacterIfSecure);
5254
5355 if (AXObjectCache::accessibilityEnabled())
5456 document()->axObjectCache()->nodeTextChangeNotification(m_node->renderer(), AXObjectCache::AXTextInserted, m_offset, m_text.length());
75971

Source/WebCore/rendering/RenderText.cpp

2727
2828#include "AXObjectCache.h"
2929#include "CharacterNames.h"
 30#include "EditorClient.h"
3031#include "EllipsisBox.h"
3132#include "FloatQuad.h"
3233#include "FontTranscoder.h"

@@RenderText::RenderText(Node* node, PassR
105106 , m_isAllASCII(m_text.containsOnlyASCII())
106107 , m_knownToHaveNoOverflowAndNoFallbackFonts(false)
107108 , m_needsTranscoding(false)
 109 , m_shouldSecureLastCharacter(true)
 110 , m_hasSecureLastCharacterTimer(false)
108111{
109112 ASSERT(m_text);
110113

@@void RenderText::setTextInternal(PassRef
11021105 case TSNONE:
11031106 break;
11041107 case TSCIRCLE:
1105  m_text.makeSecure(whiteBullet);
 1108 m_text.makeSecure(whiteBullet, m_shouldSecureLastCharacter);
11061109 break;
11071110 case TSDISC:
1108  m_text.makeSecure(bullet);
 1111 m_text.makeSecure(bullet, m_shouldSecureLastCharacter);
11091112 break;
11101113 case TSSQUARE:
1111  m_text.makeSecure(blackSquare);
 1114 m_text.makeSecure(blackSquare, m_shouldSecureLastCharacter);
11121115 }
11131116 }
11141117

@@void RenderText::checkConsistency() cons
15191522
15201523#endif
15211524
 1525static const float revealLastCharacterDurationInSeconds = 1.0f;
 1526typedef HashMap<RenderText*, Timer<RenderText>* > RenderTextTimerMap;
 1527static RenderTextTimerMap* gSecureLastCharacterTimers = 0;
 1528
 1529void RenderText::momentarilyRevealLastCharacter()
 1530{
 1531 m_shouldSecureLastCharacter = false;
 1532
 1533 if (!gSecureLastCharacterTimers)
 1534 gSecureLastCharacterTimers = new RenderTextTimerMap;
 1535
 1536 Timer<RenderText>* secureLastCharacterTimer;
 1537 if (m_hasSecureLastCharacterTimer) {
 1538 secureLastCharacterTimer = gSecureLastCharacterTimers->get(this);
 1539 secureLastCharacterTimer->stop();
 1540 } else {
 1541 secureLastCharacterTimer = new Timer<RenderText>(this, &RenderText::secureLastCharacter);
 1542 gSecureLastCharacterTimers->add(this, secureLastCharacterTimer);
 1543 m_hasSecureLastCharacterTimer = true;
 1544 }
 1545 secureLastCharacterTimer->startOneShot(revealLastCharacterDurationInSeconds);
 1546}
 1547
 1548void RenderText::secureLastCharacter(Timer<RenderText>*)
 1549{
 1550 secureLastCharacter();
 1551}
 1552
 1553void RenderText::secureLastCharacter()
 1554{
 1555 m_shouldSecureLastCharacter = true;
 1556 setText(m_text.impl(), true);
 1557}
 1558
15221559} // namespace WebCore
75971

Source/WebCore/rendering/RenderText.h

@@public:
116116
117117 bool containsReversedText() const { return m_containsReversedText; }
118118
 119 bool isSecure() { return style()->textSecurity() != TSNONE; }
 120 void momentarilyRevealLastCharacter();
 121 void secureLastCharacter();
 122 void secureLastCharacter(Timer<RenderText> * aTimer);
 123
119124 InlineTextBox* findNextInlineTextBox(int offset, int& pos) const;
120125
121126 bool allowTabs() const { return !style()->collapseWhiteSpace(); }

@@private:
178183 bool m_isAllASCII : 1;
179184 mutable bool m_knownToHaveNoOverflowAndNoFallbackFonts : 1;
180185 bool m_needsTranscoding : 1;
 186 bool m_shouldSecureLastCharacter : 1;
 187 bool m_hasSecureLastCharacterTimer : 1;
181188};
182189
183190inline RenderText* toRenderText(RenderObject* object)
75971

LayoutTests/ChangeLog

 12011-01-17 Chang Shu <chang.shu@nokia.com>
 2
 3 Reviewed by NOBODY (OOPS!).
 4
 5 Based on Samuel Nevala's work.
 6 Added layout tests for text security.
 7 https://bugs.webkit.org/show_bug.cgi?id=32509
 8
 9 * editing/input/secure-text-expected.txt: Added.
 10 * editing/input/secure-text.html: Added.
 11
1122011-01-17 David Kilzer <ddkilzer@apple.com>
213
314 <http://webkit.org/b/52524> fast/dom/Range/range-clone-contents.html fails only on Windows
75971

LayoutTests/editing/input/secure-text-expected.txt

 1Tests if input chars are secured correctly
 2
 3Success: secured right after. expected=false, actual=false
 4Success: secured after delay. expected=false, actual=false
 5Success: secured right after. expected=false, actual=false
 6Success: secured after delay. expected=true, actual=true
 7Success: secured right after. expected=false, actual=false
 8Success: secured after delay. expected=true, actual=true
 9Success: secured right after. expected=true, actual=true
 10Success: secured after delay. expected=true, actual=true
0

LayoutTests/editing/input/secure-text.html

 1<html>
 2<body onload=init()>
 3
 4<p>Tests if input chars are secured correctly
 5<input type='text' id='textnode' />
 6<input type='password' id='passnode' />
 7<ul id="console"></ul>
 8<script language="javascript" type="text/javascript">
 9
 10var tests = [
 11 //format: node_id, [preedit1, preedit2,...,commit_text], text_length, secured_right_after?, secured_after_delay?
 12 ["textnode", ['2','2','b'], 1, false, false], // test text input is not secured any time.
 13 ["passnode", ['2','2','b'], 1, false, true], // test password (when only 1 char) is only secured after a delay.
 14 ["passnode", ['3','3','3','f'], 2, false, true],// test password (when more than 1 char) is only secured after a delay.
 15 ["passnode", ['backspace'], 1, true, true] // test password is secured all the time when deleting.
 16 ];
 17
 18var testIdx = -1;
 19
 20function secureChar()
 21{
 22 var element = document.getElementById('passnode');
 23 var securechar = document.defaultView.getComputedStyle(element, "").getPropertyValue("-webkit-text-security");
 24 switch(securechar) {
 25 case "square":
 26 return String.fromCharCode(0x25A0);
 27 case "disc":
 28 return String.fromCharCode(0x2022);
 29 case "circle":
 30 return String.fromCharCode(0x25E6);
 31 }
 32}
 33
 34function secureText(textLength)
 35{
 36 var text = "";
 37 for(var counter=0; counter<textLength; counter++)
 38 text += secureChar();
 39 return text;
 40}
 41
 42function log(msg)
 43{
 44 var console = document.getElementById("console");
 45 var li = document.createElement("li");
 46 li.appendChild(document.createTextNode(msg));
 47 console.appendChild(li);
 48}
 49
 50function assert(expected, actual, msg)
 51{
 52 if (expected != actual)
 53 log("Error: " + msg + " expected=" + expected + ", actual=" + actual);
 54 else
 55 log("Success: " + msg + " expected=" + expected + ", actual=" + actual);
 56}
 57
 58function run()
 59{
 60 var expectedSecureTextLen;
 61 if (testIdx >= 0) {
 62 expectedSecureTextLen = tests[testIdx][2];
 63 textInputController.doCommand("moveForward:");
 64 assert(tests[testIdx][4], window.find(secureText(expectedSecureTextLen), false, true), "secured after delay.");
 65 }
 66 testIdx++;
 67 if (testIdx >= tests.length) {
 68 layoutTestController.notifyDone();
 69 return;
 70 }
 71
 72 var node = document.getElementById(tests[testIdx][0]);
 73 node.focus();
 74 textInputController.doCommand("moveForward:");
 75
 76 expectedSecureTextLen = tests[testIdx][2];
 77 var charSequence = tests[testIdx][1];
 78 for(var i = 0; i < charSequence.length - 1; i++) {
 79 textInputController.setMarkedText(charSequence[i], node.value.length, node.value.length);
 80 }
 81 if (charSequence[charSequence.length - 1] == "backspace") {
 82 textInputController.doCommand("deleteBackward:");
 83 } else {
 84 textInputController.insertText(charSequence[charSequence.length - 1]);
 85 }
 86 assert(tests[testIdx][3], window.find(secureText(expectedSecureTextLen), false, true), "secured right after.");
 87 window.setTimeout(run, 1200);
 88}
 89
 90function init()
 91{
 92 if (window.layoutTestController) {
 93 layoutTestController.dumpAsText();
 94 layoutTestController.waitUntilDone();
 95 run();
 96 }
 97}
 98</script>
 99</body>
 100</html>
 101
0