/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* vim: set ts=8 sts=4 et sw=4 tw=99: */ /* 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/. */ /* * Infrastructure for sharing DOMString data with JSStrings. * * Importing an nsAString into JS: * If possible (GetSharedBufferHandle works) use the external string support in * JS to create a JSString that points to the readable's buffer. We keep a * reference to the buffer handle until the JSString is finalized. * * Exporting a JSString as an nsAReadable: * Wrap the JSString with a root-holding XPCJSReadableStringWrapper, which roots * the string and exposes its buffer via the nsAString interface, as * well as providing refcounting support. */ #include "nsAutoPtr.h" #include "nscore.h" #include "nsString.h" #include "nsStringBuffer.h" #include "jsapi.h" #include "xpcpublic.h" using namespace JS; // static void XPCStringConvert::FreeZoneCache(JS::Zone* zone) { // Put the zone user data into an AutoPtr (which will do the cleanup for us), // and null out the user data (which may already be null). nsAutoPtr<ZoneStringCache> cache(static_cast<ZoneStringCache*>(JS_GetZoneUserData(zone))); JS_SetZoneUserData(zone, nullptr); } // static void XPCStringConvert::ClearZoneCache(JS::Zone* zone) { // Although we clear the cache in FinalizeDOMString if needed, we also clear // the cache here to avoid a dangling JSString* pointer when compacting GC // moves the external string in memory. ZoneStringCache* cache = static_cast<ZoneStringCache*>(JS_GetZoneUserData(zone)); if (cache) { cache->mBuffer = nullptr; cache->mLength = 0; cache->mString = nullptr; } } // static void XPCStringConvert::FinalizeLiteral(JS::Zone* zone, const JSStringFinalizer* fin, char16_t* chars) { } const JSStringFinalizer XPCStringConvert::sLiteralFinalizer = { XPCStringConvert::FinalizeLiteral }; // static void XPCStringConvert::FinalizeDOMString(JS::Zone* zone, const JSStringFinalizer* fin, char16_t* chars) { nsStringBuffer* buf = nsStringBuffer::FromData(chars); // Clear the ZoneStringCache if needed, as this can be called outside GC // when flattening an external string. ZoneStringCache* cache = static_cast<ZoneStringCache*>(JS_GetZoneUserData(zone)); if (cache && cache->mBuffer == buf) { cache->mBuffer = nullptr; cache->mLength = 0; cache->mString = nullptr; } buf->Release(); } const JSStringFinalizer XPCStringConvert::sDOMStringFinalizer = { XPCStringConvert::FinalizeDOMString }; // convert a readable to a JSString, copying string data // static bool XPCStringConvert::ReadableToJSVal(JSContext* cx, const nsAString& readable, nsStringBuffer** sharedBuffer, MutableHandleValue vp) { *sharedBuffer = nullptr; uint32_t length = readable.Length(); if (readable.IsLiteral()) { JSString* str = JS_NewExternalString(cx, static_cast<const char16_t*>(readable.BeginReading()), length, &sLiteralFinalizer); if (!str) return false; vp.setString(str); return true; } nsStringBuffer* buf = nsStringBuffer::FromString(readable); if (buf) { bool shared; if (!StringBufferToJSVal(cx, buf, length, vp, &shared)) return false; if (shared) *sharedBuffer = buf; return true; } // blech, have to copy. JSString* str = JS_NewUCStringCopyN(cx, readable.BeginReading(), length); if (!str) return false; vp.setString(str); return true; } namespace xpc { bool NonVoidStringToJsval(JSContext* cx, nsAString& str, MutableHandleValue rval) { nsStringBuffer* sharedBuffer; if (!XPCStringConvert::ReadableToJSVal(cx, str, &sharedBuffer, rval)) return false; if (sharedBuffer) { // The string was shared but ReadableToJSVal didn't addref it. // Move the ownership from str to jsstr. str.ForgetSharedBuffer(); } return true; } } // namespace xpc