1 : /* ***** BEGIN LICENSE BLOCK *****
2 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
3 : *
4 : * The contents of this file are subject to the Mozilla Public License Version
5 : * 1.1 (the "License"); you may not use this file except in compliance with
6 : * the License. You may obtain a copy of the License at
7 : * http://www.mozilla.org/MPL/
8 : *
9 : * Software distributed under the License is distributed on an "AS IS" basis,
10 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11 : * for the specific language governing rights and limitations under the
12 : * License.
13 : *
14 : * The Original Code is mozilla.org
15 : *
16 : * The Initial Developer of the Original Code is
17 : * Mozilla Foundation.
18 : * Portions created by the Initial Developer are Copyright (C) 2010-2011
19 : * the Initial Developer. All Rights Reserved.
20 : *
21 : * Contributor(s):
22 : * Michael Wu <mwu@mozilla.com>
23 : *
24 : * Alternatively, the contents of this file may be used under the terms of
25 : * either of the GNU General Public License Version 2 or later (the "GPL"),
26 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 : * in which case the provisions of the GPL or the LGPL are applicable instead
28 : * of those above. If you wish to allow use of your version of this file only
29 : * under the terms of either the GPL or the LGPL, and not to allow others to
30 : * use your version of this file under the terms of the MPL, indicate your
31 : * decision by deleting the provisions above and replace them with the notice
32 : * and other provisions required by the GPL or the LGPL. If you do not delete
33 : * the provisions above, a recipient may use your version of this file under
34 : * the terms of any one of the MPL, the GPL or the LGPL.
35 : *
36 : * ***** END LICENSE BLOCK ***** */
37 :
38 : #include "nsAutoPtr.h"
39 : #include "nsScriptLoader.h"
40 :
41 : #include "jsapi.h"
42 : #include "jsxdrapi.h"
43 :
44 : #include "mozilla/scache/StartupCache.h"
45 : #include "mozilla/scache/StartupCacheUtils.h"
46 :
47 : using namespace mozilla::scache;
48 :
49 : static nsresult
50 642 : ReadScriptFromStream(JSContext *cx, nsIObjectInputStream *stream,
51 : JSScript **script)
52 : {
53 642 : *script = nsnull;
54 :
55 : PRUint32 size;
56 642 : nsresult rv = stream->Read32(&size);
57 642 : NS_ENSURE_SUCCESS(rv, rv);
58 :
59 : char *data;
60 642 : rv = stream->ReadBytes(size, &data);
61 642 : NS_ENSURE_SUCCESS(rv, rv);
62 :
63 642 : JSXDRState *xdr = JS_XDRNewMem(cx, JSXDR_DECODE);
64 642 : NS_ENSURE_TRUE(xdr, NS_ERROR_OUT_OF_MEMORY);
65 :
66 642 : xdr->userdata = stream;
67 642 : JS_XDRMemSetData(xdr, data, size);
68 :
69 642 : if (!JS_XDRScript(xdr, script)) {
70 0 : rv = NS_ERROR_FAILURE;
71 : }
72 :
73 : // Update data in case ::JS_XDRScript called back into C++ code to
74 : // read an XPCOM object.
75 : //
76 : // In that case, the serialization process must have flushed a run
77 : // of counted bytes containing JS data at the point where the XPCOM
78 : // object starts, after which an encoding C++ callback from the JS
79 : // XDR code must have written the XPCOM object directly into the
80 : // nsIObjectOutputStream.
81 : //
82 : // The deserialization process will XDR-decode counted bytes up to
83 : // but not including the XPCOM object, then call back into C++ to
84 : // read the object, then read more counted bytes and hand them off
85 : // to the JSXDRState, so more JS data can be decoded.
86 : //
87 : // This interleaving of JS XDR data and XPCOM object data may occur
88 : // several times beneath the call to ::JS_XDRScript, above. At the
89 : // end of the day, we need to free (via nsMemory) the data owned by
90 : // the JSXDRState. So we steal it back, nulling xdr's buffer so it
91 : // doesn't get passed to ::JS_free by ::JS_XDRDestroy.
92 :
93 : uint32_t length;
94 642 : data = static_cast<char*>(JS_XDRMemGetData(xdr, &length));
95 642 : JS_XDRMemSetData(xdr, nsnull, 0);
96 642 : JS_XDRDestroy(xdr);
97 :
98 : // If data is null now, it must have been freed while deserializing an
99 : // XPCOM object (e.g., a principal) beneath ::JS_XDRScript.
100 642 : nsMemory::Free(data);
101 :
102 642 : return rv;
103 : }
104 :
105 : static nsresult
106 7375 : WriteScriptToStream(JSContext *cx, JSScript *script,
107 : nsIObjectOutputStream *stream)
108 : {
109 7375 : JSXDRState *xdr = JS_XDRNewMem(cx, JSXDR_ENCODE);
110 7375 : NS_ENSURE_TRUE(xdr, NS_ERROR_OUT_OF_MEMORY);
111 :
112 7375 : xdr->userdata = stream;
113 7375 : nsresult rv = NS_OK;
114 :
115 7375 : if (JS_XDRScript(xdr, &script)) {
116 : // Get the encoded JSXDRState data and write it. The JSXDRState owns
117 : // this buffer memory and will free it beneath ::JS_XDRDestroy.
118 : //
119 : // If an XPCOM object needs to be written in the midst of the JS XDR
120 : // encoding process, the C++ code called back from the JS engine (e.g.,
121 : // nsEncodeJSPrincipals in caps/src/nsJSPrincipals.cpp) will flush data
122 : // from the JSXDRState to aStream, then write the object, then return
123 : // to JS XDR code with xdr reset so new JS data is encoded at the front
124 : // of the xdr's data buffer.
125 : //
126 : // However many XPCOM objects are interleaved with JS XDR data in the
127 : // stream, when control returns here from ::JS_XDRScript, we'll have
128 : // one last buffer of data to write to aStream.
129 :
130 : uint32_t size;
131 : const char* data = reinterpret_cast<const char*>
132 7375 : (JS_XDRMemGetData(xdr, &size));
133 7375 : NS_ASSERTION(data, "no decoded JSXDRState data!");
134 :
135 7375 : rv = stream->Write32(size);
136 7375 : if (NS_SUCCEEDED(rv)) {
137 7375 : rv = stream->WriteBytes(data, size);
138 : }
139 : } else {
140 0 : rv = NS_ERROR_FAILURE; // likely to be a principals serialization error
141 : }
142 :
143 7375 : JS_XDRDestroy(xdr);
144 7375 : return rv;
145 : }
146 :
147 : nsresult
148 8100 : ReadCachedScript(StartupCache* cache, nsACString &uri, JSContext *cx, JSScript **script)
149 : {
150 : nsresult rv;
151 :
152 16200 : nsAutoArrayPtr<char> buf;
153 : PRUint32 len;
154 8100 : rv = cache->GetBuffer(PromiseFlatCString(uri).get(), getter_Transfers(buf),
155 8100 : &len);
156 8100 : if (NS_FAILED(rv)) {
157 7458 : return rv; // don't warn since NOT_AVAILABLE is an ok error
158 : }
159 :
160 1284 : nsCOMPtr<nsIObjectInputStream> ois;
161 642 : rv = NewObjectInputStreamFromBuffer(buf, len, getter_AddRefs(ois));
162 642 : NS_ENSURE_SUCCESS(rv, rv);
163 642 : buf.forget();
164 :
165 642 : return ReadScriptFromStream(cx, ois, script);
166 : }
167 :
168 : nsresult
169 7375 : WriteCachedScript(StartupCache* cache, nsACString &uri, JSContext *cx, JSScript *script)
170 : {
171 : nsresult rv;
172 :
173 14750 : nsCOMPtr<nsIObjectOutputStream> oos;
174 14750 : nsCOMPtr<nsIStorageStream> storageStream;
175 7375 : rv = NewObjectOutputWrappedStorageStream(getter_AddRefs(oos),
176 7375 : getter_AddRefs(storageStream),
177 7375 : true);
178 7375 : NS_ENSURE_SUCCESS(rv, rv);
179 :
180 7375 : rv = WriteScriptToStream(cx, script, oos);
181 7375 : oos->Close();
182 7375 : NS_ENSURE_SUCCESS(rv, rv);
183 :
184 14750 : nsAutoArrayPtr<char> buf;
185 : PRUint32 len;
186 7375 : rv = NewBufferFromStorageStream(storageStream, getter_Transfers(buf), &len);
187 7375 : NS_ENSURE_SUCCESS(rv, rv);
188 :
189 7375 : rv = cache->PutBuffer(PromiseFlatCString(uri).get(), buf, len);
190 7375 : return rv;
191 : }
|