1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7 /*
8 * Infrastructure for sharing DOMString data with JSStrings.
9 *
10 * Importing an nsAString into JS:
11 * If possible (GetSharedBufferHandle works) use the external string support in
12 * JS to create a JSString that points to the readable's buffer. We keep a
13 * reference to the buffer handle until the JSString is finalized.
14 *
15 * Exporting a JSString as an nsAReadable:
16 * Wrap the JSString with a root-holding XPCJSReadableStringWrapper, which roots
17 * the string and exposes its buffer via the nsAString interface, as
18 * well as providing refcounting support.
19 */
20
21 #include "nscore.h"
22 #include "nsString.h"
23 #include "nsStringBuffer.h"
24 #include "jsapi.h"
25 #include "xpcpublic.h"
26
27 using namespace JS;
28
29 const XPCStringConvert::LiteralExternalString
30 XPCStringConvert::sLiteralExternalString;
31
32 const XPCStringConvert::DOMStringExternalString
33 XPCStringConvert::sDOMStringExternalString;
34
35 const XPCStringConvert::DynamicAtomExternalString
36 XPCStringConvert::sDynamicAtomExternalString;
37
finalize(char16_t * aChars) const38 void XPCStringConvert::LiteralExternalString::finalize(char16_t* aChars) const {
39 // Nothing to do.
40 }
41
sizeOfBuffer(const char16_t * aChars,mozilla::MallocSizeOf aMallocSizeOf) const42 size_t XPCStringConvert::LiteralExternalString::sizeOfBuffer(
43 const char16_t* aChars, mozilla::MallocSizeOf aMallocSizeOf) const {
44 // This string's buffer is not heap-allocated, so its malloc size is 0.
45 return 0;
46 }
47
finalize(char16_t * aChars) const48 void XPCStringConvert::DOMStringExternalString::finalize(
49 char16_t* aChars) const {
50 nsStringBuffer* buf = nsStringBuffer::FromData(aChars);
51 buf->Release();
52 }
53
sizeOfBuffer(const char16_t * aChars,mozilla::MallocSizeOf aMallocSizeOf) const54 size_t XPCStringConvert::DOMStringExternalString::sizeOfBuffer(
55 const char16_t* aChars, mozilla::MallocSizeOf aMallocSizeOf) const {
56 // We promised the JS engine we would not GC. Enforce that:
57 JS::AutoCheckCannotGC autoCannotGC;
58
59 const nsStringBuffer* buf =
60 nsStringBuffer::FromData(const_cast<char16_t*>(aChars));
61 // We want sizeof including this, because the entire string buffer is owned by
62 // the external string. But only report here if we're unshared; if we're
63 // shared then we don't know who really owns this data.
64 return buf->SizeOfIncludingThisIfUnshared(aMallocSizeOf);
65 }
66
finalize(char16_t * aChars) const67 void XPCStringConvert::DynamicAtomExternalString::finalize(
68 char16_t* aChars) const {
69 nsDynamicAtom* atom = nsDynamicAtom::FromChars(aChars);
70 // nsDynamicAtom::Release is always-inline and defined in a translation unit
71 // we can't get to here. So we need to go through nsAtom::Release to call
72 // it.
73 static_cast<nsAtom*>(atom)->Release();
74 }
75
sizeOfBuffer(const char16_t * aChars,mozilla::MallocSizeOf aMallocSizeOf) const76 size_t XPCStringConvert::DynamicAtomExternalString::sizeOfBuffer(
77 const char16_t* aChars, mozilla::MallocSizeOf aMallocSizeOf) const {
78 // We return 0 here because NS_AddSizeOfAtoms reports all memory associated
79 // with atoms in the atom table.
80 return 0;
81 }
82
83 // convert a readable to a JSString, copying string data
84 // static
ReadableToJSVal(JSContext * cx,const nsAString & readable,nsStringBuffer ** sharedBuffer,MutableHandleValue vp)85 bool XPCStringConvert::ReadableToJSVal(JSContext* cx, const nsAString& readable,
86 nsStringBuffer** sharedBuffer,
87 MutableHandleValue vp) {
88 *sharedBuffer = nullptr;
89
90 uint32_t length = readable.Length();
91
92 if (readable.IsLiteral()) {
93 return StringLiteralToJSVal(cx, readable.BeginReading(), length, vp);
94 }
95
96 nsStringBuffer* buf = nsStringBuffer::FromString(readable);
97 if (buf) {
98 bool shared;
99 if (!StringBufferToJSVal(cx, buf, length, vp, &shared)) {
100 return false;
101 }
102 if (shared) {
103 *sharedBuffer = buf;
104 }
105 return true;
106 }
107
108 // blech, have to copy.
109 JSString* str = JS_NewUCStringCopyN(cx, readable.BeginReading(), length);
110 if (!str) {
111 return false;
112 }
113 vp.setString(str);
114 return true;
115 }
116
117 namespace xpc {
118
NonVoidStringToJsval(JSContext * cx,nsAString & str,MutableHandleValue rval)119 bool NonVoidStringToJsval(JSContext* cx, nsAString& str,
120 MutableHandleValue rval) {
121 nsStringBuffer* sharedBuffer;
122 if (!XPCStringConvert::ReadableToJSVal(cx, str, &sharedBuffer, rval)) {
123 return false;
124 }
125
126 if (sharedBuffer) {
127 // The string was shared but ReadableToJSVal didn't addref it.
128 // Move the ownership from str to jsstr.
129 str.ForgetSharedBuffer();
130 }
131 return true;
132 }
133
134 } // namespace xpc
135