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