]> git.donarmstrong.com Git - dactyl.git/blob - binary/src/mozJSLoaderUtils.cpp
Import 1.0rc1 supporting Firefox up to 11.*
[dactyl.git] / binary / src / mozJSLoaderUtils.cpp
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 "mozJSLoaderUtils.h"
39 #include "nsAutoPtr.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 #include "nsIChromeRegistry.h"
48 #include "nsIIOService.h"
49 #include "nsIResProtocolHandler.h"
50 #include "nsNetUtil.h"
51
52 using namespace mozilla::scache;
53
54 static nsresult
55 ReadScriptFromStream(JSContext *cx, nsIObjectInputStream *stream,
56                      JSScriptType **script)
57 {
58     *script = nsnull;
59
60     PRUint32 size;
61     nsresult rv = stream->Read32(&size);
62     NS_ENSURE_SUCCESS(rv, rv);
63
64     char *data;
65     rv = stream->ReadBytes(size, &data);
66     NS_ENSURE_SUCCESS(rv, rv);
67
68     JSXDRState *xdr = JS_XDRNewMem(cx, JSXDR_DECODE);
69     NS_ENSURE_TRUE(xdr, NS_ERROR_OUT_OF_MEMORY);
70
71     xdr->userdata = stream;
72     JS_XDRMemSetData(xdr, data, size);
73
74     if (!JS_XDRScript(xdr, script)) {
75         rv = NS_ERROR_FAILURE;
76     }
77
78     // Update data in case ::JS_XDRScript called back into C++ code to
79     // read an XPCOM object.
80     //
81     // In that case, the serialization process must have flushed a run
82     // of counted bytes containing JS data at the point where the XPCOM
83     // object starts, after which an encoding C++ callback from the JS
84     // XDR code must have written the XPCOM object directly into the
85     // nsIObjectOutputStream.
86     //
87     // The deserialization process will XDR-decode counted bytes up to
88     // but not including the XPCOM object, then call back into C++ to
89     // read the object, then read more counted bytes and hand them off
90     // to the JSXDRState, so more JS data can be decoded.
91     //
92     // This interleaving of JS XDR data and XPCOM object data may occur
93     // several times beneath the call to ::JS_XDRScript, above.  At the
94     // end of the day, we need to free (via nsMemory) the data owned by
95     // the JSXDRState.  So we steal it back, nulling xdr's buffer so it
96     // doesn't get passed to ::JS_free by ::JS_XDRDestroy.
97
98     uint32 length;
99     data = static_cast<char*>(JS_XDRMemGetData(xdr, &length));
100     JS_XDRMemSetData(xdr, nsnull, 0);
101     JS_XDRDestroy(xdr);
102
103     // If data is null now, it must have been freed while deserializing an
104     // XPCOM object (e.g., a principal) beneath ::JS_XDRScript.
105     nsMemory::Free(data);
106
107     return rv;
108 }
109
110 static nsresult
111 WriteScriptToStream(JSContext *cx, JSScriptType *script,
112                     nsIObjectOutputStream *stream)
113 {
114     JSXDRState *xdr = JS_XDRNewMem(cx, JSXDR_ENCODE);
115     NS_ENSURE_TRUE(xdr, NS_ERROR_OUT_OF_MEMORY);
116
117     xdr->userdata = stream;
118     nsresult rv = NS_OK;
119
120     if (JS_XDRScript(xdr, &script)) {
121         // Get the encoded JSXDRState data and write it.  The JSXDRState owns
122         // this buffer memory and will free it beneath ::JS_XDRDestroy.
123         //
124         // If an XPCOM object needs to be written in the midst of the JS XDR
125         // encoding process, the C++ code called back from the JS engine (e.g.,
126         // nsEncodeJSPrincipals in caps/src/nsJSPrincipals.cpp) will flush data
127         // from the JSXDRState to aStream, then write the object, then return
128         // to JS XDR code with xdr reset so new JS data is encoded at the front
129         // of the xdr's data buffer.
130         //
131         // However many XPCOM objects are interleaved with JS XDR data in the
132         // stream, when control returns here from ::JS_XDRScript, we'll have
133         // one last buffer of data to write to aStream.
134
135         uint32 size;
136         const char* data = reinterpret_cast<const char*>
137                                            (JS_XDRMemGetData(xdr, &size));
138         NS_ASSERTION(data, "no decoded JSXDRState data!");
139
140         rv = stream->Write32(size);
141         if (NS_SUCCEEDED(rv)) {
142             rv = stream->WriteBytes(data, size);
143         }
144     } else {
145         rv = NS_ERROR_FAILURE; // likely to be a principals serialization error
146     }
147
148     JS_XDRDestroy(xdr);
149     return rv;
150 }
151
152 nsresult
153 ReadCachedScript(nsIStartupCache* cache, nsACString &uri, JSContext *cx, JSScriptType **script)
154 {
155     nsresult rv;
156
157     nsAutoArrayPtr<char> buf;
158     PRUint32 len;
159     rv = cache->GetBuffer(PromiseFlatCString(uri).get(), getter_Transfers(buf),
160                           &len);
161     if (NS_FAILED(rv)) {
162         return rv; // don't warn since NOT_AVAILABLE is an ok error
163     }
164
165     nsCOMPtr<nsIObjectInputStream> ois;
166     rv = NewObjectInputStreamFromBuffer(buf, len, getter_AddRefs(ois));
167     NS_ENSURE_SUCCESS(rv, rv);
168     buf.forget();
169
170     return ReadScriptFromStream(cx, ois, script);
171 }
172
173 nsresult
174 WriteCachedScript(nsIStartupCache* cache, nsACString &uri, JSContext *cx, JSScriptType *script)
175 {
176     nsresult rv;
177
178     nsCOMPtr<nsIObjectOutputStream> oos;
179     nsCOMPtr<nsIStorageStream> storageStream;
180     rv = NewObjectOutputWrappedStorageStream(getter_AddRefs(oos),
181                                              getter_AddRefs(storageStream),
182                                              true);
183     NS_ENSURE_SUCCESS(rv, rv);
184
185     rv = WriteScriptToStream(cx, script, oos);
186     oos->Close();
187     NS_ENSURE_SUCCESS(rv, rv);
188
189     nsAutoArrayPtr<char> buf;
190     PRUint32 len;
191     rv = NewBufferFromStorageStream(storageStream, getter_Transfers(buf), &len);
192     NS_ENSURE_SUCCESS(rv, rv);
193
194     rv = cache->PutBuffer(PromiseFlatCString(uri).get(), buf, len);
195     return rv;
196 }