~ubuntu-branches/ubuntu/precise/kompozer/precise

« back to all changes in this revision

Viewing changes to mozilla/layout/html/base/src/nsTextTransformer.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Anthony Yarusso
  • Date: 2007-08-27 01:11:03 UTC
  • Revision ID: james.westby@ubuntu.com-20070827011103-2jgf4s6532gqu2ka
Tags: upstream-0.7.10
ImportĀ upstreamĀ versionĀ 0.7.10

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 
2
/* ***** BEGIN LICENSE BLOCK *****
 
3
 * Version: NPL 1.1/GPL 2.0/LGPL 2.1
 
4
 *
 
5
 * The contents of this file are subject to the Netscape Public License
 
6
 * Version 1.1 (the "License"); you may not use this file except in
 
7
 * compliance with the License. You may obtain a copy of the License at
 
8
 * http://www.mozilla.org/NPL/
 
9
 *
 
10
 * Software distributed under the License is distributed on an "AS IS" basis,
 
11
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 
12
 * for the specific language governing rights and limitations under the
 
13
 * License.
 
14
 *
 
15
 * The Original Code is Mozilla Communicator client code.
 
16
 *
 
17
 * The Initial Developer of the Original Code is
 
18
 * Netscape Communications Corporation.
 
19
 * Portions created by the Initial Developer are Copyright (C) 1998
 
20
 * the Initial Developer. All Rights Reserved.
 
21
 *
 
22
 * Contributor(s):
 
23
 *
 
24
 * Alternatively, the contents of this file may be used under the terms of
 
25
 * either the GNU General Public License Version 2 or later (the "GPL"), or 
 
26
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 
27
 * in which case the provisions of the GPL or the LGPL are applicable instead
 
28
 * of those above. If you wish to allow use of your version of this file only
 
29
 * under the terms of either the GPL or the LGPL, and not to allow others to
 
30
 * use your version of this file under the terms of the NPL, indicate your
 
31
 * decision by deleting the provisions above and replace them with the notice
 
32
 * and other provisions required by the GPL or the LGPL. If you do not delete
 
33
 * the provisions above, a recipient may use your version of this file under
 
34
 * the terms of any one of the NPL, the GPL or the LGPL.
 
35
 *
 
36
 * ***** END LICENSE BLOCK ***** */
 
37
#include <ctype.h>
 
38
#include "nsCOMPtr.h"
 
39
#include "nsTextTransformer.h"
 
40
#include "nsIContent.h"
 
41
#include "nsIFrame.h"
 
42
#include "nsITextContent.h"
 
43
#include "nsStyleConsts.h"
 
44
#include "nsILineBreaker.h"
 
45
#include "nsIWordBreaker.h"
 
46
#include "nsIServiceManager.h"
 
47
#include "nsUnicharUtilCIID.h"
 
48
#include "nsUnicharUtils.h"
 
49
#include "nsICaseConversion.h"
 
50
#include "prenv.h"
 
51
#include "nsIPrefBranchInternal.h"
 
52
#include "nsIPrefBranch.h"
 
53
#include "nsIPrefService.h"
 
54
#ifdef IBMBIDI
 
55
#include "nsLayoutAtoms.h"
 
56
#endif
 
57
 
 
58
 
 
59
nsTextTransformer::WordSelectListener *nsTextTransformer::sWordSelectListener = nsnull;
 
60
PRBool nsTextTransformer::sWordSelectStopAtPunctuation = PR_FALSE;
 
61
static const char kWordSelectPref[] = "layout.word_select.stop_at_punctuation";
 
62
 
 
63
NS_IMPL_ISUPPORTS1(nsTextTransformer::WordSelectListener, nsIObserver)
 
64
 
 
65
NS_IMETHODIMP
 
66
nsTextTransformer::WordSelectListener::Observe(nsISupports *aSubject,
 
67
                                                 const char *aTopic,
 
68
                                                 const PRUnichar *aData)
 
69
{
 
70
  NS_ASSERTION(!nsCRT::strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID),
 
71
               "wrong topic");
 
72
  nsCOMPtr<nsIPrefBranch> prefBranch = do_QueryInterface(aSubject);
 
73
  return prefBranch->GetBoolPref(kWordSelectPref, &sWordSelectStopAtPunctuation);
 
74
}
 
75
 
 
76
nsAutoTextBuffer::nsAutoTextBuffer()
 
77
  : mBuffer(mAutoBuffer),
 
78
    mBufferLen(NS_TEXT_TRANSFORMER_AUTO_WORD_BUF_SIZE)
 
79
{
 
80
}
 
81
 
 
82
nsAutoTextBuffer::~nsAutoTextBuffer()
 
83
{
 
84
  if (mBuffer && (mBuffer != mAutoBuffer)) {
 
85
    delete [] mBuffer;
 
86
  }
 
87
}
 
88
 
 
89
nsresult
 
90
nsAutoTextBuffer::GrowBy(PRInt32 aAtLeast, PRBool aCopyToHead)
 
91
{
 
92
  PRInt32 newSize = mBufferLen * 2;
 
93
  if (newSize < mBufferLen + aAtLeast) {
 
94
    newSize = mBufferLen + aAtLeast + 100;
 
95
  }
 
96
  return GrowTo(newSize, aCopyToHead);
 
97
}
 
98
 
 
99
nsresult
 
100
nsAutoTextBuffer::GrowTo(PRInt32 aNewSize, PRBool aCopyToHead)
 
101
{
 
102
  if (aNewSize > mBufferLen) {
 
103
    PRUnichar* newBuffer = new PRUnichar[aNewSize];
 
104
    if (!newBuffer) {
 
105
      return NS_ERROR_OUT_OF_MEMORY;
 
106
    }
 
107
    memcpy(&newBuffer[aCopyToHead ? 0 : mBufferLen],
 
108
           mBuffer, sizeof(PRUnichar) * mBufferLen);
 
109
    if (mBuffer != mAutoBuffer) {
 
110
      delete [] mBuffer;
 
111
    }
 
112
    mBuffer = newBuffer;
 
113
    mBufferLen = aNewSize;
 
114
  }
 
115
  return NS_OK;
 
116
}
 
117
 
 
118
//----------------------------------------------------------------------
 
119
 
 
120
static NS_DEFINE_CID(kUnicharUtilCID, NS_UNICHARUTIL_CID);
 
121
 
 
122
static nsICaseConversion* gCaseConv =  nsnull;
 
123
 
 
124
nsresult
 
125
nsTextTransformer::Initialize()
 
126
{
 
127
  // read in our global word selection prefs
 
128
  if ( !sWordSelectListener ) {
 
129
    nsCOMPtr<nsIPrefBranchInternal> prefBranch =
 
130
        do_GetService( NS_PREFSERVICE_CONTRACTID );
 
131
    if ( prefBranch ) {
 
132
      prefBranch->GetBoolPref(kWordSelectPref, &sWordSelectStopAtPunctuation);
 
133
      sWordSelectListener = new WordSelectListener();
 
134
      if (sWordSelectListener) {
 
135
        NS_ADDREF(sWordSelectListener);
 
136
        prefBranch->AddObserver(kWordSelectPref, sWordSelectListener, PR_FALSE);
 
137
      }
 
138
    }
 
139
  }
 
140
 
 
141
  return NS_OK;
 
142
}
 
143
static nsresult EnsureCaseConv()
 
144
{
 
145
  nsresult res = NS_OK;
 
146
  if (!gCaseConv) {
 
147
    res = nsServiceManager::GetService(kUnicharUtilCID, NS_GET_IID(nsICaseConversion),
 
148
                                       (nsISupports**)&gCaseConv);
 
149
    NS_ASSERTION( NS_SUCCEEDED(res), "cannot get UnicharUtil");
 
150
    NS_ASSERTION( gCaseConv != NULL, "cannot get UnicharUtil");
 
151
  }
 
152
  return res;
 
153
}
 
154
 
 
155
void
 
156
nsTextTransformer::Shutdown()
 
157
{
 
158
  NS_IF_RELEASE(sWordSelectListener);
 
159
  if (gCaseConv) {
 
160
    nsServiceManager::ReleaseService(kUnicharUtilCID, gCaseConv);
 
161
    gCaseConv = nsnull;
 
162
  }
 
163
}
 
164
 
 
165
// For now, we have only a couple of characters to strip out. If we get
 
166
// any more, change this to use a bitset to lookup into.
 
167
//   CH_SHY - soft hyphen (discretionary hyphen)
 
168
#ifdef IBMBIDI
 
169
// added BIDI formatting codes
 
170
#define IS_DISCARDED(_ch) \
 
171
  (((_ch) == CH_SHY) || ((_ch) == '\r') || IS_BIDI_CONTROL(_ch))
 
172
#else
 
173
#define IS_DISCARDED(_ch) \
 
174
  (((_ch) == CH_SHY) || ((_ch) == '\r'))
 
175
#endif
 
176
 
 
177
 
 
178
#define MAX_UNIBYTE 127
 
179
 
 
180
MOZ_DECL_CTOR_COUNTER(nsTextTransformer)
 
181
 
 
182
nsTextTransformer::nsTextTransformer(nsILineBreaker* aLineBreaker,
 
183
                                     nsIWordBreaker* aWordBreaker,
 
184
                                     nsIPresContext* aPresContext)
 
185
  : mFrag(nsnull),
 
186
    mOffset(0),
 
187
    mMode(eNormal),
 
188
    mLineBreaker(aLineBreaker),
 
189
    mWordBreaker(aWordBreaker),
 
190
    mBufferPos(0),
 
191
    mTextTransform(NS_STYLE_TEXT_TRANSFORM_NONE),
 
192
    mFlags(0)
 
193
{
 
194
  MOZ_COUNT_CTOR(nsTextTransformer);
 
195
 
 
196
  aPresContext->
 
197
    GetLanguageSpecificTransformType(&mLanguageSpecificTransformType);
 
198
 
 
199
#ifdef IBMBIDI
 
200
  mPresContext = aPresContext;
 
201
#endif
 
202
  if (aLineBreaker == nsnull && aWordBreaker == nsnull )
 
203
    NS_ASSERTION(0, "invalid creation of nsTextTransformer");
 
204
  
 
205
#ifdef DEBUG
 
206
  static PRBool firstTime = PR_TRUE;
 
207
  if (firstTime) {
 
208
    firstTime = PR_FALSE;
 
209
    SelfTest(aLineBreaker, aWordBreaker, aPresContext);
 
210
  }
 
211
#endif
 
212
}
 
213
 
 
214
nsTextTransformer::~nsTextTransformer()
 
215
{
 
216
  MOZ_COUNT_DTOR(nsTextTransformer);
 
217
}
 
218
 
 
219
nsresult
 
220
nsTextTransformer::Init(nsIFrame* aFrame,
 
221
                        nsIContent* aContent,
 
222
                        PRInt32 aStartingOffset,
 
223
                        PRBool aForceArabicShaping,
 
224
                        PRBool aLeaveAsAscii)
 
225
{
 
226
  /*
 
227
   * If the document has Bidi content, check whether we need to do
 
228
   * Arabic shaping.
 
229
   *
 
230
   *  Does the frame contains Arabic characters
 
231
   *   (mCharType == eCharType_RightToLeftArabic)?
 
232
   *  Are we rendering character by character (aForceArabicShaping ==
 
233
   *   PR_TRUE)? If so, we always do our own Arabic shaping, even if
 
234
   *   the platform has native shaping support. Otherwise, we only do
 
235
   *   shaping if the platform has no shaping support.
 
236
   *
 
237
   *  We do numeric shaping in all Bidi documents.
 
238
   */
 
239
  PRBool bidiEnabled;
 
240
 
 
241
  mPresContext->GetBidiEnabled(&bidiEnabled);
 
242
  if (bidiEnabled) {
 
243
    aFrame->GetBidiProperty(mPresContext, nsLayoutAtoms::charType,
 
244
                            (void**)&mCharType, sizeof(mCharType));
 
245
    if (mCharType == eCharType_RightToLeftArabic) {
 
246
      if (aForceArabicShaping) {
 
247
        SetNeedsArabicShaping(PR_TRUE);
 
248
      }
 
249
      else {
 
250
        if (!mPresContext->IsBidiSystem()) {
 
251
          SetNeedsArabicShaping(PR_TRUE);
 
252
        }
 
253
      }
 
254
    }
 
255
    SetNeedsNumericShaping(PR_TRUE);
 
256
  }
 
257
 
 
258
  // Get the contents text content
 
259
  nsresult rv;
 
260
  nsCOMPtr<nsITextContent> tc = do_QueryInterface(aContent, &rv);
 
261
  if (tc.get()) {
 
262
    tc->GetText(&mFrag);
 
263
 
 
264
    // Sanitize aStartingOffset
 
265
    if (aStartingOffset < 0) {
 
266
      NS_WARNING("bad starting offset");
 
267
      aStartingOffset = 0;
 
268
    }
 
269
    else if (aStartingOffset > mFrag->GetLength()) {
 
270
      NS_WARNING("bad starting offset");
 
271
      aStartingOffset = mFrag->GetLength();
 
272
    }
 
273
    mOffset = aStartingOffset;
 
274
 
 
275
    // Get the frames text style information
 
276
    const nsStyleText* styleText = aFrame->GetStyleText();
 
277
    if (NS_STYLE_WHITESPACE_PRE == styleText->mWhiteSpace) {
 
278
      mMode = ePreformatted;
 
279
    }
 
280
    else if (NS_STYLE_WHITESPACE_MOZ_PRE_WRAP == styleText->mWhiteSpace) {
 
281
      mMode = ePreWrap;
 
282
    }
 
283
    mTextTransform = styleText->mTextTransform;
 
284
    
 
285
    if (aLeaveAsAscii) { // See if the text fragment is 1-byte text
 
286
      SetLeaveAsAscii(PR_TRUE);     
 
287
      // XXX Currently we only leave it as ascii for normal text and not for preformatted
 
288
      // or preformatted wrapped text or language specific transforms
 
289
      if (mFrag->Is2b() || (eNormal != mMode) ||
 
290
          (mLanguageSpecificTransformType !=
 
291
           eLanguageSpecificTransformType_None))
 
292
        // We don't step down from Unicode to ascii
 
293
        SetLeaveAsAscii(PR_FALSE);           
 
294
    } 
 
295
    else 
 
296
      SetLeaveAsAscii(PR_FALSE);
 
297
  }
 
298
  return rv;
 
299
}
 
300
 
 
301
//----------------------------------------------------------------------
 
302
 
 
303
// wordlen==1, contentlen=newOffset-currentOffset, isWhitespace=t
 
304
PRInt32
 
305
nsTextTransformer::ScanNormalWhiteSpace_F()
 
306
{
 
307
  const nsTextFragment* frag = mFrag;
 
308
  PRInt32 fragLen = frag->GetLength();
 
309
  PRInt32 offset = mOffset;
 
310
 
 
311
  for (; offset < fragLen; offset++) {
 
312
    PRUnichar ch = frag->CharAt(offset);
 
313
    if (!XP_IS_SPACE(ch)) {
 
314
      // If character is not discardable then stop looping, otherwise
 
315
      // let the discarded character collapse with the other spaces.
 
316
      if (!IS_DISCARDED(ch)) {
 
317
        break;
 
318
      }
 
319
    }
 
320
  }
 
321
 
 
322
  // Make sure we have enough room in the transform buffer
 
323
  if (mBufferPos >= mTransformBuf.mBufferLen) {
 
324
    mTransformBuf.GrowBy(128);
 
325
  }
 
326
 
 
327
  if (TransformedTextIsAscii()) {
 
328
    unsigned char*  bp = (unsigned char*)mTransformBuf.mBuffer;
 
329
    bp[mBufferPos++] = ' ';
 
330
  } else {
 
331
    mTransformBuf.mBuffer[mBufferPos++] = PRUnichar(' ');
 
332
  }
 
333
  return offset;
 
334
}
 
335
  
 
336
void
 
337
nsTextTransformer::ConvertTransformedTextToUnicode()
 
338
{
 
339
  // Go backwards over the characters and convert them.
 
340
  PRInt32         lastChar = mBufferPos - 1;
 
341
  unsigned char*  cp1 = (unsigned char*)mTransformBuf.mBuffer + lastChar;
 
342
  PRUnichar*      cp2 = mTransformBuf.mBuffer + lastChar;
 
343
  
 
344
  NS_ASSERTION(mTransformBuf.mBufferLen >= mBufferPos,
 
345
               "transform buffer is too small");
 
346
  for (PRInt32 count = mBufferPos; count > 0; count--) {
 
347
    *cp2-- = PRUnichar(*cp1--);
 
348
  }
 
349
}
 
350
 
 
351
// wordlen==*aWordLen, contentlen=newOffset-currentOffset, isWhitespace=f
 
352
PRInt32
 
353
nsTextTransformer::ScanNormalAsciiText_F(PRInt32* aWordLen,
 
354
                                         PRBool*  aWasTransformed)
 
355
{
 
356
  const nsTextFragment* frag = mFrag;
 
357
  PRInt32 fragLen = frag->GetLength();
 
358
  PRInt32 offset = mOffset;
 
359
  PRInt32 prevBufferPos = mBufferPos;
 
360
  const unsigned char* cp = (const unsigned char*)frag->Get1b() + offset;
 
361
  union {
 
362
    unsigned char* bp1;
 
363
    PRUnichar* bp2;
 
364
  };
 
365
  bp2 = mTransformBuf.GetBuffer();
 
366
  if (TransformedTextIsAscii()) {
 
367
    bp1 += mBufferPos;
 
368
  } else {
 
369
    bp2 += mBufferPos;
 
370
  }
 
371
 
 
372
  for (; offset < fragLen; offset++) {
 
373
    unsigned char ch = *cp++;
 
374
    if (XP_IS_SPACE(ch)) {
 
375
      break;
 
376
    }
 
377
    if (CH_NBSP == ch) {
 
378
      ch = ' ';
 
379
      *aWasTransformed = PR_TRUE;
 
380
    }
 
381
    else if (IS_DISCARDED(ch)) {
 
382
      // Strip discarded characters from the transformed output
 
383
      continue;
 
384
    }
 
385
    if (ch > MAX_UNIBYTE) {
 
386
      // The text has a multibyte character so we can no longer leave the
 
387
      // text as ascii text
 
388
      SetHasMultibyte(PR_TRUE);        
 
389
                
 
390
      if (TransformedTextIsAscii()) { 
 
391
        SetTransformedTextIsAscii(PR_FALSE);
 
392
        *aWasTransformed = PR_TRUE;
 
393
 
 
394
        // Transform any existing ascii text to Unicode
 
395
        if (mBufferPos > 0) {
 
396
          ConvertTransformedTextToUnicode();
 
397
          bp2 = mTransformBuf.GetBuffer() + mBufferPos;
 
398
        }
 
399
      }
 
400
    }
 
401
    if (mBufferPos >= mTransformBuf.mBufferLen) {
 
402
      nsresult rv = mTransformBuf.GrowBy(128);
 
403
      if (NS_FAILED(rv)) {
 
404
        // If we run out of space then just truncate the text
 
405
        break;
 
406
      }
 
407
      bp2 = mTransformBuf.GetBuffer();
 
408
      if (TransformedTextIsAscii()) {
 
409
        bp1 += mBufferPos;
 
410
      } else {
 
411
        bp2 += mBufferPos;
 
412
      }
 
413
    }
 
414
    if (TransformedTextIsAscii()) {
 
415
      *bp1++ = ch;
 
416
    } else {
 
417
      *bp2++ = PRUnichar(ch);
 
418
    }
 
419
    mBufferPos++;
 
420
  }
 
421
 
 
422
  *aWordLen = mBufferPos - prevBufferPos;
 
423
  return offset;
 
424
}
 
425
 
 
426
PRInt32
 
427
nsTextTransformer::ScanNormalAsciiText_F_ForWordBreak(PRInt32* aWordLen,
 
428
                                         PRBool*  aWasTransformed,
 
429
                                         PRBool aIsKeyboardSelect)
 
430
{
 
431
  const nsTextFragment* frag = mFrag;
 
432
  PRInt32 fragLen = frag->GetLength();
 
433
  PRInt32 offset = mOffset;
 
434
  PRInt32 prevBufferPos = mBufferPos;
 
435
  PRBool breakAfterThis = PR_FALSE;
 
436
  const unsigned char* cp = (const unsigned char*)frag->Get1b() + offset;
 
437
  union {
 
438
    unsigned char* bp1;
 
439
    PRUnichar* bp2;
 
440
  };
 
441
  bp2 = mTransformBuf.GetBuffer();
 
442
  if (TransformedTextIsAscii()) {
 
443
    bp1 += mBufferPos;
 
444
  } else {
 
445
    bp2 += mBufferPos;
 
446
  }
 
447
  PRBool readingAlphaNumeric = PR_TRUE; //only used in sWordSelectStopAtPunctuation
 
448
 
 
449
  // We must know if we are starting in alpha numerics.
 
450
  // Treat high bit chars as alphanumeric, otherwise we get stuck on accented letters
 
451
  // We can't trust isalnum() results for isalnum()
 
452
  // Therefore we don't stop at non-ascii (high bit) punctuation,
 
453
  // which is just fine. The punctuation we care about is low bit.
 
454
  if (sWordSelectStopAtPunctuation && offset < fragLen)
 
455
    readingAlphaNumeric = isalnum((unsigned char)*cp) || !IS_ASCII_CHAR(*cp);
 
456
  
 
457
  for (; offset < fragLen && !breakAfterThis; offset++) {
 
458
    unsigned char ch = *cp++;
 
459
    if (CH_NBSP == ch) {
 
460
      ch = ' ';
 
461
      *aWasTransformed = PR_TRUE;
 
462
      if (offset == mOffset)
 
463
        breakAfterThis = PR_TRUE;
 
464
      else
 
465
        break;
 
466
    }
 
467
    else if (XP_IS_SPACE(ch)) {
 
468
      break;
 
469
    }
 
470
    else if (sWordSelectStopAtPunctuation && 
 
471
             readingAlphaNumeric && !isalnum(ch) && IS_ASCII_CHAR(ch)) {
 
472
      if (!aIsKeyboardSelect)
 
473
        break;
 
474
      // For keyboard move-by-word, need to pass by at least
 
475
      // one alphanumeric char before stopping at punct
 
476
      readingAlphaNumeric = PR_FALSE;
 
477
    }
 
478
    else if (sWordSelectStopAtPunctuation && 
 
479
            !readingAlphaNumeric && (isalnum(ch) || !IS_ASCII_CHAR(ch))) {
 
480
      // On some platforms, punctuation breaks for word selection
 
481
      break;
 
482
    }
 
483
    else if (IS_DISCARDED(ch)) {
 
484
      // Strip discarded characters from the transformed output
 
485
      continue;
 
486
    }
 
487
    if (ch > MAX_UNIBYTE) {
 
488
      // The text has a multibyte character so we can no longer leave the
 
489
      // text as ascii text
 
490
      SetHasMultibyte(PR_TRUE);
 
491
 
 
492
      if (TransformedTextIsAscii()) {
 
493
        SetTransformedTextIsAscii(PR_FALSE);
 
494
        *aWasTransformed = PR_TRUE;
 
495
 
 
496
        // Transform any existing ascii text to Unicode
 
497
        if (mBufferPos > 0) {
 
498
          ConvertTransformedTextToUnicode();
 
499
          bp2 = mTransformBuf.GetBuffer() + mBufferPos;
 
500
        }
 
501
      }
 
502
    }
 
503
    if (mBufferPos >= mTransformBuf.mBufferLen) {
 
504
      nsresult rv = mTransformBuf.GrowBy(128);
 
505
      if (NS_FAILED(rv)) {
 
506
        // If we run out of space then just truncate the text
 
507
        break;
 
508
      }
 
509
      bp2 = mTransformBuf.GetBuffer();
 
510
      if (TransformedTextIsAscii()) {
 
511
        bp1 += mBufferPos;
 
512
      } else {
 
513
        bp2 += mBufferPos;
 
514
      }
 
515
    }
 
516
    if (TransformedTextIsAscii()) {
 
517
      *bp1++ = ch;
 
518
    } else {
 
519
      *bp2++ = PRUnichar(ch);
 
520
    }
 
521
    mBufferPos++;
 
522
  }
 
523
 
 
524
  *aWordLen = mBufferPos - prevBufferPos;
 
525
  return offset;
 
526
}
 
527
 
 
528
 
 
529
// wordlen==*aWordLen, contentlen=newOffset-currentOffset, isWhitespace=f
 
530
PRInt32
 
531
nsTextTransformer::ScanNormalUnicodeText_F(PRBool   aForLineBreak,
 
532
                                           PRInt32* aWordLen,
 
533
                                           PRBool*  aWasTransformed)
 
534
{
 
535
  const nsTextFragment* frag = mFrag;
 
536
  const PRUnichar* cp0 = frag->Get2b();
 
537
  PRInt32 fragLen = frag->GetLength();
 
538
#ifdef IBMBIDI
 
539
  if (*aWordLen > 0 && *aWordLen < fragLen) {
 
540
    fragLen = *aWordLen;
 
541
  }
 
542
#endif
 
543
  PRInt32 offset = mOffset;
 
544
 
 
545
  PRUnichar firstChar = frag->CharAt(offset++);
 
546
 
 
547
#ifdef IBMBIDI
 
548
  // Need to strip BIDI controls even when those are 'firstChars'.
 
549
  // This doesn't seem to produce bug 14280 (or similar bugs).
 
550
  while (offset < fragLen && IS_BIDI_CONTROL(firstChar) ) {
 
551
    firstChar = frag->CharAt(offset++);
 
552
  }
 
553
#endif // IBMBIDI
 
554
 
 
555
  if (firstChar > MAX_UNIBYTE) SetHasMultibyte(PR_TRUE);
 
556
 
 
557
  // Only evaluate complex breaking logic if there are more characters
 
558
  // beyond the first to look at.
 
559
  PRInt32 numChars = 1;
 
560
  if (offset < fragLen) {
 
561
    const PRUnichar* cp = cp0 + offset;
 
562
    PRBool breakBetween = PR_FALSE;
 
563
    if (aForLineBreak) {
 
564
      mLineBreaker->BreakInBetween(&firstChar, 1, cp, (fragLen-offset), &breakBetween);
 
565
    }
 
566
    else {
 
567
      mWordBreaker->BreakInBetween(&firstChar, 1, cp, (fragLen-offset), &breakBetween);
 
568
    }
 
569
 
 
570
    // don't transform the first character until after BreakInBetween is called
 
571
    // Kipp originally did this at the top of the function, which was too early.
 
572
    // see bug 14280
 
573
    if (CH_NBSP == firstChar) {
 
574
      firstChar = ' ';
 
575
      *aWasTransformed = PR_TRUE;
 
576
    }
 
577
    nsresult rv = mTransformBuf.GrowTo(mBufferPos + 1);
 
578
    if (NS_FAILED(rv)) {
 
579
                *aWordLen = 0;
 
580
                return offset - 1;
 
581
        }
 
582
 
 
583
    mTransformBuf.mBuffer[mBufferPos++] = firstChar;
 
584
 
 
585
    if (!breakBetween) {
 
586
      // Find next position
 
587
      PRBool tryNextFrag;
 
588
      PRUint32 next;
 
589
      if (aForLineBreak) {
 
590
        mLineBreaker->Next(cp0, fragLen, offset, &next, &tryNextFrag);
 
591
      }
 
592
      else {
 
593
        mWordBreaker->NextWord(cp0, fragLen, offset, &next, &tryNextFrag);
 
594
      }
 
595
      numChars = (PRInt32) (next - (PRUint32) offset) + 1;
 
596
 
 
597
      // Since we know the number of characters we're adding grow the buffer
 
598
      // now before we start copying
 
599
      nsresult rv = mTransformBuf.GrowTo(mBufferPos + numChars);
 
600
      if (NS_FAILED(rv)) {
 
601
        numChars = mTransformBuf.GetBufferLength() - mBufferPos;
 
602
      }
 
603
 
 
604
      offset += numChars - 1;
 
605
 
 
606
      // 1. convert nbsp into space
 
607
      // 2. check for discarded characters
 
608
      // 3. check mHasMultibyte flag
 
609
      // 4. copy buffer
 
610
      PRUnichar* bp = &mTransformBuf.mBuffer[mBufferPos];
 
611
      const PRUnichar* end = cp + numChars - 1;
 
612
      while (cp < end) {
 
613
        PRUnichar ch = *cp++;
 
614
        if (CH_NBSP == ch) {
 
615
          ch = ' ';
 
616
        }
 
617
        else if (IS_DISCARDED(ch) || (ch == 0x0a) || (ch == 0x0d)) {
 
618
          // Strip discarded characters from the transformed output
 
619
          numChars--;
 
620
          continue;
 
621
        }
 
622
        if (ch > MAX_UNIBYTE) SetHasMultibyte(PR_TRUE);
 
623
        *bp++ = ch;
 
624
        mBufferPos++;
 
625
      }
 
626
    }
 
627
  }
 
628
  else 
 
629
  { // transform the first character
 
630
    // we do this here, rather than at the top of the function (like Kipp originally had it)
 
631
    // because if we must call BreakInBetween, then we must do so before the transformation
 
632
    // this is the case where BreakInBetween does not need to be called at all.
 
633
    // see bug 14280
 
634
    if (CH_NBSP == firstChar) {
 
635
      firstChar = ' ';
 
636
      *aWasTransformed = PR_TRUE;
 
637
    }
 
638
    nsresult rv = mTransformBuf.GrowTo(mBufferPos + 1);
 
639
    if (NS_FAILED(rv)) {
 
640
                *aWordLen = 0;
 
641
                return offset - 1;
 
642
        }
 
643
    mTransformBuf.mBuffer[mBufferPos++] = firstChar;
 
644
  }
 
645
 
 
646
  *aWordLen = numChars;
 
647
  return offset;
 
648
}
 
649
 
 
650
// wordlen==*aWordLen, contentlen=newOffset-currentOffset, isWhitespace=t
 
651
PRInt32
 
652
nsTextTransformer::ScanPreWrapWhiteSpace_F(PRInt32* aWordLen)
 
653
{
 
654
  const nsTextFragment* frag = mFrag;
 
655
  PRInt32 fragLen = frag->GetLength();
 
656
  PRInt32 offset = mOffset;
 
657
  PRUnichar* bp = mTransformBuf.GetBuffer() + mBufferPos;
 
658
  PRUnichar* endbp = mTransformBuf.GetBufferEnd();
 
659
  PRInt32 prevBufferPos = mBufferPos;
 
660
 
 
661
  for (; offset < fragLen; offset++) {
 
662
    // This function is used for both Unicode and ascii strings so don't
 
663
    // make any assumptions about what kind of data it is
 
664
    PRUnichar ch = frag->CharAt(offset);
 
665
    if (!XP_IS_SPACE(ch) || (ch == '\t') || (ch == '\n')) {
 
666
      if (IS_DISCARDED(ch)) {
 
667
        // Keep looping if this is a discarded character
 
668
        continue;
 
669
      }
 
670
      break;
 
671
    }
 
672
    if (bp == endbp) {
 
673
      PRInt32 oldLength = bp - mTransformBuf.GetBuffer();
 
674
      nsresult rv = mTransformBuf.GrowBy(1000);
 
675
      if (NS_FAILED(rv)) {
 
676
        // If we run out of space (unlikely) then just chop the input
 
677
        break;
 
678
      }
 
679
      bp = mTransformBuf.GetBuffer() + oldLength;
 
680
      endbp = mTransformBuf.GetBufferEnd();
 
681
    }
 
682
    *bp++ = ' ';
 
683
    mBufferPos++;
 
684
  }
 
685
 
 
686
  *aWordLen = mBufferPos - prevBufferPos;
 
687
  return offset;
 
688
}
 
689
 
 
690
// wordlen==*aWordLen, contentlen=newOffset-currentOffset, isWhitespace=f
 
691
PRInt32
 
692
nsTextTransformer::ScanPreData_F(PRInt32* aWordLen,
 
693
                                 PRBool*  aWasTransformed)
 
694
{
 
695
  const nsTextFragment* frag = mFrag;
 
696
  PRInt32 fragLen = frag->GetLength();
 
697
  PRInt32 offset = mOffset;
 
698
  PRUnichar* bp = mTransformBuf.GetBuffer() + mBufferPos;
 
699
  PRUnichar* endbp = mTransformBuf.GetBufferEnd();
 
700
  PRInt32 prevBufferPos = mBufferPos;
 
701
 
 
702
  for (; offset < fragLen; offset++) {
 
703
    // This function is used for both Unicode and ascii strings so don't
 
704
    // make any assumptions about what kind of data it is
 
705
    PRUnichar ch = frag->CharAt(offset);
 
706
    if ((ch == '\t') || (ch == '\n')) {
 
707
      break;
 
708
    }
 
709
    if (CH_NBSP == ch) {
 
710
      ch = ' ';
 
711
      *aWasTransformed = PR_TRUE;
 
712
    }
 
713
    else if (IS_DISCARDED(ch)) {
 
714
      continue;
 
715
    }
 
716
    if (ch > MAX_UNIBYTE) SetHasMultibyte(PR_TRUE);
 
717
    if (bp == endbp) {
 
718
      PRInt32 oldLength = bp - mTransformBuf.GetBuffer();
 
719
      nsresult rv = mTransformBuf.GrowBy(1000);
 
720
      if (NS_FAILED(rv)) {
 
721
        // If we run out of space (unlikely) then just chop the input
 
722
        break;
 
723
      }
 
724
      bp = mTransformBuf.GetBuffer() + oldLength;
 
725
      endbp = mTransformBuf.GetBufferEnd();
 
726
    }
 
727
    *bp++ = ch;
 
728
    mBufferPos++;
 
729
  }
 
730
 
 
731
  *aWordLen = mBufferPos - prevBufferPos;
 
732
  return offset;
 
733
}
 
734
 
 
735
// wordlen==*aWordLen, contentlen=newOffset-currentOffset, isWhitespace=f
 
736
PRInt32
 
737
nsTextTransformer::ScanPreAsciiData_F(PRInt32* aWordLen,
 
738
                                      PRBool*  aWasTransformed)
 
739
{
 
740
  const nsTextFragment* frag = mFrag;
 
741
  PRUnichar* bp = mTransformBuf.GetBuffer() + mBufferPos;
 
742
  PRUnichar* endbp = mTransformBuf.GetBufferEnd();
 
743
  const unsigned char* cp = (const unsigned char*) frag->Get1b();
 
744
  const unsigned char* end = cp + frag->GetLength();
 
745
  PRInt32 prevBufferPos = mBufferPos;
 
746
  cp += mOffset;
 
747
 
 
748
  while (cp < end) {
 
749
    PRUnichar ch = (PRUnichar) *cp++;
 
750
    if ((ch == '\t') || (ch == '\n')) {
 
751
      cp--;
 
752
      break;
 
753
    }
 
754
    if (CH_NBSP == ch) {
 
755
      ch = ' ';
 
756
      *aWasTransformed = PR_TRUE;
 
757
    }
 
758
    else if (IS_DISCARDED(ch)) {
 
759
      continue;
 
760
    }
 
761
    if (ch > MAX_UNIBYTE) SetHasMultibyte(PR_TRUE);
 
762
    if (bp == endbp) {
 
763
      PRInt32 oldLength = bp - mTransformBuf.GetBuffer();
 
764
      nsresult rv = mTransformBuf.GrowBy(1000);
 
765
      if (NS_FAILED(rv)) {
 
766
        // If we run out of space (unlikely) then just chop the input
 
767
        break;
 
768
      }
 
769
      bp = mTransformBuf.GetBuffer() + oldLength;
 
770
      endbp = mTransformBuf.GetBufferEnd();
 
771
    }
 
772
    *bp++ = ch;
 
773
    mBufferPos++;
 
774
  }
 
775
 
 
776
  *aWordLen = mBufferPos - prevBufferPos;
 
777
  return cp - ((const unsigned char*)frag->Get1b());
 
778
}
 
779
 
 
780
//----------------------------------------
 
781
 
 
782
static void
 
783
AsciiToLowerCase(unsigned char* aText, PRInt32 aWordLen)
 
784
{
 
785
  while (aWordLen-- > 0) {
 
786
    *aText = tolower(*aText);
 
787
    aText++;
 
788
  }
 
789
}
 
790
 
 
791
static void
 
792
AsciiToUpperCase(unsigned char* aText, PRInt32 aWordLen)
 
793
{
 
794
  while (aWordLen-- > 0) {
 
795
    *aText = toupper(*aText);
 
796
    aText++;
 
797
  }
 
798
}
 
799
 
 
800
#define kSzlig 0x00DF
 
801
static PRInt32 CountGermanSzlig(const PRUnichar* aText, PRInt32 len)
 
802
{
 
803
  PRInt32 i,cnt;
 
804
  for(i=0,cnt=0; i<len; i++, aText++)
 
805
  {
 
806
     if(kSzlig == *aText)
 
807
         cnt++;
 
808
  }
 
809
  return cnt;
 
810
}
 
811
static void ReplaceGermanSzligToSS(PRUnichar* aText, PRInt32 len, PRInt32 szCnt)
 
812
{
 
813
  PRUnichar *src, *dest;
 
814
  src = aText + len - 1;
 
815
  dest = src + szCnt;
 
816
  while( (src!=dest) && (src >= aText) )
 
817
  {
 
818
      if(kSzlig == *src )
 
819
      {     
 
820
        *dest-- = PRUnichar('S');
 
821
        *dest-- = PRUnichar('S');
 
822
        src--;
 
823
      } else {
 
824
        *dest-- = *src--;
 
825
      }
 
826
  }
 
827
}
 
828
 
 
829
void
 
830
nsTextTransformer::LanguageSpecificTransform(PRUnichar* aText, PRInt32 aLen,
 
831
                                             PRBool* aWasTransformed)
 
832
{
 
833
  if (mLanguageSpecificTransformType ==
 
834
      eLanguageSpecificTransformType_Japanese) {
 
835
    for (PRInt32 i = 0; i < aLen; i++) {
 
836
      if (aText[i] == 0x5C) { // BACKSLASH
 
837
        aText[i] = 0xA5; // YEN SIGN
 
838
        SetHasMultibyte(PR_TRUE);        
 
839
        *aWasTransformed = PR_TRUE;
 
840
      }
 
841
#if 0
 
842
      /*
 
843
       * We considered doing this, but since some systems may not have fonts
 
844
       * with this OVERLINE glyph, we decided not to do this.
 
845
       */
 
846
      else if (aText[i] == 0x7E) { // TILDE
 
847
        aText[i] = 0x203E; // OVERLINE
 
848
        SetHasMultibyte(PR_TRUE);        
 
849
        *aWasTransformed = PR_TRUE;
 
850
      }
 
851
#endif
 
852
    }
 
853
  }
 
854
  /* we once do transformation for Korean, but later decide to remove it */
 
855
  /* see bug 88050 for more information */
 
856
}
 
857
 
 
858
PRUnichar*
 
859
nsTextTransformer::GetNextWord(PRBool aInWord,
 
860
                               PRInt32* aWordLenResult,
 
861
                               PRInt32* aContentLenResult,
 
862
                               PRBool* aIsWhiteSpaceResult,
 
863
                               PRBool* aWasTransformed,
 
864
                               PRBool aResetTransformBuf,
 
865
                               PRBool aForLineBreak,
 
866
                               PRBool aIsKeyboardSelect)
 
867
{
 
868
  const nsTextFragment* frag = mFrag;
 
869
  PRInt32 fragLen = frag->GetLength();
 
870
#ifdef IBMBIDI
 
871
  if (*aWordLenResult > 0 && *aWordLenResult < fragLen) {
 
872
    fragLen = *aWordLenResult;
 
873
  }
 
874
#endif
 
875
  PRInt32 offset = mOffset;
 
876
  PRInt32 wordLen = 0;
 
877
  PRBool isWhitespace = PR_FALSE;
 
878
  PRUnichar* result = nsnull;
 
879
  PRBool prevBufferPos;
 
880
  PRBool skippedWhitespace = PR_FALSE;
 
881
 
 
882
  // Initialize OUT parameter
 
883
  *aWasTransformed = PR_FALSE;
 
884
 
 
885
  // See if we should reset the current buffer position back to the
 
886
  // beginning of the buffer
 
887
  if (aResetTransformBuf) {
 
888
    mBufferPos = 0;
 
889
    SetTransformedTextIsAscii(LeaveAsAscii());
 
890
  }
 
891
  prevBufferPos = mBufferPos;
 
892
 
 
893
  // Fix word breaking problem w/ PREFORMAT and PREWRAP
 
894
  // for word breaking, we should really go to the normal code
 
895
  if((! aForLineBreak) && (eNormal != mMode))
 
896
     mMode = eNormal;
 
897
 
 
898
  while (offset < fragLen) {
 
899
    PRUnichar firstChar = frag->CharAt(offset);
 
900
 
 
901
    // Eat up any discarded characters before dispatching
 
902
    if (IS_DISCARDED(firstChar)) {
 
903
      offset++;
 
904
      continue;
 
905
    }
 
906
 
 
907
    switch (mMode) {
 
908
      default:
 
909
      case eNormal:
 
910
        if (XP_IS_SPACE(firstChar)) {
 
911
          offset = ScanNormalWhiteSpace_F();
 
912
 
 
913
          // if this is just a '\n', and characters before and after it are CJK chars, 
 
914
          // we will skip this one.
 
915
          if (firstChar == '\n' && 
 
916
              offset - mOffset == 1 && 
 
917
              mOffset > 0 &&
 
918
              offset < fragLen) 
 
919
          {
 
920
            PRUnichar lastChar = frag->CharAt(mOffset - 1);
 
921
            PRUnichar nextChar = frag->CharAt(offset);
 
922
            if (IS_CJ_CHAR(lastChar) && IS_CJ_CHAR(nextChar)) {
 
923
              skippedWhitespace = PR_TRUE;
 
924
              --mBufferPos;
 
925
              mOffset = offset;
 
926
              continue;            }
 
927
          }
 
928
          if (firstChar != ' ') {
 
929
            *aWasTransformed = PR_TRUE;
 
930
          }
 
931
          wordLen = 1;
 
932
          isWhitespace = PR_TRUE;
 
933
        }
 
934
        else if (CH_NBSP == firstChar && !aForLineBreak) {
 
935
          wordLen = 1;
 
936
          isWhitespace = PR_TRUE;
 
937
          *aWasTransformed = PR_TRUE;
 
938
 
 
939
          // Make sure we have enough room in the transform buffer
 
940
          if (mBufferPos >= mTransformBuf.mBufferLen) {
 
941
             mTransformBuf.GrowBy(128);
 
942
          }
 
943
 
 
944
          offset++;
 
945
          if (TransformedTextIsAscii()) {
 
946
            ((unsigned char*)mTransformBuf.mBuffer)[mBufferPos++] = ' ';
 
947
          } else {
 
948
            mTransformBuf.mBuffer[mBufferPos++] = PRUnichar(' ');
 
949
          }
 
950
        }
 
951
        else if (frag->Is2b()) {
 
952
#ifdef IBMBIDI
 
953
          wordLen = *aWordLenResult;
 
954
#endif
 
955
          offset = ScanNormalUnicodeText_F(aForLineBreak, &wordLen, aWasTransformed);
 
956
        }
 
957
        else {
 
958
          if (!aForLineBreak)
 
959
            offset = ScanNormalAsciiText_F_ForWordBreak(&wordLen, 
 
960
                                                        aWasTransformed, 
 
961
                                                        aIsKeyboardSelect);
 
962
          else
 
963
            offset = ScanNormalAsciiText_F(&wordLen, aWasTransformed);
 
964
        }
 
965
        break;
 
966
 
 
967
      case ePreformatted:
 
968
        if (('\n' == firstChar) || ('\t' == firstChar)) {
 
969
          mTransformBuf.mBuffer[mBufferPos++] = firstChar;
 
970
          offset++;
 
971
          wordLen = 1;
 
972
          isWhitespace = PR_TRUE;
 
973
        }
 
974
        else if (frag->Is2b()) {
 
975
          offset = ScanPreData_F(&wordLen, aWasTransformed);
 
976
        }
 
977
        else {
 
978
          offset = ScanPreAsciiData_F(&wordLen, aWasTransformed);
 
979
        }
 
980
        break;
 
981
 
 
982
      case ePreWrap:
 
983
        if (XP_IS_SPACE(firstChar)) {
 
984
          if (('\n' == firstChar) || ('\t' == firstChar)) {
 
985
            mTransformBuf.mBuffer[mBufferPos++] = firstChar;
 
986
            offset++;
 
987
            wordLen = 1;
 
988
          }
 
989
          else {
 
990
            offset = ScanPreWrapWhiteSpace_F(&wordLen);
 
991
          }
 
992
          isWhitespace = PR_TRUE;
 
993
        }
 
994
        else if (frag->Is2b()) {
 
995
#ifdef IBMBIDI
 
996
          wordLen = *aWordLenResult;
 
997
#endif
 
998
          offset = ScanNormalUnicodeText_F(aForLineBreak, &wordLen, aWasTransformed);
 
999
        }
 
1000
        else {
 
1001
          if (!aForLineBreak)
 
1002
            offset = ScanNormalAsciiText_F_ForWordBreak(&wordLen, aWasTransformed, 
 
1003
                                                        aIsKeyboardSelect);
 
1004
          else
 
1005
            offset = ScanNormalAsciiText_F(&wordLen, aWasTransformed);
 
1006
        }
 
1007
        break;
 
1008
    }
 
1009
 
 
1010
    if (TransformedTextIsAscii()) {
 
1011
      unsigned char* wordPtr = (unsigned char*)mTransformBuf.mBuffer + prevBufferPos;
 
1012
      
 
1013
      if (!isWhitespace) {
 
1014
        switch (mTextTransform) {
 
1015
        case NS_STYLE_TEXT_TRANSFORM_CAPITALIZE:
 
1016
          *wordPtr = toupper(*wordPtr);
 
1017
          break;
 
1018
        case NS_STYLE_TEXT_TRANSFORM_LOWERCASE:
 
1019
          AsciiToLowerCase(wordPtr, wordLen);
 
1020
          break;
 
1021
        case NS_STYLE_TEXT_TRANSFORM_UPPERCASE:
 
1022
          AsciiToUpperCase(wordPtr, wordLen);
 
1023
          break;
 
1024
        }
 
1025
        NS_ASSERTION(mLanguageSpecificTransformType ==
 
1026
                     eLanguageSpecificTransformType_None,
 
1027
                     "should not be ASCII for language specific transforms");
 
1028
      }
 
1029
      result = (PRUnichar*)wordPtr;
 
1030
 
 
1031
    } else {
 
1032
      result = &mTransformBuf.mBuffer[prevBufferPos];
 
1033
 
 
1034
      if (!isWhitespace) {
 
1035
        switch (mTextTransform) {
 
1036
        case NS_STYLE_TEXT_TRANSFORM_CAPITALIZE:
 
1037
          if(NS_SUCCEEDED(EnsureCaseConv()))
 
1038
            gCaseConv->ToTitle(result, result, wordLen, !aInWord);
 
1039
          // if the first character is szlig
 
1040
          if(kSzlig == *result)
 
1041
          {
 
1042
              if ((prevBufferPos + wordLen + 1) >= mTransformBuf.mBufferLen) {
 
1043
                mTransformBuf.GrowBy(128);
 
1044
                result = &mTransformBuf.mBuffer[prevBufferPos];
 
1045
              }
 
1046
              PRUnichar* src = result +  wordLen;
 
1047
              while(src>result) 
 
1048
              {
 
1049
                  *(src+1) = *src;
 
1050
                  src--;
 
1051
              }
 
1052
              result[0] = PRUnichar('S');
 
1053
              result[1] = PRUnichar('S');
 
1054
              wordLen++;
 
1055
          }
 
1056
          break;
 
1057
        case NS_STYLE_TEXT_TRANSFORM_LOWERCASE:
 
1058
          if(NS_SUCCEEDED(EnsureCaseConv()))
 
1059
            gCaseConv->ToLower(result, result, wordLen);
 
1060
          break;
 
1061
        case NS_STYLE_TEXT_TRANSFORM_UPPERCASE:
 
1062
          {
 
1063
            if(NS_SUCCEEDED(EnsureCaseConv()))
 
1064
              gCaseConv->ToUpper(result, result, wordLen);
 
1065
 
 
1066
            // first we search for German Szlig
 
1067
            PRInt32 szligCnt = CountGermanSzlig(result, wordLen);
 
1068
            if(szligCnt > 0) {
 
1069
              // Make sure we have enough room in the transform buffer
 
1070
              if ((prevBufferPos + wordLen + szligCnt) >= mTransformBuf.mBufferLen) 
 
1071
              {
 
1072
                mTransformBuf.GrowBy(128);
 
1073
                result = &mTransformBuf.mBuffer[prevBufferPos];
 
1074
              }
 
1075
              ReplaceGermanSzligToSS(result, wordLen, szligCnt);
 
1076
              wordLen += szligCnt;
 
1077
            }
 
1078
          }
 
1079
          break;
 
1080
        }
 
1081
        if (mLanguageSpecificTransformType !=
 
1082
            eLanguageSpecificTransformType_None) {
 
1083
          LanguageSpecificTransform(result, wordLen, aWasTransformed);
 
1084
        }
 
1085
        if (NeedsArabicShaping()) {
 
1086
          DoArabicShaping(result, wordLen, aWasTransformed);
 
1087
        }
 
1088
        if (NeedsNumericShaping()) {
 
1089
          DoNumericShaping(result, wordLen, aWasTransformed);
 
1090
        }
 
1091
      }
 
1092
    }
 
1093
 
 
1094
    break;
 
1095
  }
 
1096
 
 
1097
  *aIsWhiteSpaceResult = isWhitespace;
 
1098
  *aWordLenResult = wordLen;
 
1099
  *aContentLenResult = offset - mOffset;
 
1100
 
 
1101
  // we need to adjust the length if a '\n' has been skip between CJK chars
 
1102
  *aContentLenResult += (skippedWhitespace ? 1 : 0);
 
1103
 
 
1104
  // If the word length doesn't match the content length then we transformed
 
1105
  // the text
 
1106
  if ((mTextTransform != NS_STYLE_TEXT_TRANSFORM_NONE) ||
 
1107
      (*aWordLenResult != *aContentLenResult)) {
 
1108
    *aWasTransformed = PR_TRUE;
 
1109
    mBufferPos = prevBufferPos + *aWordLenResult;
 
1110
  }
 
1111
 
 
1112
  mOffset = offset;
 
1113
 
 
1114
  NS_ASSERTION(mBufferPos == prevBufferPos + *aWordLenResult, "internal error");
 
1115
  return result;
 
1116
}
 
1117
 
 
1118
//----------------------------------------------------------------------
 
1119
 
 
1120
// wordlen==1, contentlen=newOffset-currentOffset, isWhitespace=t
 
1121
PRInt32
 
1122
nsTextTransformer::ScanNormalWhiteSpace_B()
 
1123
{
 
1124
  const nsTextFragment* frag = mFrag;
 
1125
  PRInt32 offset = mOffset;
 
1126
 
 
1127
  while (--offset >= 0) {
 
1128
    PRUnichar ch = frag->CharAt(offset);
 
1129
    if (!XP_IS_SPACE(ch)) {
 
1130
      // If character is not discardable then stop looping, otherwise
 
1131
      // let the discarded character collapse with the other spaces.
 
1132
      if (!IS_DISCARDED(ch)) {
 
1133
        break;
 
1134
      }
 
1135
    }
 
1136
  }
 
1137
 
 
1138
  mTransformBuf.mBuffer[mTransformBuf.mBufferLen - 1] = ' ';
 
1139
  return offset;
 
1140
}
 
1141
 
 
1142
// wordlen==*aWordLen, contentlen=newOffset-currentOffset, isWhitespace=f
 
1143
PRInt32
 
1144
nsTextTransformer::ScanNormalAsciiText_B(PRInt32* aWordLen, PRBool aIsKeyboardSelect)
 
1145
{
 
1146
  const nsTextFragment* frag = mFrag;
 
1147
  PRInt32 offset = mOffset;
 
1148
  PRUnichar* bp = mTransformBuf.GetBufferEnd();
 
1149
  PRUnichar* startbp = mTransformBuf.GetBuffer();
 
1150
 
 
1151
  PRUnichar ch = frag->CharAt(offset - 1);
 
1152
  // Treat high bit chars as alphanumeric, otherwise we get stuck on accented letters
 
1153
  // We can't trust isalnum() results for isalnum()
 
1154
  // Therefore we don't stop at non-ascii (high bit) punctuation,
 
1155
  // which is just fine. The punctuation we care about is low bit.
 
1156
  PRBool readingAlphaNumeric = isalnum(ch) || !IS_ASCII_CHAR(ch);
 
1157
 
 
1158
  while (--offset >= 0) {
 
1159
    PRUnichar ch = frag->CharAt(offset);
 
1160
    if (CH_NBSP == ch) {
 
1161
      ch = ' ';
 
1162
    }
 
1163
    if (XP_IS_SPACE(ch)) {
 
1164
      break;
 
1165
    }
 
1166
    else if (IS_DISCARDED(ch)) {
 
1167
      continue;
 
1168
    } 
 
1169
    else if (sWordSelectStopAtPunctuation && readingAlphaNumeric && 
 
1170
             !isalnum(ch) && IS_ASCII_CHAR(ch)) {
 
1171
      // Break on ascii punctuation
 
1172
      break;
 
1173
    }
 
1174
    else if (sWordSelectStopAtPunctuation && !readingAlphaNumeric &&
 
1175
             (isalnum(ch) || !IS_ASCII_CHAR(ch))) {
 
1176
      if (!aIsKeyboardSelect)
 
1177
        break;
 
1178
      readingAlphaNumeric = PR_TRUE;
 
1179
    }
 
1180
    
 
1181
    if (ch > MAX_UNIBYTE) SetHasMultibyte(PR_TRUE);
 
1182
    if (bp == startbp) {
 
1183
      PRInt32 oldLength = mTransformBuf.mBufferLen;
 
1184
      nsresult rv = mTransformBuf.GrowBy(1000);
 
1185
      if (NS_FAILED(rv)) {
 
1186
        // If we run out of space (unlikely) then just chop the input
 
1187
        break;
 
1188
      }
 
1189
      bp = mTransformBuf.GetBufferEnd() - oldLength;
 
1190
      startbp = mTransformBuf.GetBuffer();
 
1191
    }
 
1192
    *--bp = ch;
 
1193
  }
 
1194
 
 
1195
  *aWordLen = mTransformBuf.GetBufferEnd() - bp;
 
1196
  return offset;
 
1197
}
 
1198
 
 
1199
// wordlen==*aWordLen, contentlen=newOffset-currentOffset, isWhitespace=f
 
1200
PRInt32
 
1201
nsTextTransformer::ScanNormalUnicodeText_B(PRBool aForLineBreak,
 
1202
                                           PRInt32* aWordLen)
 
1203
{
 
1204
  const nsTextFragment* frag = mFrag;
 
1205
  const PRUnichar* cp0 = frag->Get2b();
 
1206
  PRInt32 offset = mOffset - 1;
 
1207
 
 
1208
  PRUnichar firstChar = frag->CharAt(offset);
 
1209
 
 
1210
#ifdef IBMBIDI
 
1211
  PRInt32 limit = (*aWordLen > 0) ? *aWordLen : 0;
 
1212
  
 
1213
  while (offset > limit && IS_BIDI_CONTROL(firstChar) ) {
 
1214
    firstChar = frag->CharAt(--offset);
 
1215
  }
 
1216
#endif
 
1217
 
 
1218
  mTransformBuf.mBuffer[mTransformBuf.mBufferLen - 1] = firstChar;
 
1219
  if (firstChar > MAX_UNIBYTE) SetHasMultibyte(PR_TRUE);
 
1220
 
 
1221
  PRInt32 numChars = 1;
 
1222
 
 
1223
#ifdef IBMBIDI
 
1224
  if (offset > limit) {
 
1225
#else
 
1226
  if (offset > 0) {
 
1227
#endif
 
1228
    const PRUnichar* cp = cp0 + offset;
 
1229
    PRBool breakBetween = PR_FALSE;
 
1230
    if (aForLineBreak) {
 
1231
      mLineBreaker->BreakInBetween(cp0, offset + 1,
 
1232
                                   mTransformBuf.GetBufferEnd()-1, 1,
 
1233
                                   &breakBetween);
 
1234
    }
 
1235
    else {
 
1236
      mWordBreaker->BreakInBetween(cp0, offset + 1,
 
1237
                                   mTransformBuf.GetBufferEnd()-1, 1,
 
1238
                                   &breakBetween);
 
1239
    }
 
1240
 
 
1241
    if (!breakBetween) {
 
1242
      // Find next position
 
1243
      PRBool tryPrevFrag;
 
1244
      PRUint32 prev;
 
1245
      if (aForLineBreak) {
 
1246
        mLineBreaker->Prev(cp0, offset, offset, &prev, &tryPrevFrag);
 
1247
      }
 
1248
      else {
 
1249
        mWordBreaker->PrevWord(cp0, offset, offset, &prev, &tryPrevFrag);
 
1250
      }
 
1251
      numChars = (PRInt32) ((PRUint32) offset - prev) + 1;
 
1252
 
 
1253
      // Grow buffer before copying
 
1254
      nsresult rv = mTransformBuf.GrowTo(numChars);
 
1255
      if (NS_FAILED(rv)) {
 
1256
        numChars = mTransformBuf.GetBufferLength();
 
1257
      }
 
1258
 
 
1259
      // 1. convert nbsp into space
 
1260
      // 2. check mHasMultibyte flag
 
1261
      // 3. copy buffer
 
1262
      PRUnichar* bp = mTransformBuf.GetBufferEnd() - 1;
 
1263
      const PRUnichar* end = cp - numChars + 1;
 
1264
      while (cp > end) {
 
1265
        PRUnichar ch = *--cp;
 
1266
        if (CH_NBSP == ch) {
 
1267
          ch = ' ';
 
1268
        }
 
1269
        else if (IS_DISCARDED(ch)) {
 
1270
          continue;
 
1271
        }
 
1272
        if (ch > MAX_UNIBYTE) SetHasMultibyte(PR_TRUE);
 
1273
        *--bp = ch;
 
1274
      }
 
1275
 
 
1276
      // Recompute offset and numChars in case we stripped something
 
1277
      offset = offset - numChars;
 
1278
      numChars =  mTransformBuf.GetBufferEnd() - bp;
 
1279
    }
 
1280
  }
 
1281
  else 
 
1282
          offset--;
 
1283
 
 
1284
  *aWordLen = numChars;
 
1285
  return offset;
 
1286
}
 
1287
 
 
1288
// wordlen==*aWordLen, contentlen=newOffset-currentOffset, isWhitespace=t
 
1289
PRInt32
 
1290
nsTextTransformer::ScanPreWrapWhiteSpace_B(PRInt32* aWordLen)
 
1291
{
 
1292
  const nsTextFragment* frag = mFrag;
 
1293
  PRInt32 offset = mOffset;
 
1294
  PRUnichar* bp = mTransformBuf.GetBufferEnd();
 
1295
  PRUnichar* startbp = mTransformBuf.GetBuffer();
 
1296
 
 
1297
  while (--offset >= 0) {
 
1298
    PRUnichar ch = frag->CharAt(offset);
 
1299
    if (!XP_IS_SPACE(ch) || (ch == '\t') || (ch == '\n')) {
 
1300
      // Keep looping if this is a discarded character
 
1301
      if (IS_DISCARDED(ch)) {
 
1302
        continue;
 
1303
      }
 
1304
      break;
 
1305
    }
 
1306
    if (bp == startbp) {
 
1307
      PRInt32 oldLength = mTransformBuf.mBufferLen;
 
1308
      nsresult rv = mTransformBuf.GrowBy(1000);
 
1309
      if (NS_FAILED(rv)) {
 
1310
        // If we run out of space (unlikely) then just chop the input
 
1311
        break;
 
1312
      }
 
1313
      bp = mTransformBuf.GetBufferEnd() - oldLength;
 
1314
      startbp = mTransformBuf.GetBuffer();
 
1315
    }
 
1316
    *--bp = ' ';
 
1317
  }
 
1318
 
 
1319
  *aWordLen = mTransformBuf.GetBufferEnd() - bp;
 
1320
  return offset;
 
1321
}
 
1322
 
 
1323
// wordlen==*aWordLen, contentlen=newOffset-currentOffset, isWhitespace=f
 
1324
PRInt32
 
1325
nsTextTransformer::ScanPreData_B(PRInt32* aWordLen)
 
1326
{
 
1327
  const nsTextFragment* frag = mFrag;
 
1328
  PRInt32 offset = mOffset;
 
1329
  PRUnichar* bp = mTransformBuf.GetBufferEnd();
 
1330
  PRUnichar* startbp = mTransformBuf.GetBuffer();
 
1331
 
 
1332
  while (--offset >= 0) {
 
1333
    PRUnichar ch = frag->CharAt(offset);
 
1334
    if ((ch == '\t') || (ch == '\n')) {
 
1335
      break;
 
1336
    }
 
1337
    if (CH_NBSP == ch) {
 
1338
      ch = ' ';
 
1339
    }
 
1340
    else if (IS_DISCARDED(ch)) {
 
1341
      continue;
 
1342
    }
 
1343
    if (ch > MAX_UNIBYTE) SetHasMultibyte(PR_TRUE);
 
1344
    if (bp == startbp) {
 
1345
      PRInt32 oldLength = mTransformBuf.mBufferLen;
 
1346
      nsresult rv = mTransformBuf.GrowBy(1000);
 
1347
      if (NS_FAILED(rv)) {
 
1348
        // If we run out of space (unlikely) then just chop the input
 
1349
        offset++;
 
1350
        break;
 
1351
      }
 
1352
      bp = mTransformBuf.GetBufferEnd() - oldLength;
 
1353
      startbp = mTransformBuf.GetBuffer();
 
1354
    }
 
1355
    *--bp = ch;
 
1356
  }
 
1357
 
 
1358
  *aWordLen = mTransformBuf.GetBufferEnd() - bp;
 
1359
  return offset;
 
1360
}
 
1361
 
 
1362
//----------------------------------------
 
1363
 
 
1364
PRUnichar*
 
1365
nsTextTransformer::GetPrevWord(PRBool aInWord,
 
1366
                               PRInt32* aWordLenResult,
 
1367
                               PRInt32* aContentLenResult,
 
1368
                               PRBool* aIsWhiteSpaceResult,
 
1369
                               PRBool aForLineBreak,
 
1370
                               PRBool aIsKeyboardSelect)
 
1371
{
 
1372
  const nsTextFragment* frag = mFrag;
 
1373
  PRInt32 offset = mOffset;
 
1374
  PRInt32 wordLen = 0;
 
1375
  PRBool isWhitespace = PR_FALSE;
 
1376
  PRUnichar* result = nsnull;
 
1377
 
 
1378
  // Fix word breaking problem w/ PREFORMAT and PREWRAP
 
1379
  // for word breaking, we should really go to the normal code
 
1380
  if((! aForLineBreak) && (eNormal != mMode))
 
1381
     mMode = eNormal;
 
1382
 
 
1383
#ifdef IBMBIDI
 
1384
  PRInt32 limit = (*aWordLenResult > 0) ? *aWordLenResult : 0;
 
1385
  while (--offset >= limit) {
 
1386
#else
 
1387
  while (--offset >= 0) {
 
1388
#endif
 
1389
    PRUnichar firstChar = frag->CharAt(offset);
 
1390
 
 
1391
    // Eat up any discarded characters before dispatching
 
1392
    if (IS_DISCARDED(firstChar)) {
 
1393
      continue;
 
1394
    }
 
1395
 
 
1396
    switch (mMode) {
 
1397
      default:
 
1398
      case eNormal:
 
1399
        if (XP_IS_SPACE(firstChar)) {
 
1400
          offset = ScanNormalWhiteSpace_B();
 
1401
          wordLen = 1;
 
1402
          isWhitespace = PR_TRUE;
 
1403
        }
 
1404
        else if (CH_NBSP == firstChar && !aForLineBreak) {
 
1405
          wordLen = 1;
 
1406
          isWhitespace = PR_TRUE;
 
1407
          mTransformBuf.mBuffer[mTransformBuf.mBufferLen - 1] = ' ';
 
1408
          offset--;
 
1409
        } else if (frag->Is2b()) {
 
1410
#ifdef IBMBIDI
 
1411
          wordLen = *aWordLenResult;
 
1412
#endif
 
1413
          offset = ScanNormalUnicodeText_B(aForLineBreak, &wordLen);
 
1414
        }
 
1415
        else {
 
1416
          offset = ScanNormalAsciiText_B(&wordLen, aIsKeyboardSelect);
 
1417
        }
 
1418
        break;
 
1419
 
 
1420
      case ePreformatted:
 
1421
        if (('\n' == firstChar) || ('\t' == firstChar)) {
 
1422
          mTransformBuf.mBuffer[mTransformBuf.mBufferLen-1] = firstChar;
 
1423
          offset--;  // make sure we overshoot
 
1424
          wordLen = 1;
 
1425
          isWhitespace = PR_TRUE;
 
1426
        }
 
1427
        else {
 
1428
          offset = ScanPreData_B(&wordLen);
 
1429
        }
 
1430
        break;
 
1431
 
 
1432
      case ePreWrap:
 
1433
        if (XP_IS_SPACE(firstChar)) {
 
1434
          if (('\n' == firstChar) || ('\t' == firstChar)) {
 
1435
            mTransformBuf.mBuffer[mTransformBuf.mBufferLen-1] = firstChar;
 
1436
            offset--;  // make sure we overshoot
 
1437
            wordLen = 1;
 
1438
          }
 
1439
          else {
 
1440
            offset = ScanPreWrapWhiteSpace_B(&wordLen);
 
1441
          }
 
1442
          isWhitespace = PR_TRUE;
 
1443
        }
 
1444
        else if (frag->Is2b()) {
 
1445
#ifdef IBMBIDI
 
1446
          wordLen = *aWordLenResult;
 
1447
#endif
 
1448
          offset = ScanNormalUnicodeText_B(aForLineBreak, &wordLen);
 
1449
        }
 
1450
        else {
 
1451
          offset = ScanNormalAsciiText_B(&wordLen, aIsKeyboardSelect);
 
1452
        }
 
1453
        break;
 
1454
    }
 
1455
 
 
1456
    // Backwards scanning routines *always* overshoot by one for the
 
1457
    // returned offset value.
 
1458
    offset = offset + 1;
 
1459
 
 
1460
    result = mTransformBuf.GetBufferEnd() - wordLen;
 
1461
 
 
1462
    if (!isWhitespace) {
 
1463
      switch (mTextTransform) {
 
1464
        case NS_STYLE_TEXT_TRANSFORM_CAPITALIZE:
 
1465
          if(NS_SUCCEEDED(EnsureCaseConv()))
 
1466
            gCaseConv->ToTitle(result, result, wordLen, !aInWord);
 
1467
          break;
 
1468
        case NS_STYLE_TEXT_TRANSFORM_LOWERCASE:
 
1469
          if(NS_SUCCEEDED(EnsureCaseConv()))
 
1470
            gCaseConv->ToLower(result, result, wordLen);
 
1471
          break;
 
1472
        case NS_STYLE_TEXT_TRANSFORM_UPPERCASE:
 
1473
          if(NS_SUCCEEDED(EnsureCaseConv()))
 
1474
            gCaseConv->ToUpper(result, result, wordLen);
 
1475
          break;
 
1476
      }
 
1477
    }
 
1478
    break;
 
1479
  }
 
1480
 
 
1481
  *aWordLenResult = wordLen;
 
1482
  *aContentLenResult = mOffset - offset;
 
1483
  *aIsWhiteSpaceResult = isWhitespace;
 
1484
 
 
1485
  mOffset = offset;
 
1486
  return result;
 
1487
}
 
1488
 
 
1489
void
 
1490
nsTextTransformer::DoArabicShaping(PRUnichar* aText, 
 
1491
                                   PRInt32& aTextLength, 
 
1492
                                   PRBool* aWasTransformed)
 
1493
{
 
1494
  if (aTextLength <= 0)
 
1495
    return;
 
1496
  
 
1497
  PRInt32 newLen;
 
1498
  PRBool isVisual = mPresContext->IsVisualMode();
 
1499
 
 
1500
  nsAutoString buf;
 
1501
  buf.SetLength(aTextLength);
 
1502
  PRUnichar* buffer = buf.BeginWriting();
 
1503
  
 
1504
  ArabicShaping(aText, buf.Length(), buffer, (PRUint32 *)&newLen, !isVisual, !isVisual);
 
1505
 
 
1506
  aTextLength = newLen;
 
1507
  *aWasTransformed = PR_TRUE;
 
1508
 
 
1509
  StripZeroWidthJoinControls(buffer, aText, aTextLength, aWasTransformed);
 
1510
}
 
1511
 
 
1512
void
 
1513
nsTextTransformer::DoNumericShaping(PRUnichar* aText, 
 
1514
                                    PRInt32& aTextLength, 
 
1515
                                    PRBool* aWasTransformed)
 
1516
{
 
1517
  if (aTextLength <= 0)
 
1518
    return;
 
1519
 
 
1520
  PRUint32 bidiOptions;
 
1521
  mPresContext->GetBidi(&bidiOptions);
 
1522
 
 
1523
  switch (GET_BIDI_OPTION_NUMERAL(bidiOptions)) {
 
1524
 
 
1525
    case IBMBIDI_NUMERAL_HINDI:
 
1526
      HandleNumbers(aText, aTextLength, IBMBIDI_NUMERAL_HINDI);
 
1527
      break;
 
1528
 
 
1529
    case IBMBIDI_NUMERAL_ARABIC:
 
1530
      HandleNumbers(aText, aTextLength, IBMBIDI_NUMERAL_ARABIC);
 
1531
      break;
 
1532
 
 
1533
    case IBMBIDI_NUMERAL_REGULAR:
 
1534
 
 
1535
      switch (mCharType) {
 
1536
 
 
1537
        case eCharType_EuropeanNumber:
 
1538
          HandleNumbers(aText, aTextLength, IBMBIDI_NUMERAL_ARABIC);
 
1539
          break;
 
1540
 
 
1541
        case eCharType_ArabicNumber:
 
1542
          HandleNumbers(aText, aTextLength, IBMBIDI_NUMERAL_HINDI);
 
1543
          break;
 
1544
 
 
1545
        default:
 
1546
          break;
 
1547
      }
 
1548
      break;
 
1549
 
 
1550
    case IBMBIDI_NUMERAL_HINDICONTEXT:
 
1551
      if (((GET_BIDI_OPTION_DIRECTION(bidiOptions)==IBMBIDI_TEXTDIRECTION_RTL) &&
 
1552
           (IS_ARABIC_DIGIT (aText[0]))) ||
 
1553
          (eCharType_ArabicNumber == mCharType))
 
1554
        HandleNumbers(aText, aTextLength, IBMBIDI_NUMERAL_HINDI);
 
1555
      else if (eCharType_EuropeanNumber == mCharType)
 
1556
        HandleNumbers(aText, aTextLength, IBMBIDI_NUMERAL_ARABIC);
 
1557
      break;
 
1558
 
 
1559
    case IBMBIDI_NUMERAL_NOMINAL:
 
1560
    default:
 
1561
      break;
 
1562
  }
 
1563
}
 
1564
 
 
1565
void
 
1566
nsTextTransformer::StripZeroWidthJoinControls(PRUnichar* aSource,
 
1567
                                              PRUnichar* aTarget,
 
1568
                                              PRInt32& aTextLength, 
 
1569
                                              PRBool* aWasTransformed)
 
1570
{
 
1571
  PRUnichar *src, *dest;
 
1572
  PRInt32 stripped = 0;
 
1573
 
 
1574
  src = aSource;
 
1575
  dest = aTarget;
 
1576
 
 
1577
  for (PRInt32 i = 0; i < aTextLength; ++i) {
 
1578
    while (*src == CH_ZWNJ || *src == CH_ZWJ) {
 
1579
      ++stripped;
 
1580
      ++src;
 
1581
      *aWasTransformed = PR_TRUE;
 
1582
    }
 
1583
    *dest++ = *src++;
 
1584
  }
 
1585
  aTextLength -= stripped;
 
1586
}
 
1587
 
 
1588
//----------------------------------------------------------------------
 
1589
// Self test logic for this class. This will (hopefully) make sure
 
1590
// that the forward and backward word iterator methods continue to
 
1591
// function as people change things...
 
1592
 
 
1593
#ifdef DEBUG
 
1594
struct SelfTestSection {
 
1595
  int length;
 
1596
  int* data;
 
1597
};
 
1598
 
 
1599
#define NUM_MODES 3
 
1600
 
 
1601
struct SelfTestData {
 
1602
  const PRUnichar* text;
 
1603
  SelfTestSection modes[NUM_MODES];
 
1604
};
 
1605
 
 
1606
static PRUint8 preModeValue[NUM_MODES] = {
 
1607
  NS_STYLE_WHITESPACE_NORMAL,
 
1608
  NS_STYLE_WHITESPACE_PRE,
 
1609
  NS_STYLE_WHITESPACE_MOZ_PRE_WRAP
 
1610
};
 
1611
 
 
1612
static PRUnichar test1text[] = {
 
1613
  'o', 'n', 'c', 'e', ' ', 'u', 'p', 'o', 'n', '\t',
 
1614
  'a', ' ', 's', 'h', 'o', 'r', 't', ' ', 't', 'i', 'm', 'e', 0
 
1615
};
 
1616
static int test1Results[] = { 4, 1, 4, 1, 1, 1, 5, 1, 4 };
 
1617
static int test1PreResults[] = { 9, 1, 12 };
 
1618
static int test1PreWrapResults[] = { 4, 1, 4, 1, 1, 1, 5, 1, 4 };
 
1619
 
 
1620
static PRUnichar test2text[] = {
 
1621
  0xF6, 'n', 'c', 'e', ' ', 0xFB, 'p', 'o', 'n', '\t',
 
1622
  0xE3, ' ', 's', 'h', 0xF3, 'r', 't', ' ', 't', 0xEE, 'm', 'e', ' ', 0
 
1623
};
 
1624
static int test2Results[] = { 4, 1, 4, 1, 1, 1, 5, 1, 4, 1 };
 
1625
static int test2PreResults[] = { 9, 1, 13 };
 
1626
static int test2PreWrapResults[] = { 4, 1, 4, 1, 1, 1, 5, 1, 4, 1 };
 
1627
 
 
1628
static PRUnichar test3text[] = {
 
1629
  0x0152, 'n', 'c', 'e', ' ', 'x', 'y', '\t', 'z', 'y', ' ', 0
 
1630
};
 
1631
static int test3Results[] = { 4, 1, 2, 1, 2, 1, };
 
1632
static int test3PreResults[] = { 7, 1, 3, };
 
1633
static int test3PreWrapResults[] = { 4, 1, 2, 1, 2, 1, };
 
1634
 
 
1635
static PRUnichar test4text[] = {
 
1636
  'o', 'n', CH_SHY, 'c', 'e', ' ', CH_SHY, ' ', 'u', 'p', 'o', 'n', '\t',
 
1637
  'a', ' ', 's', 'h', 'o', 'r', 't', ' ', 't', 'i', 'm', 'e', 0
 
1638
};
 
1639
static int test4Results[] = { 4, 1, 4, 1, 1, 1, 5, 1, 4 };
 
1640
static int test4PreResults[] = { 10, 1, 12 };
 
1641
static int test4PreWrapResults[] = { 4, 2, 4, 1, 1, 1, 5, 1, 4 };
 
1642
 
 
1643
static PRUnichar test5text[] = {
 
1644
  CH_SHY, 0
 
1645
};
 
1646
static int test5Results[] = { 0 };
 
1647
static int test5PreResults[] = { 0 };
 
1648
static int test5PreWrapResults[] = { 0 };
 
1649
 
 
1650
#if 0
 
1651
static PRUnichar test6text[] = {
 
1652
  0x30d5, 0x30b8, 0x30c6, 0x30ec, 0x30d3, 0x306e, 0x97f3, 0x697d,
 
1653
  0x756a, 0x7d44, 0x300c, 'H', 'E', 'Y', '!', ' ', 'H', 'E', 'Y', '!',
 
1654
  '\t', 'H', 'E', 'Y', '!', 0x300d, 0x306e, 0x30db, 0x30fc, 0x30e0,
 
1655
  0x30da, 0x30fc, 0x30b8, 0x3002, 0
 
1656
};
 
1657
static int test6Results[] = { 1, 1, 1, 1, 1,
 
1658
                              1, 1, 1, 1, 1,
 
1659
                              5, 1, 4, 1, 5,
 
1660
                              1, 2, 1, 2, 2 };
 
1661
static int test6PreResults[] = { 20, 1, 13 };
 
1662
static int test6PreWrapResults[] = { 1, 1, 1, 1, 1,
 
1663
                                     1, 1, 1, 1, 1,
 
1664
                                     5, 1, 4, 1, 5,
 
1665
                                     1, 2, 1, 2, 2 };
 
1666
#endif
 
1667
 
 
1668
static SelfTestData tests[] = {
 
1669
  { test1text,
 
1670
    { { sizeof(test1Results)/sizeof(int), test1Results, },
 
1671
      { sizeof(test1PreResults)/sizeof(int), test1PreResults, },
 
1672
      { sizeof(test1PreWrapResults)/sizeof(int), test1PreWrapResults, } }
 
1673
  },
 
1674
  { test2text,
 
1675
    { { sizeof(test2Results)/sizeof(int), test2Results, },
 
1676
      { sizeof(test2PreResults)/sizeof(int), test2PreResults, },
 
1677
      { sizeof(test2PreWrapResults)/sizeof(int), test2PreWrapResults, } }
 
1678
  },
 
1679
  { test3text,
 
1680
    { { sizeof(test3Results)/sizeof(int), test3Results, },
 
1681
      { sizeof(test3PreResults)/sizeof(int), test3PreResults, },
 
1682
      { sizeof(test3PreWrapResults)/sizeof(int), test3PreWrapResults, } }
 
1683
  },
 
1684
  { test4text,
 
1685
    { { sizeof(test4Results)/sizeof(int), test4Results, },
 
1686
      { sizeof(test4PreResults)/sizeof(int), test4PreResults, },
 
1687
      { sizeof(test4PreWrapResults)/sizeof(int), test4PreWrapResults, } }
 
1688
  },
 
1689
  { test5text,
 
1690
    { { sizeof(test5Results)/sizeof(int), test5Results, },
 
1691
      { sizeof(test5PreResults)/sizeof(int), test5PreResults, },
 
1692
      { sizeof(test5PreWrapResults)/sizeof(int), test5PreWrapResults, } }
 
1693
  },
 
1694
#if 0
 
1695
  { test6text,
 
1696
    { { sizeof(test6Results)/sizeof(int), test6Results, },
 
1697
      { sizeof(test6PreResults)/sizeof(int), test6PreResults, },
 
1698
      { sizeof(test6PreWrapResults)/sizeof(int), test6PreWrapResults, } }
 
1699
  },
 
1700
#endif
 
1701
};
 
1702
 
 
1703
#define NUM_TESTS (sizeof(tests) / sizeof(tests[0]))
 
1704
 
 
1705
void
 
1706
nsTextTransformer::SelfTest(nsILineBreaker* aLineBreaker,
 
1707
                            nsIWordBreaker* aWordBreaker,
 
1708
                            nsIPresContext* aPresContext)
 
1709
{
 
1710
  PRBool gNoisy = PR_FALSE;
 
1711
  if (PR_GetEnv("GECKO_TEXT_TRANSFORMER_NOISY_SELF_TEST")) {
 
1712
    gNoisy = PR_TRUE;
 
1713
  }
 
1714
 
 
1715
  PRBool error = PR_FALSE;
 
1716
  PRInt32 testNum = 0;
 
1717
  SelfTestData* st = tests;
 
1718
  SelfTestData* last = st + NUM_TESTS;
 
1719
  for (; st < last; st++) {
 
1720
    PRUnichar* bp;
 
1721
    PRInt32 wordLen, contentLen;
 
1722
    PRBool ws, transformed;
 
1723
 
 
1724
    PRBool isAsciiTest = PR_TRUE;
 
1725
    const PRUnichar* cp = st->text;
 
1726
    while (*cp) {
 
1727
      if (*cp > 255) {
 
1728
        isAsciiTest = PR_FALSE;
 
1729
        break;
 
1730
      }
 
1731
      cp++;
 
1732
    }
 
1733
 
 
1734
    nsTextFragment frag(st->text);
 
1735
    nsTextTransformer tx(aLineBreaker, aWordBreaker, aPresContext);
 
1736
 
 
1737
    for (PRInt32 preMode = 0; preMode < NUM_MODES; preMode++) {
 
1738
      // Do forwards test
 
1739
      if (gNoisy) {
 
1740
        nsAutoString uc2(st->text);
 
1741
        printf("%s forwards test: '", isAsciiTest ? "ascii" : "unicode");
 
1742
        fputs(NS_ConvertUCS2toUTF8(uc2).get(), stdout);
 
1743
        printf("'\n");
 
1744
      }
 
1745
      tx.Init2(&frag, 0, preModeValue[preMode], NS_STYLE_TEXT_TRANSFORM_NONE);
 
1746
 
 
1747
      int* expectedResults = st->modes[preMode].data;
 
1748
      int resultsLen = st->modes[preMode].length;
 
1749
 
 
1750
#ifdef IBMBIDI
 
1751
      wordLen = -1;
 
1752
#endif
 
1753
      while ((bp = tx.GetNextWord(PR_FALSE, &wordLen, &contentLen, &ws, &transformed))) {
 
1754
        if (gNoisy) {
 
1755
          nsAutoString tmp(bp, wordLen);
 
1756
          printf("  '");
 
1757
          fputs(NS_ConvertUCS2toUTF8(tmp).get(), stdout);
 
1758
          printf("': ws=%s wordLen=%d (%d) contentLen=%d (offset=%d)\n",
 
1759
                 ws ? "yes" : "no",
 
1760
                 wordLen, *expectedResults, contentLen, tx.mOffset);
 
1761
        }
 
1762
        if (*expectedResults != wordLen) {
 
1763
          error = PR_TRUE;
 
1764
          break;
 
1765
        }
 
1766
        expectedResults++;
 
1767
#ifdef IBMBIDI
 
1768
        wordLen = -1;
 
1769
#endif
 
1770
      }
 
1771
      if (expectedResults != st->modes[preMode].data + resultsLen) {
 
1772
        if (st->modes[preMode].data[0] != 0) {
 
1773
          error = PR_TRUE;
 
1774
        }
 
1775
      }
 
1776
 
 
1777
      // Do backwards test
 
1778
      if (gNoisy) {
 
1779
        nsAutoString uc2(st->text);
 
1780
        printf("%s backwards test: '", isAsciiTest ? "ascii" : "unicode");
 
1781
        fputs(NS_ConvertUCS2toUTF8(uc2).get(), stdout);
 
1782
        printf("'\n");
 
1783
      }
 
1784
      tx.Init2(&frag, frag.GetLength(), NS_STYLE_WHITESPACE_NORMAL,
 
1785
               NS_STYLE_TEXT_TRANSFORM_NONE);
 
1786
      expectedResults = st->modes[preMode].data + resultsLen;
 
1787
#ifdef IBMBIDI
 
1788
      wordLen = -1;
 
1789
#endif
 
1790
      while ((bp = tx.GetPrevWord(PR_FALSE, &wordLen, &contentLen, &ws))) {
 
1791
        --expectedResults;
 
1792
        if (gNoisy) {
 
1793
          nsAutoString tmp(bp, wordLen);
 
1794
          printf("  '");
 
1795
          fputs(NS_ConvertUCS2toUTF8(tmp).get(), stdout);
 
1796
          printf("': ws=%s wordLen=%d contentLen=%d (offset=%d)\n",
 
1797
                 ws ? "yes" : "no",
 
1798
                 wordLen, contentLen, tx.mOffset);
 
1799
        }
 
1800
        if (*expectedResults != wordLen) {
 
1801
          error = PR_TRUE;
 
1802
          break;
 
1803
        }
 
1804
#ifdef IBMBIDI
 
1805
        wordLen = -1;
 
1806
#endif
 
1807
      }
 
1808
      if (expectedResults != st->modes[preMode].data) {
 
1809
        if (st->modes[preMode].data[0] != 0) {
 
1810
          error = PR_TRUE;
 
1811
        }
 
1812
      }
 
1813
 
 
1814
      if (error) {
 
1815
        fprintf(stderr, "nsTextTransformer: self test %d failed\n", testNum);
 
1816
      }
 
1817
      else if (gNoisy) {
 
1818
        fprintf(stdout, "nsTextTransformer: self test %d succeeded\n", testNum);
 
1819
      }
 
1820
 
 
1821
      testNum++;
 
1822
    }
 
1823
  }
 
1824
  if (error) {
 
1825
    NS_ABORT();
 
1826
  }
 
1827
}
 
1828
 
 
1829
nsresult
 
1830
nsTextTransformer::Init2(const nsTextFragment* aFrag,
 
1831
                         PRInt32 aStartingOffset,
 
1832
                         PRUint8 aWhiteSpace,
 
1833
                         PRUint8 aTextTransform)
 
1834
{
 
1835
  mFrag = aFrag;
 
1836
 
 
1837
  // Sanitize aStartingOffset
 
1838
  if (aStartingOffset < 0) {
 
1839
    NS_WARNING("bad starting offset");
 
1840
    aStartingOffset = 0;
 
1841
  }
 
1842
  else if (aStartingOffset > mFrag->GetLength()) {
 
1843
    NS_WARNING("bad starting offset");
 
1844
    aStartingOffset = mFrag->GetLength();
 
1845
  }
 
1846
  mOffset = aStartingOffset;
 
1847
 
 
1848
  // Get the frames text style information
 
1849
  if (NS_STYLE_WHITESPACE_PRE == aWhiteSpace) {
 
1850
    mMode = ePreformatted;
 
1851
  }
 
1852
  else if (NS_STYLE_WHITESPACE_MOZ_PRE_WRAP == aWhiteSpace) {
 
1853
    mMode = ePreWrap;
 
1854
  }
 
1855
  mTextTransform = aTextTransform;
 
1856
 
 
1857
  return NS_OK;
 
1858
}
 
1859
#endif /* DEBUG */