Files
palemoon27/xpcom/string/nsReadableUtils.cpp
T
roytam1 7f8ba9c1d7 import changes from `dev' branch of rmottola/Arctic-Fox:
- Bug 1236786 - [WebGL2] pass getVertexAttrib in gl-object-get-calls.html, r=jgilbert (60a2c91a38)
- Bug 1233046 - Fix OES_texture_float on OSX. - r=jrmuizel (4bc0059f5f)
- Bug 1233557 - Allow RGB8 to be renderable again for web-compat. - r=jrmuizel (4c13bfd8e8)
- Bug 1233549. Disallow ES3 compressed texture formats. r=jgilbert (1073033161)
- Bug 1241702 - Allow unsized DEPTH_STENCIL for RBs in WebGL 2. - r=kamidphish (87d17d2cf9)
- Bug 1239126. Handle gl_InstanceID attribute with no location. r=jgilbert (4894997e98)
- Bug 1236782 - [WebGL2] pass getProgramParameter in gl-object-get-calls.html; r=jgilbert (2136fcce48)
- Bug 1232462. Only ask for a higher version of GLSL when using WebGL2. r=jgilbert (0317be4eb4)
- Bug 1242330 - "Four extensions were promoted to core in WebGL 2 and should no longer be available as extensions." r=jgilbert r=jmuizelaar (6df020b8d4)
- Bug 1233626 - Default MaxDrawingBuffers to 1 unless ext/webgl2. - r=jrmuizel (a7580d661c)
- Bug 1231657. Don't allow linking different versions shaders. r=jgilbert (e610f98066)
- Bug 1241777 - TexCompareFunc should be stored in ascending order. r=jgilbert (b6151a0076)
- Bug 1228885 - Implement WebGLTexture::MemoryUsage. - r=kamidphish (ea06815414)
- Bug 1239259 - Fix WebGL2 generateMipmap checking. r=jgilbert (39f587c421)
- Bug 1242347 - Allow unsized internal format when generate mipmap. r=jgilbert (b203a8898c)
- Bug 1232502. Use the correct internalFormat when calling CopyTexImage2D. r=jgilbert (eeaef3215e)
- Bug 1243663 - Max uniform and attribute location lengths in WebGL2 should be 1024. r=jgilbert (c4ec6de507)
- Bug 1239488 - Add int/uint to vertex attrib data type. r=jgilbert (11b4968025)
- Bug 1184242 - Remove aTabParent != sActiveTabParent warning from IMEStateManager::SetInputContextForChildProcess. r=masayuki (0fcda10e15)
- Bug 1178652 - Send NOTIFY_IME_OF_COMPOSITION_UPDATE to parent process correctly. r=masayuki (bce28e2c91)
- Bug 1107782 - Only accept certain mouse, gamepad events as user-active. r=smaug (00542c80b9)
- Bug 1247850 - Shrink NameTableKey in nsStaticCaseInsensitiveNameTable. r=froydnj,erahm. (ce3cb3edfb)
- Bug 1247359 - micro-optimize the common case of String{Begins,End}With; r=erahm (333e042b31)
- Bug 1239125. Add operator!=(char_type*) to nsTSubstring. r=froydnj (0cc047a9a1)
- Bug 1213862 - Align nsString whitespace handling with web specs; r=froydnj (db5b11ca52)
- Bug 1141884 - Trigger compositor smooth scrolling to snap points when APZ is enabled. r=mstange,kip (593af59f2a)
- Bug 1244582: Add back in a null check that was accidentally removed. r=smaug (76bff1b01f)
- Bug 1234176 - Introduce and use the WriteSysFile() helper function. r=dhylands (22a46fbe8b)
- missing bit of Bug 1198124 - Enable -Wshadow (f84535a7a2)
- Bug 1249171 - Simplify nsCOMArray::SizeOfExcludingThis(). r=erahm. (57efdce1c6)
- Bug 1156416 - Validate camera parameters supplied by the application. r=mikeh (f8b4b84ccf)
- Bug 1186808 - Replace nsBaseHashtable::EnumerateRead() calls in dom/camera/ with iterators. r=mikeh. (7b1db5f6a1)
- Bug 1158378 - Fix how a failed set configuration call would try to shutdown the camera after release. (9d5e323bca)
- Bug 1171374 - Permit software video codecs with the emulated camera. r=sotaro (c1ae26ea0d)
- Bug 1234458 P1 Allow the CacheChild to be "locked" into memory so it will delay destruction. r=ehsan a=ritu (9e46185779)
- Bug 1234458 P2 Lock the CacheChild actor while Cache DOM methods are running. r=ehsan a=ritu (038342a6e2)
- Bug 1244764 P1 Make Cache .add()/.addAll() fail if a Response.ok() is false. r=ehsan (ae26ca9ef1)
- Bug 1172562 - Clear QuotaManager storage when uninstalling an app. Test. r=bkelly (b07311a3b7)
- Bug 1172629 - Use the caches global property from an iframe loaded after setting the pref in order to make the tests pass with the pref disabled; r=bkelly a=RyanVM (e7c05d8b79)
- Bug 1244764 P2 Make dom/cache mochitests pass with new add()/addAll() behavior. r=ehsan (e1f667c1b4)
- Bug 1244764 P3 Make service worker tests pass with new Cache add()/addAll() behavior. r=ehsan (1518ae5225)
- Bug 1003860 - Simplify storage setup tasks in storage inspector tests. r=mratcliffe (249a8bdb2b)
- Bug 1003860 - Service worker cache for storage actor. r=mratcliffe (5c3d1ecd0c)
- Bug 1244764 P5 Fix devtools test to work with new Cache add()/addAll() behavior. r=ehsan (bf85405de8)
- Bug 1232901 - Use channel.asyncOpen2 within dom/browser-element/BrowserElementParent.js (r=sicking,aus) (2a228ed551)
- Bug 1180330 - http auth prompt shown when opening browser if prompt canceled/dismissed earlier. r=fabrice (ba3666f4bd)
- Bug 1234118 - Delete code for supporting 'do-command' and 'copypaste-docommand'. r=mtseng, r=smaug (b1b575d3c5)
- Bug 1238883 - [TV Browser] It shows "The page cannot be displayed" when user browse some webpages. r=roc (e6d7739dd6)
- Bug 1238440 - FileReader should throw an error when the blob changed size when reading, r=khuey (b006adba10)
- Bug 1230422 - FileReader should handle nested ReadAs*() calls. r=khuey (5a3ff84a31)
- Bug 1225202, part 3 - Create files in test_fileapi_slice.html using SpecialPowers.createFiles. r=baku (1137975548)
- Bug 1241171 - FormData should not force 'blob' as filename, r=smaug (748055f751)
- Bug 1246375 - Restore the previous spec version of FormData, r=smaug (3586af2b88)
- Bug 1237183 - Modify implementation of reading preference. r=seanlin (a132bc7246)
- Bug 801545 - Remove DocumentType.internalSubset, r=bz (ea30c9b5ee)
- Bug 1226440 - Expose a method to get a node's immediate dominator; r=bz,sfink (f77ae44037)
- Bug 825318 - Implement adoptDownload for mozDownloadManager, r=aus, r=sicking (e98cb05210)
- Bug 1237370 - Always log the reason for remote AppRep lookup failures. r=gcp (2c804e68fc)
- Bug 1167493 - Application Reputation: disable remote lookup of zip files on Mac/Linux, r=gcp (517459e064)
- Bug 1195519 - Use channel->ascynOpen2 toolkit/components/downloads/ApplicationReputation.cpp (r=sicking) (2856e5213a)
- Bug 1237856 - Add prefs to honor/ignore Application Reputation verdicts. r=gcp (54ee06264f)
- Bug 1243643 - Deprecate unsafe CPOW usage in contentAreaUtils' saveImage. r=jld (6ae790f1ef)
- Bug 1229224: Add an eslint plugin for importing all browser.js globals for browser-chrome tests. r=miker (9df52a7f3b)
- Bug 1245916: Add additional browser window scripts to eslint globals. r=felipe (92d316ca5e)
- Bug 1246244 - Allow non-CPOW documents to pass through saveImageURL properly. r=jaws,Margaret (c8d4ca241d)
- some missing bits after world fix (c0439eebb0)
- add some missing stuff (ddbd47dc03)
- bissing bit of 1229519 (4e255c3dae)
- Bug 1199662 - Crash ping environment block is broken when any string field contains a quotation mark. Unescape INI fields properly using the library that already exists for the purpose. r=ted (874a999edc)
- Bug 1216150 - Turn on the experimental Intl.DateTimeFormat.prototype.formatToParts in b2g certified apps. r=fabrice (40eeb1a4d4)
- Bug 1216150 - Mini-bustage fix for something I think I unintentionally qref'd into the final patch. r=bustage in a CLOSED TREE (36d9b21a67)
- Bug 1141311 - Add async mode support to GonkNativeWindow on Lollipop Gonk r=pchang (39d9d56326)
- Bug 1146671 - Ensure camera not already released when performing operations. r=dhylands (71b59caa1f)
- Bug 1248737. Improve documentation for WorkerRunnable and associated classes. r=khuey (4ff57790c5)
- Bug 1235629 - Remove dead code in WorkerFeature.h, r=smaug (75a51fcf03)
- Bug 1212333 - WorkerDebuggerManager should live on the main thread;r=khuey (11fdfbbae6)
- Bug 1226443 P3 Re-enable service worker update wpt tests. r=ehsan (605dac5f9e)
- Bug 1226443 P4 Cleanup ServiceWorkerScriptCache objects when initialization fails. r=ehsan (43de3429a2)
- Bug 1234127: Change |BluetoothAdapter.pairingReqs| as a nullable object; r=btian, r=mrbkap (45d2038f6a)
- Bug 1188487 - BrowserElement webidl changes for muting and setting volume. r=ehsan (21bea70a07)
- Bug 1238210 - Correct the Promise return types on two Clients methods; r=baku (fa41b25df0)
- Bug 1246784 - Expose Console to the WorkerDebuggerGlobalScope - part 2, r=khuey (0da9ce8ff6)
- Bug 1228702. Don't expose the 'location' property of Exception/DOMException on workers. r=bholley (0fe86ea586)
- Bug 1223825 - Change Directory.path to include the directory's name. r=baku (0cdae4c2f0)
- Bug 1238225 - Mark ExtendableMessageEvent.ports as SameObject; r=baku (45b9a9746f)
- Bug 1236933 - Return null from FetchEvent.clientId for non-subresource network requests; r=bkelly (4a9c4b40cb)
- Bug 1238213 - Make FetchEvent.request non-nullable; r=baku (751082c8ba)
- Bug 1193125 - Avoid corrupting image data in test_fetch_event.html. r=bkelly (9f6bff232f)
- Bug 1201664 - Avoid using Request's constructor when creating FetchEvent.request; r=bkelly (7a3401e345)
- Bug 1175944 - Packaged app's (app://) JS files are not loaded and do not trigger "onfetch" handler. r=jdm (62df139153)
- Bug 1233644 - use pattern matching when listening clear-origin-data. r=baku (ea2594f50e)
- Bug 1237363 - Part 1: Unregister all service workers registered in mochitests at the end of the test; r=jdm (5be97e5bb0)
- Bug 1237363 - Part 2: Fail mochitests which register a service worker without unregistering it; r=jdm (c4160ffd5f)
- Bug 1237363 - Part 3: Add a test for a mochitest finishing without unregistering its service worker; r=jdm (911d37291b)
- Bug 1174078 - Calling "fetch" inside Service Worker's "onfetch" handler in b2g causes "onfetch" again that leads to an infinite loop. Test. r=nsm (208451f346)
- Bug 1197379 - Remove support for intercepting app:// URIs using service workers; r=jdm (3cbdd725f1)
- Bug 1179399 - Part 1: Relax the ShouldIntercept checks when overriding JAR channel info; r=jdm (850bb2bdb8)
- Bug 1238213 follow-up: Mark the FetchEventInit dictionary argument to FetchEvent's constructor optional too; r=bzbarsky (356cbe6db7)
- Bug 1232732 - modify NS_WARNING in MOZ_WIN_MEM_TRY_CATCH; r=aklotz (e2be4d6919)
- Bug 1247658 - Expose a method to JS for find the shortest retaining paths of some nodes in a heap snapshot; r=bz r=jimb (2c82198808)
- Bug 1188115: Expose IDBCursorWithValue in workers. r=baku (e1c40aeb6e)
- Bug 1162680 - Notify Keyboard.jsm to send blur event when the message manager is closed first. r=timdream (53727ab300)
- Bug 1192986 Also mark Cache/CacheStorage as release interfaces on workers. r=ehsan a=bustage (25cf83c154)
- Bug 1159742. Get rid of the pref annotation from test_interfaces, since it basically corresponds to disabling the test. r=jst (c229e3f881)
- Bug 1203160 - Part 2: Fix the interfaces tests to allow SW interfaces for non-release Fennec; r=baku (072840db1f)
- Bug 1197700 - Correct mistakes in InputMethod.webidl. r=kanru, r=janjongboom, sr=smaug (4edb6f201f)
- Bug 1206970 - Stop expecting AnimationPlaybackEvent to be exposed on release branches, where it's disabled by pref, r=smaug (30ae2b13db)
- Bug 1177276 - Pref on canvas.captureStream by default. r=smaug,mt (0cfe0f72f2)
- Bug 1215147 - Enable VR API's on FF for Android by default. r=snorp, r=vlad, r=bz (5ff3725318)
- Bug 1218482 - Enable WebVR By Default,r=bz (f26111ed82)
- Bug 1159755. Stop forcing the media.eme.apiVisible preference to be true in our test harness. r=cpearce (09f7887917)
- Bug 1149312 - Obtain test coverage for the file-backed case of MediaRecorder. r=roc (bd2e7e40f0)
- Bug 1154559 - Remove flaky timeouts from manifest.js and register SimpleTest.registerCleanupFunction() to report unfinished tests. r=cpearce. (eb68db0fb2)
- Bug 1154564 - Add the ability to notify timeouts to MediaTestManager and remove flaky timeouts from test_playback.html. r=cpearce. (c89b4e58d9)
- Bug 1135170 - Fix up racey test_seek-1.html. rpending=mattwoodrow (b3a7d0dcd6)
- Bug 902686 - Change manifest.js to use SpecialPowers.pushPrefEnv. r=edwin (636b0edc1a)
- Bug 1183502 - give androidVersion a correct value in manifest.js. r=sotaro. (933e9ea712)
- Bug 1235588 - add null check to SimpleTest. r=bechen. (958ede68de)
- misspatch (c8922447ff)
- Bug 1151740 - pass the callback object as-is to SpecialPowers.exactGC(). r=edwin (99ca873bce)
- Bug 1197682 - InputMethodManager#setSupportsSwitchingTypes, r=janjongboom, sr=smaug (e7eb54e491)
- Bug 1201407 - Add input-manage-only events for InputMethod API. r=janjongboom, sr=smaug (776d064bd1)
- Bug 1234459 - Expose full text in the input box to InputMethod API, r=masayuki, sr=smaug (4fa0554356)
- Bug 1198163 - Workaround Mochitest app and assign frame proper permissions, r=kanru (c3bcf8ecc1)
- Bug 990250 - Fold nsIStyleSheet into CSSStyleSheet. r=dbaron (23579cb300)
2024-01-16 11:25:53 +08:00

1252 lines
32 KiB
C++

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsReadableUtils.h"
#include "mozilla/CheckedInt.h"
#include "nsMemory.h"
#include "nsString.h"
#include "nsTArray.h"
#include "nsUTF8Utils.h"
void
LossyCopyUTF16toASCII(const nsAString& aSource, nsACString& aDest)
{
aDest.Truncate();
LossyAppendUTF16toASCII(aSource, aDest);
}
void
CopyASCIItoUTF16(const nsACString& aSource, nsAString& aDest)
{
aDest.Truncate();
AppendASCIItoUTF16(aSource, aDest);
}
void
LossyCopyUTF16toASCII(const char16ptr_t aSource, nsACString& aDest)
{
aDest.Truncate();
if (aSource) {
LossyAppendUTF16toASCII(nsDependentString(aSource), aDest);
}
}
void
CopyASCIItoUTF16(const char* aSource, nsAString& aDest)
{
aDest.Truncate();
if (aSource) {
AppendASCIItoUTF16(nsDependentCString(aSource), aDest);
}
}
void
CopyUTF16toUTF8(const nsAString& aSource, nsACString& aDest)
{
if (!CopyUTF16toUTF8(aSource, aDest, mozilla::fallible)) {
// Note that this may wildly underestimate the allocation that failed, as
// we report the length of aSource as UTF-16 instead of UTF-8.
aDest.AllocFailed(aDest.Length() + aSource.Length());
}
}
bool
CopyUTF16toUTF8(const nsAString& aSource, nsACString& aDest,
const mozilla::fallible_t& aFallible)
{
aDest.Truncate();
if (!AppendUTF16toUTF8(aSource, aDest, aFallible)) {
return false;
}
return true;
}
void
CopyUTF8toUTF16(const nsACString& aSource, nsAString& aDest)
{
aDest.Truncate();
AppendUTF8toUTF16(aSource, aDest);
}
void
CopyUTF16toUTF8(const char16ptr_t aSource, nsACString& aDest)
{
aDest.Truncate();
AppendUTF16toUTF8(aSource, aDest);
}
void
CopyUTF8toUTF16(const char* aSource, nsAString& aDest)
{
aDest.Truncate();
AppendUTF8toUTF16(aSource, aDest);
}
void
LossyAppendUTF16toASCII(const nsAString& aSource, nsACString& aDest)
{
uint32_t old_dest_length = aDest.Length();
aDest.SetLength(old_dest_length + aSource.Length());
nsAString::const_iterator fromBegin, fromEnd;
nsACString::iterator dest;
aDest.BeginWriting(dest);
dest.advance(old_dest_length);
// right now, this won't work on multi-fragment destinations
LossyConvertEncoding16to8 converter(dest.get());
copy_string(aSource.BeginReading(fromBegin), aSource.EndReading(fromEnd),
converter);
}
void
AppendASCIItoUTF16(const nsACString& aSource, nsAString& aDest)
{
if (!AppendASCIItoUTF16(aSource, aDest, mozilla::fallible)) {
aDest.AllocFailed(aDest.Length() + aSource.Length());
}
}
bool
AppendASCIItoUTF16(const nsACString& aSource, nsAString& aDest,
const mozilla::fallible_t& aFallible)
{
uint32_t old_dest_length = aDest.Length();
if (!aDest.SetLength(old_dest_length + aSource.Length(),
aFallible)) {
return false;
}
nsACString::const_iterator fromBegin, fromEnd;
nsAString::iterator dest;
aDest.BeginWriting(dest);
dest.advance(old_dest_length);
// right now, this won't work on multi-fragment destinations
LossyConvertEncoding8to16 converter(dest.get());
copy_string(aSource.BeginReading(fromBegin), aSource.EndReading(fromEnd),
converter);
return true;
}
void
LossyAppendUTF16toASCII(const char16ptr_t aSource, nsACString& aDest)
{
if (aSource) {
LossyAppendUTF16toASCII(nsDependentString(aSource), aDest);
}
}
bool
AppendASCIItoUTF16(const char* aSource, nsAString& aDest, const mozilla::fallible_t& aFallible)
{
if (aSource) {
return AppendASCIItoUTF16(nsDependentCString(aSource), aDest, aFallible);
}
return true;
}
void
AppendASCIItoUTF16(const char* aSource, nsAString& aDest)
{
if (aSource) {
AppendASCIItoUTF16(nsDependentCString(aSource), aDest);
}
}
void
AppendUTF16toUTF8(const nsAString& aSource, nsACString& aDest)
{
if (!AppendUTF16toUTF8(aSource, aDest, mozilla::fallible)) {
// Note that this may wildly underestimate the allocation that failed, as
// we report the length of aSource as UTF-16 instead of UTF-8.
aDest.AllocFailed(aDest.Length() + aSource.Length());
}
}
bool
AppendUTF16toUTF8(const nsAString& aSource, nsACString& aDest,
const mozilla::fallible_t& aFallible)
{
nsAString::const_iterator source_start, source_end;
CalculateUTF8Size calculator;
copy_string(aSource.BeginReading(source_start),
aSource.EndReading(source_end), calculator);
size_t count = calculator.Size();
if (count) {
auto old_dest_length = aDest.Length();
// Grow the buffer if we need to.
mozilla::CheckedInt<nsACString::size_type> new_length(count);
new_length += old_dest_length;
if (!new_length.isValid() ||
!aDest.SetLength(new_length.value(), aFallible)) {
return false;
}
// All ready? Time to convert
ConvertUTF16toUTF8 converter(aDest.BeginWriting() + old_dest_length);
copy_string(aSource.BeginReading(source_start),
aSource.EndReading(source_end), converter);
NS_ASSERTION(converter.Size() == count,
"Unexpected disparity between CalculateUTF8Size and "
"ConvertUTF16toUTF8");
}
return true;
}
void
AppendUTF8toUTF16(const nsACString& aSource, nsAString& aDest)
{
if (!AppendUTF8toUTF16(aSource, aDest, mozilla::fallible)) {
aDest.AllocFailed(aDest.Length() + aSource.Length());
}
}
bool
AppendUTF8toUTF16(const nsACString& aSource, nsAString& aDest,
const mozilla::fallible_t& aFallible)
{
nsACString::const_iterator source_start, source_end;
CalculateUTF8Length calculator;
copy_string(aSource.BeginReading(source_start),
aSource.EndReading(source_end), calculator);
uint32_t count = calculator.Length();
// Avoid making the string mutable if we're appending an empty string
if (count) {
uint32_t old_dest_length = aDest.Length();
// Grow the buffer if we need to.
if (!aDest.SetLength(old_dest_length + count, aFallible)) {
return false;
}
// All ready? Time to convert
ConvertUTF8toUTF16 converter(aDest.BeginWriting() + old_dest_length);
copy_string(aSource.BeginReading(source_start),
aSource.EndReading(source_end), converter);
NS_ASSERTION(converter.ErrorEncountered() ||
converter.Length() == count,
"CalculateUTF8Length produced the wrong length");
if (converter.ErrorEncountered()) {
NS_ERROR("Input wasn't UTF8 or incorrect length was calculated");
aDest.SetLength(old_dest_length);
}
}
return true;
}
void
AppendUTF16toUTF8(const char16ptr_t aSource, nsACString& aDest)
{
if (aSource) {
AppendUTF16toUTF8(nsDependentString(aSource), aDest);
}
}
void
AppendUTF8toUTF16(const char* aSource, nsAString& aDest)
{
if (aSource) {
AppendUTF8toUTF16(nsDependentCString(aSource), aDest);
}
}
/**
* A helper function that allocates a buffer of the desired character type big enough to hold a copy of the supplied string (plus a zero terminator).
*
* @param aSource an string you will eventually be making a copy of
* @return a new buffer (of the type specified by the second parameter) which you must free with |free|.
*
*/
template <class FromStringT, class ToCharT>
inline
ToCharT*
AllocateStringCopy(const FromStringT& aSource, ToCharT*)
{
return static_cast<ToCharT*>(moz_xmalloc(
(aSource.Length() + 1) * sizeof(ToCharT)));
}
char*
ToNewCString(const nsAString& aSource)
{
char* result = AllocateStringCopy(aSource, (char*)0);
if (!result) {
return nullptr;
}
nsAString::const_iterator fromBegin, fromEnd;
LossyConvertEncoding16to8 converter(result);
copy_string(aSource.BeginReading(fromBegin), aSource.EndReading(fromEnd),
converter).write_terminator();
return result;
}
char*
ToNewUTF8String(const nsAString& aSource, uint32_t* aUTF8Count)
{
nsAString::const_iterator start, end;
CalculateUTF8Size calculator;
copy_string(aSource.BeginReading(start), aSource.EndReading(end),
calculator);
if (aUTF8Count) {
*aUTF8Count = calculator.Size();
}
char* result = static_cast<char*>
(moz_xmalloc(calculator.Size() + 1));
if (!result) {
return nullptr;
}
ConvertUTF16toUTF8 converter(result);
copy_string(aSource.BeginReading(start), aSource.EndReading(end),
converter).write_terminator();
NS_ASSERTION(calculator.Size() == converter.Size(), "length mismatch");
return result;
}
char*
ToNewCString(const nsACString& aSource)
{
// no conversion needed, just allocate a buffer of the correct length and copy into it
char* result = AllocateStringCopy(aSource, (char*)0);
if (!result) {
return nullptr;
}
nsACString::const_iterator fromBegin, fromEnd;
char* toBegin = result;
*copy_string(aSource.BeginReading(fromBegin), aSource.EndReading(fromEnd),
toBegin) = char(0);
return result;
}
char16_t*
ToNewUnicode(const nsAString& aSource)
{
// no conversion needed, just allocate a buffer of the correct length and copy into it
char16_t* result = AllocateStringCopy(aSource, (char16_t*)0);
if (!result) {
return nullptr;
}
nsAString::const_iterator fromBegin, fromEnd;
char16_t* toBegin = result;
*copy_string(aSource.BeginReading(fromBegin), aSource.EndReading(fromEnd),
toBegin) = char16_t(0);
return result;
}
char16_t*
ToNewUnicode(const nsACString& aSource)
{
char16_t* result = AllocateStringCopy(aSource, (char16_t*)0);
if (!result) {
return nullptr;
}
nsACString::const_iterator fromBegin, fromEnd;
LossyConvertEncoding8to16 converter(result);
copy_string(aSource.BeginReading(fromBegin), aSource.EndReading(fromEnd),
converter).write_terminator();
return result;
}
uint32_t
CalcUTF8ToUnicodeLength(const nsACString& aSource)
{
nsACString::const_iterator start, end;
CalculateUTF8Length calculator;
copy_string(aSource.BeginReading(start), aSource.EndReading(end),
calculator);
return calculator.Length();
}
char16_t*
UTF8ToUnicodeBuffer(const nsACString& aSource, char16_t* aBuffer,
uint32_t* aUTF16Count)
{
nsACString::const_iterator start, end;
ConvertUTF8toUTF16 converter(aBuffer);
copy_string(aSource.BeginReading(start),
aSource.EndReading(end),
converter).write_terminator();
if (aUTF16Count) {
*aUTF16Count = converter.Length();
}
return aBuffer;
}
char16_t*
UTF8ToNewUnicode(const nsACString& aSource, uint32_t* aUTF16Count)
{
const uint32_t length = CalcUTF8ToUnicodeLength(aSource);
const size_t buffer_size = (length + 1) * sizeof(char16_t);
char16_t* buffer = static_cast<char16_t*>(moz_xmalloc(buffer_size));
if (!buffer) {
return nullptr;
}
uint32_t copied;
UTF8ToUnicodeBuffer(aSource, buffer, &copied);
NS_ASSERTION(length == copied, "length mismatch");
if (aUTF16Count) {
*aUTF16Count = copied;
}
return buffer;
}
char16_t*
CopyUnicodeTo(const nsAString& aSource, uint32_t aSrcOffset, char16_t* aDest,
uint32_t aLength)
{
nsAString::const_iterator fromBegin, fromEnd;
char16_t* toBegin = aDest;
copy_string(aSource.BeginReading(fromBegin).advance(int32_t(aSrcOffset)),
aSource.BeginReading(fromEnd).advance(int32_t(aSrcOffset + aLength)),
toBegin);
return aDest;
}
void
CopyUnicodeTo(const nsAString::const_iterator& aSrcStart,
const nsAString::const_iterator& aSrcEnd,
nsAString& aDest)
{
nsAString::iterator writer;
aDest.SetLength(Distance(aSrcStart, aSrcEnd));
aDest.BeginWriting(writer);
nsAString::const_iterator fromBegin(aSrcStart);
copy_string(fromBegin, aSrcEnd, writer);
}
void
AppendUnicodeTo(const nsAString::const_iterator& aSrcStart,
const nsAString::const_iterator& aSrcEnd,
nsAString& aDest)
{
nsAString::iterator writer;
uint32_t oldLength = aDest.Length();
aDest.SetLength(oldLength + Distance(aSrcStart, aSrcEnd));
aDest.BeginWriting(writer).advance(oldLength);
nsAString::const_iterator fromBegin(aSrcStart);
copy_string(fromBegin, aSrcEnd, writer);
}
bool
IsASCII(const nsAString& aString)
{
static const char16_t NOT_ASCII = char16_t(~0x007F);
// Don't want to use |copy_string| for this task, since we can stop at the first non-ASCII character
nsAString::const_iterator iter, done_reading;
aString.BeginReading(iter);
aString.EndReading(done_reading);
const char16_t* c = iter.get();
const char16_t* end = done_reading.get();
while (c < end) {
if (*c++ & NOT_ASCII) {
return false;
}
}
return true;
}
bool
IsASCII(const nsACString& aString)
{
static const char NOT_ASCII = char(~0x7F);
// Don't want to use |copy_string| for this task, since we can stop at the first non-ASCII character
nsACString::const_iterator iter, done_reading;
aString.BeginReading(iter);
aString.EndReading(done_reading);
const char* c = iter.get();
const char* end = done_reading.get();
while (c < end) {
if (*c++ & NOT_ASCII) {
return false;
}
}
return true;
}
bool
IsUTF8(const nsACString& aString, bool aRejectNonChar)
{
nsReadingIterator<char> done_reading;
aString.EndReading(done_reading);
int32_t state = 0;
bool overlong = false;
bool surrogate = false;
bool nonchar = false;
uint16_t olupper = 0; // overlong byte upper bound.
uint16_t slower = 0; // surrogate byte lower bound.
nsReadingIterator<char> iter;
aString.BeginReading(iter);
const char* ptr = iter.get();
const char* end = done_reading.get();
while (ptr < end) {
uint8_t c;
if (0 == state) {
c = *ptr++;
if (UTF8traits::isASCII(c)) {
continue;
}
if (c <= 0xC1) { // [80-BF] where not expected, [C0-C1] for overlong.
return false;
} else if (UTF8traits::is2byte(c)) {
state = 1;
} else if (UTF8traits::is3byte(c)) {
state = 2;
if (c == 0xE0) { // to exclude E0[80-9F][80-BF]
overlong = true;
olupper = 0x9F;
} else if (c == 0xED) { // ED[A0-BF][80-BF] : surrogate codepoint
surrogate = true;
slower = 0xA0;
} else if (c == 0xEF) { // EF BF [BE-BF] : non-character
nonchar = true;
}
} else if (c <= 0xF4) { // XXX replace /w UTF8traits::is4byte when it's updated to exclude [F5-F7].(bug 199090)
state = 3;
nonchar = true;
if (c == 0xF0) { // to exclude F0[80-8F][80-BF]{2}
overlong = true;
olupper = 0x8F;
} else if (c == 0xF4) { // to exclude F4[90-BF][80-BF]
// actually not surrogates but codepoints beyond 0x10FFFF
surrogate = true;
slower = 0x90;
}
} else {
return false; // Not UTF-8 string
}
}
if (nonchar && !aRejectNonChar) {
nonchar = false;
}
while (ptr < end && state) {
c = *ptr++;
--state;
// non-character : EF BF [BE-BF] or F[0-7] [89AB]F BF [BE-BF]
if (nonchar &&
((!state && c < 0xBE) ||
(state == 1 && c != 0xBF) ||
(state == 2 && 0x0F != (0x0F & c)))) {
nonchar = false;
}
if (!UTF8traits::isInSeq(c) || (overlong && c <= olupper) ||
(surrogate && slower <= c) || (nonchar && !state)) {
return false; // Not UTF-8 string
}
overlong = surrogate = false;
}
}
return !state; // state != 0 at the end indicates an invalid UTF-8 seq.
}
/**
* A character sink for in-place case conversion.
*/
class ConvertToUpperCase
{
public:
typedef char value_type;
uint32_t
write(const char* aSource, uint32_t aSourceLength)
{
char* cp = const_cast<char*>(aSource);
const char* end = aSource + aSourceLength;
while (cp != end) {
char ch = *cp;
if (ch >= 'a' && ch <= 'z') {
*cp = ch - ('a' - 'A');
}
++cp;
}
return aSourceLength;
}
};
void
ToUpperCase(nsCSubstring& aCString)
{
ConvertToUpperCase converter;
char* start;
converter.write(aCString.BeginWriting(start), aCString.Length());
}
/**
* A character sink for copying with case conversion.
*/
class CopyToUpperCase
{
public:
typedef char value_type;
explicit CopyToUpperCase(nsACString::iterator& aDestIter)
: mIter(aDestIter)
{
}
uint32_t
write(const char* aSource, uint32_t aSourceLength)
{
uint32_t len = XPCOM_MIN(uint32_t(mIter.size_forward()), aSourceLength);
char* cp = mIter.get();
const char* end = aSource + len;
while (aSource != end) {
char ch = *aSource;
if ((ch >= 'a') && (ch <= 'z')) {
*cp = ch - ('a' - 'A');
} else {
*cp = ch;
}
++aSource;
++cp;
}
mIter.advance(len);
return len;
}
protected:
nsACString::iterator& mIter;
};
void
ToUpperCase(const nsACString& aSource, nsACString& aDest)
{
nsACString::const_iterator fromBegin, fromEnd;
nsACString::iterator toBegin;
aDest.SetLength(aSource.Length());
CopyToUpperCase converter(aDest.BeginWriting(toBegin));
copy_string(aSource.BeginReading(fromBegin), aSource.EndReading(fromEnd),
converter);
}
/**
* A character sink for case conversion.
*/
class ConvertToLowerCase
{
public:
typedef char value_type;
uint32_t
write(const char* aSource, uint32_t aSourceLength)
{
char* cp = const_cast<char*>(aSource);
const char* end = aSource + aSourceLength;
while (cp != end) {
char ch = *cp;
if ((ch >= 'A') && (ch <= 'Z')) {
*cp = ch + ('a' - 'A');
}
++cp;
}
return aSourceLength;
}
};
void
ToLowerCase(nsCSubstring& aCString)
{
ConvertToLowerCase converter;
char* start;
converter.write(aCString.BeginWriting(start), aCString.Length());
}
/**
* A character sink for copying with case conversion.
*/
class CopyToLowerCase
{
public:
typedef char value_type;
explicit CopyToLowerCase(nsACString::iterator& aDestIter)
: mIter(aDestIter)
{
}
uint32_t
write(const char* aSource, uint32_t aSourceLength)
{
uint32_t len = XPCOM_MIN(uint32_t(mIter.size_forward()), aSourceLength);
char* cp = mIter.get();
const char* end = aSource + len;
while (aSource != end) {
char ch = *aSource;
if ((ch >= 'A') && (ch <= 'Z')) {
*cp = ch + ('a' - 'A');
} else {
*cp = ch;
}
++aSource;
++cp;
}
mIter.advance(len);
return len;
}
protected:
nsACString::iterator& mIter;
};
void
ToLowerCase(const nsACString& aSource, nsACString& aDest)
{
nsACString::const_iterator fromBegin, fromEnd;
nsACString::iterator toBegin;
aDest.SetLength(aSource.Length());
CopyToLowerCase converter(aDest.BeginWriting(toBegin));
copy_string(aSource.BeginReading(fromBegin), aSource.EndReading(fromEnd),
converter);
}
bool
ParseString(const nsACString& aSource, char aDelimiter,
nsTArray<nsCString>& aArray)
{
nsACString::const_iterator start, end;
aSource.BeginReading(start);
aSource.EndReading(end);
uint32_t oldLength = aArray.Length();
for (;;) {
nsACString::const_iterator delimiter = start;
FindCharInReadable(aDelimiter, delimiter, end);
if (delimiter != start) {
if (!aArray.AppendElement(Substring(start, delimiter))) {
aArray.RemoveElementsAt(oldLength, aArray.Length() - oldLength);
return false;
}
}
if (delimiter == end) {
break;
}
start = ++delimiter;
if (start == end) {
break;
}
}
return true;
}
template <class StringT, class IteratorT, class Comparator>
bool
FindInReadable_Impl(const StringT& aPattern, IteratorT& aSearchStart,
IteratorT& aSearchEnd, const Comparator& aCompare)
{
bool found_it = false;
// only bother searching at all if we're given a non-empty range to search
if (aSearchStart != aSearchEnd) {
IteratorT aPatternStart, aPatternEnd;
aPattern.BeginReading(aPatternStart);
aPattern.EndReading(aPatternEnd);
// outer loop keeps searching till we find it or run out of string to search
while (!found_it) {
// fast inner loop (that's what it's called, not what it is) looks for a potential match
while (aSearchStart != aSearchEnd &&
aCompare(aPatternStart.get(), aSearchStart.get(), 1, 1)) {
++aSearchStart;
}
// if we broke out of the `fast' loop because we're out of string ... we're done: no match
if (aSearchStart == aSearchEnd) {
break;
}
// otherwise, we're at a potential match, let's see if we really hit one
IteratorT testPattern(aPatternStart);
IteratorT testSearch(aSearchStart);
// slow inner loop verifies the potential match (found by the `fast' loop) at the current position
for (;;) {
// we already compared the first character in the outer loop,
// so we'll advance before the next comparison
++testPattern;
++testSearch;
// if we verified all the way to the end of the pattern, then we found it!
if (testPattern == aPatternEnd) {
found_it = true;
aSearchEnd = testSearch; // return the exact found range through the parameters
break;
}
// if we got to end of the string we're searching before we hit the end of the
// pattern, we'll never find what we're looking for
if (testSearch == aSearchEnd) {
aSearchStart = aSearchEnd;
break;
}
// else if we mismatched ... it's time to advance to the next search position
// and get back into the `fast' loop
if (aCompare(testPattern.get(), testSearch.get(), 1, 1)) {
++aSearchStart;
break;
}
}
}
}
return found_it;
}
/**
* This searches the entire string from right to left, and returns the first match found, if any.
*/
template <class StringT, class IteratorT, class Comparator>
bool
RFindInReadable_Impl(const StringT& aPattern, IteratorT& aSearchStart,
IteratorT& aSearchEnd, const Comparator& aCompare)
{
IteratorT patternStart, patternEnd, searchEnd = aSearchEnd;
aPattern.BeginReading(patternStart);
aPattern.EndReading(patternEnd);
// Point to the last character in the pattern
--patternEnd;
// outer loop keeps searching till we run out of string to search
while (aSearchStart != searchEnd) {
// Point to the end position of the next possible match
--searchEnd;
// Check last character, if a match, explore further from here
if (aCompare(patternEnd.get(), searchEnd.get(), 1, 1) == 0) {
// We're at a potential match, let's see if we really hit one
IteratorT testPattern(patternEnd);
IteratorT testSearch(searchEnd);
// inner loop verifies the potential match at the current position
do {
// if we verified all the way to the end of the pattern, then we found it!
if (testPattern == patternStart) {
aSearchStart = testSearch; // point to start of match
aSearchEnd = ++searchEnd; // point to end of match
return true;
}
// if we got to end of the string we're searching before we hit the end of the
// pattern, we'll never find what we're looking for
if (testSearch == aSearchStart) {
aSearchStart = aSearchEnd;
return false;
}
// test previous character for a match
--testPattern;
--testSearch;
} while (aCompare(testPattern.get(), testSearch.get(), 1, 1) == 0);
}
}
aSearchStart = aSearchEnd;
return false;
}
bool
FindInReadable(const nsAString& aPattern,
nsAString::const_iterator& aSearchStart,
nsAString::const_iterator& aSearchEnd,
const nsStringComparator& aComparator)
{
return FindInReadable_Impl(aPattern, aSearchStart, aSearchEnd, aComparator);
}
bool
FindInReadable(const nsACString& aPattern,
nsACString::const_iterator& aSearchStart,
nsACString::const_iterator& aSearchEnd,
const nsCStringComparator& aComparator)
{
return FindInReadable_Impl(aPattern, aSearchStart, aSearchEnd, aComparator);
}
bool
CaseInsensitiveFindInReadable(const nsACString& aPattern,
nsACString::const_iterator& aSearchStart,
nsACString::const_iterator& aSearchEnd)
{
return FindInReadable_Impl(aPattern, aSearchStart, aSearchEnd,
nsCaseInsensitiveCStringComparator());
}
bool
RFindInReadable(const nsAString& aPattern,
nsAString::const_iterator& aSearchStart,
nsAString::const_iterator& aSearchEnd,
const nsStringComparator& aComparator)
{
return RFindInReadable_Impl(aPattern, aSearchStart, aSearchEnd, aComparator);
}
bool
RFindInReadable(const nsACString& aPattern,
nsACString::const_iterator& aSearchStart,
nsACString::const_iterator& aSearchEnd,
const nsCStringComparator& aComparator)
{
return RFindInReadable_Impl(aPattern, aSearchStart, aSearchEnd, aComparator);
}
bool
FindCharInReadable(char16_t aChar, nsAString::const_iterator& aSearchStart,
const nsAString::const_iterator& aSearchEnd)
{
int32_t fragmentLength = aSearchEnd.get() - aSearchStart.get();
const char16_t* charFoundAt =
nsCharTraits<char16_t>::find(aSearchStart.get(), fragmentLength, aChar);
if (charFoundAt) {
aSearchStart.advance(charFoundAt - aSearchStart.get());
return true;
}
aSearchStart.advance(fragmentLength);
return false;
}
bool
FindCharInReadable(char aChar, nsACString::const_iterator& aSearchStart,
const nsACString::const_iterator& aSearchEnd)
{
int32_t fragmentLength = aSearchEnd.get() - aSearchStart.get();
const char* charFoundAt =
nsCharTraits<char>::find(aSearchStart.get(), fragmentLength, aChar);
if (charFoundAt) {
aSearchStart.advance(charFoundAt - aSearchStart.get());
return true;
}
aSearchStart.advance(fragmentLength);
return false;
}
uint32_t
CountCharInReadable(const nsAString& aStr, char16_t aChar)
{
uint32_t count = 0;
nsAString::const_iterator begin, end;
aStr.BeginReading(begin);
aStr.EndReading(end);
while (begin != end) {
if (*begin == aChar) {
++count;
}
++begin;
}
return count;
}
uint32_t
CountCharInReadable(const nsACString& aStr, char aChar)
{
uint32_t count = 0;
nsACString::const_iterator begin, end;
aStr.BeginReading(begin);
aStr.EndReading(end);
while (begin != end) {
if (*begin == aChar) {
++count;
}
++begin;
}
return count;
}
bool
StringBeginsWith(const nsAString& aSource, const nsAString& aSubstring)
{
nsAString::size_type src_len = aSource.Length(),
sub_len = aSubstring.Length();
if (sub_len > src_len) {
return false;
}
return Substring(aSource, 0, sub_len).Equals(aSubstring);
}
bool
StringBeginsWith(const nsAString& aSource, const nsAString& aSubstring,
const nsStringComparator& aComparator)
{
nsAString::size_type src_len = aSource.Length(),
sub_len = aSubstring.Length();
if (sub_len > src_len) {
return false;
}
return Substring(aSource, 0, sub_len).Equals(aSubstring, aComparator);
}
bool
StringBeginsWith(const nsACString& aSource, const nsACString& aSubstring)
{
nsACString::size_type src_len = aSource.Length(),
sub_len = aSubstring.Length();
if (sub_len > src_len) {
return false;
}
return Substring(aSource, 0, sub_len).Equals(aSubstring);
}
bool
StringBeginsWith(const nsACString& aSource, const nsACString& aSubstring,
const nsCStringComparator& aComparator)
{
nsACString::size_type src_len = aSource.Length(),
sub_len = aSubstring.Length();
if (sub_len > src_len) {
return false;
}
return Substring(aSource, 0, sub_len).Equals(aSubstring, aComparator);
}
bool
StringEndsWith(const nsAString& aSource, const nsAString& aSubstring)
{
nsAString::size_type src_len = aSource.Length(),
sub_len = aSubstring.Length();
if (sub_len > src_len) {
return false;
}
return Substring(aSource, src_len - sub_len, sub_len).Equals(aSubstring);
}
bool
StringEndsWith(const nsAString& aSource, const nsAString& aSubstring,
const nsStringComparator& aComparator)
{
nsAString::size_type src_len = aSource.Length(),
sub_len = aSubstring.Length();
if (sub_len > src_len) {
return false;
}
return Substring(aSource, src_len - sub_len, sub_len).Equals(aSubstring,
aComparator);
}
bool
StringEndsWith(const nsACString& aSource, const nsACString& aSubstring)
{
nsACString::size_type src_len = aSource.Length(),
sub_len = aSubstring.Length();
if (sub_len > src_len) {
return false;
}
return Substring(aSource, src_len - sub_len, sub_len).Equals(aSubstring);
}
bool
StringEndsWith(const nsACString& aSource, const nsACString& aSubstring,
const nsCStringComparator& aComparator)
{
nsACString::size_type src_len = aSource.Length(),
sub_len = aSubstring.Length();
if (sub_len > src_len) {
return false;
}
return Substring(aSource, src_len - sub_len, sub_len).Equals(aSubstring,
aComparator);
}
static const char16_t empty_buffer[1] = { '\0' };
const nsAFlatString&
EmptyString()
{
static const nsDependentString sEmpty(empty_buffer);
return sEmpty;
}
const nsAFlatCString&
EmptyCString()
{
static const nsDependentCString sEmpty((const char*)empty_buffer);
return sEmpty;
}
const nsAFlatString&
NullString()
{
static const nsXPIDLString sNull;
return sNull;
}
const nsAFlatCString&
NullCString()
{
static const nsXPIDLCString sNull;
return sNull;
}
int32_t
CompareUTF8toUTF16(const nsASingleFragmentCString& aUTF8String,
const nsASingleFragmentString& aUTF16String)
{
static const uint32_t NOT_ASCII = uint32_t(~0x7F);
const char* u8;
const char* u8end;
aUTF8String.BeginReading(u8);
aUTF8String.EndReading(u8end);
const char16_t* u16;
const char16_t* u16end;
aUTF16String.BeginReading(u16);
aUTF16String.EndReading(u16end);
while (u8 != u8end && u16 != u16end) {
// Cast away the signedness of *u8 to prevent signextension when
// converting to uint32_t
uint32_t c8_32 = (uint8_t)*u8;
if (c8_32 & NOT_ASCII) {
bool err;
c8_32 = UTF8CharEnumerator::NextChar(&u8, u8end, &err);
if (err) {
return INT32_MIN;
}
uint32_t c16_32 = UTF16CharEnumerator::NextChar(&u16, u16end);
// The above UTF16CharEnumerator::NextChar() calls can
// fail, but if it does for anything other than no data to
// look at (which can't happen here), it returns the
// Unicode replacement character 0xFFFD for the invalid
// data they were fed. Ignore that error and treat invalid
// UTF16 as 0xFFFD.
//
// This matches what our UTF16 to UTF8 conversion code
// does, and thus a UTF8 string that came from an invalid
// UTF16 string will compare equal to the invalid UTF16
// string it came from. Same is true for any other UTF16
// string differs only in the invalid part of the string.
if (c8_32 != c16_32) {
return c8_32 < c16_32 ? -1 : 1;
}
} else {
if (c8_32 != *u16) {
return c8_32 > *u16 ? 1 : -1;
}
++u8;
++u16;
}
}
if (u8 != u8end) {
// We get to the end of the UTF16 string, but no to the end of
// the UTF8 string. The UTF8 string is longer than the UTF16
// string
return 1;
}
if (u16 != u16end) {
// We get to the end of the UTF8 string, but no to the end of
// the UTF16 string. The UTF16 string is longer than the UTF8
// string
return -1;
}
// The two strings match.
return 0;
}
void
AppendUCS4ToUTF16(const uint32_t aSource, nsAString& aDest)
{
NS_ASSERTION(IS_VALID_CHAR(aSource), "Invalid UCS4 char");
if (IS_IN_BMP(aSource)) {
aDest.Append(char16_t(aSource));
} else {
aDest.Append(H_SURROGATE(aSource));
aDest.Append(L_SURROGATE(aSource));
}
}