url_canon_icu.cc 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  1. // Copyright 2013 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. // ICU-based character set converter.
  5. #include <stdint.h>
  6. #include <stdlib.h>
  7. #include <string.h>
  8. #include "base/check.h"
  9. #include "base/memory/raw_ptr.h"
  10. #include "third_party/icu/source/common/unicode/ucnv.h"
  11. #include "third_party/icu/source/common/unicode/ucnv_cb.h"
  12. #include "third_party/icu/source/common/unicode/utypes.h"
  13. #include "url/url_canon_icu.h"
  14. #include "url/url_canon_internal.h" // for _itoa_s
  15. namespace url {
  16. namespace {
  17. // Called when converting a character that can not be represented, this will
  18. // append an escaped version of the numerical character reference for that code
  19. // point. It is of the form "&#1234;" and we will escape the non-digits to
  20. // "%26%231234%3B". Why? This is what Netscape did back in the olden days.
  21. void appendURLEscapedChar(const void* context,
  22. UConverterFromUnicodeArgs* from_args,
  23. const UChar* code_units,
  24. int32_t length,
  25. UChar32 code_point,
  26. UConverterCallbackReason reason,
  27. UErrorCode* err) {
  28. if (reason == UCNV_UNASSIGNED) {
  29. *err = U_ZERO_ERROR;
  30. const static int prefix_len = 6;
  31. const static char prefix[prefix_len + 1] = "%26%23"; // "&#" percent-escaped
  32. ucnv_cbFromUWriteBytes(from_args, prefix, prefix_len, 0, err);
  33. DCHECK(code_point < 0x110000);
  34. char number[8]; // Max Unicode code point is 7 digits.
  35. _itoa_s(code_point, number, 10);
  36. int number_len = static_cast<int>(strlen(number));
  37. ucnv_cbFromUWriteBytes(from_args, number, number_len, 0, err);
  38. const static int postfix_len = 3;
  39. const static char postfix[postfix_len + 1] = "%3B"; // ";" percent-escaped
  40. ucnv_cbFromUWriteBytes(from_args, postfix, postfix_len, 0, err);
  41. }
  42. }
  43. // A class for scoping the installation of the invalid character callback.
  44. class AppendHandlerInstaller {
  45. public:
  46. // The owner of this object must ensure that the converter is alive for the
  47. // duration of this object's lifetime.
  48. AppendHandlerInstaller(UConverter* converter) : converter_(converter) {
  49. UErrorCode err = U_ZERO_ERROR;
  50. ucnv_setFromUCallBack(converter_, appendURLEscapedChar, 0,
  51. &old_callback_, &old_context_, &err);
  52. }
  53. ~AppendHandlerInstaller() {
  54. UErrorCode err = U_ZERO_ERROR;
  55. ucnv_setFromUCallBack(converter_, old_callback_, old_context_, 0, 0, &err);
  56. }
  57. private:
  58. raw_ptr<UConverter> converter_;
  59. UConverterFromUCallback old_callback_;
  60. const void* old_context_;
  61. };
  62. } // namespace
  63. ICUCharsetConverter::ICUCharsetConverter(UConverter* converter)
  64. : converter_(converter) {
  65. }
  66. ICUCharsetConverter::~ICUCharsetConverter() = default;
  67. void ICUCharsetConverter::ConvertFromUTF16(const char16_t* input,
  68. int input_len,
  69. CanonOutput* output) {
  70. // Install our error handler. It will be called for character that can not
  71. // be represented in the destination character set.
  72. AppendHandlerInstaller handler(converter_);
  73. int begin_offset = output->length();
  74. int dest_capacity = output->capacity() - begin_offset;
  75. output->set_length(output->length());
  76. do {
  77. UErrorCode err = U_ZERO_ERROR;
  78. char* dest = &output->data()[begin_offset];
  79. int required_capacity = ucnv_fromUChars(converter_, dest, dest_capacity,
  80. input, input_len, &err);
  81. if (err != U_BUFFER_OVERFLOW_ERROR) {
  82. output->set_length(begin_offset + required_capacity);
  83. return;
  84. }
  85. // Output didn't fit, expand
  86. dest_capacity = required_capacity;
  87. output->Resize(begin_offset + dest_capacity);
  88. } while (true);
  89. }
  90. } // namespace url