Merge XFA to PDFium master at 4dc95e7 on 10/28/2014
[pdfium.git] / xfa_test / process / process.cc
1 // Copyright 2012 the V8 project authors. All rights reserved.\r
2 // Redistribution and use in source and binary forms, with or without\r
3 // modification, are permitted provided that the following conditions are\r
4 // met:\r
5 //\r
6 //     * Redistributions of source code must retain the above copyright\r
7 //       notice, this list of conditions and the following disclaimer.\r
8 //     * Redistributions in binary form must reproduce the above\r
9 //       copyright notice, this list of conditions and the following\r
10 //       disclaimer in the documentation and/or other materials provided\r
11 //       with the distribution.\r
12 //     * Neither the name of Google Inc. nor the names of its\r
13 //       contributors may be used to endorse or promote products derived\r
14 //       from this software without specific prior written permission.\r
15 //\r
16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\r
17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\r
18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\r
19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\r
20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\r
21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\r
22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\r
23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\r
24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\r
25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\r
26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r
27 \r
28 #include <v8.h>\r
29 \r
30 #include <string>\r
31 #include <map>\r
32 \r
33 #ifdef COMPRESS_STARTUP_DATA_BZ2\r
34 #error Using compressed startup data is not supported for this sample\r
35 #endif\r
36 \r
37 using namespace std;\r
38 using namespace v8;\r
39 \r
40 // These interfaces represent an existing request processing interface.\r
41 // The idea is to imagine a real application that uses these interfaces\r
42 // and then add scripting capabilities that allow you to interact with\r
43 // the objects through JavaScript.\r
44 \r
45 /**\r
46  * A simplified http request.\r
47  */\r
48 class HttpRequest {\r
49  public:\r
50   virtual ~HttpRequest() { }\r
51   virtual const string& Path() = 0;\r
52   virtual const string& Referrer() = 0;\r
53   virtual const string& Host() = 0;\r
54   virtual const string& UserAgent() = 0;\r
55 };\r
56 \r
57 \r
58 /**\r
59  * The abstract superclass of http request processors.\r
60  */\r
61 class HttpRequestProcessor {\r
62  public:\r
63   virtual ~HttpRequestProcessor() { }\r
64 \r
65   // Initialize this processor.  The map contains options that control\r
66   // how requests should be processed.\r
67   virtual bool Initialize(map<string, string>* options,\r
68                           map<string, string>* output) = 0;\r
69 \r
70   // Process a single request.\r
71   virtual bool Process(HttpRequest* req) = 0;\r
72 \r
73   static void Log(const char* event);\r
74 };\r
75 \r
76 \r
77 /**\r
78  * An http request processor that is scriptable using JavaScript.\r
79  */\r
80 class JsHttpRequestProcessor : public HttpRequestProcessor {\r
81  public:\r
82   // Creates a new processor that processes requests by invoking the\r
83   // Process function of the JavaScript script given as an argument.\r
84   JsHttpRequestProcessor(Isolate* isolate, Handle<String> script)\r
85       : isolate_(isolate), script_(script) { }\r
86   virtual ~JsHttpRequestProcessor();\r
87 \r
88   virtual bool Initialize(map<string, string>* opts,\r
89                           map<string, string>* output);\r
90   virtual bool Process(HttpRequest* req);\r
91 \r
92  private:\r
93   // Execute the script associated with this processor and extract the\r
94   // Process function.  Returns true if this succeeded, otherwise false.\r
95   bool ExecuteScript(Handle<String> script);\r
96 \r
97   // Wrap the options and output map in a JavaScript objects and\r
98   // install it in the global namespace as 'options' and 'output'.\r
99   bool InstallMaps(map<string, string>* opts, map<string, string>* output);\r
100 \r
101   // Constructs the template that describes the JavaScript wrapper\r
102   // type for requests.\r
103   static Handle<ObjectTemplate> MakeRequestTemplate(Isolate* isolate);\r
104   static Handle<ObjectTemplate> MakeMapTemplate(Isolate* isolate);\r
105 \r
106   // Callbacks that access the individual fields of request objects.\r
107   static void GetPath(Local<String> name,\r
108                       const PropertyCallbackInfo<Value>& info);\r
109   static void GetReferrer(Local<String> name,\r
110                           const PropertyCallbackInfo<Value>& info);\r
111   static void GetHost(Local<String> name,\r
112                       const PropertyCallbackInfo<Value>& info);\r
113   static void GetUserAgent(Local<String> name,\r
114                            const PropertyCallbackInfo<Value>& info);\r
115 \r
116   // Callbacks that access maps\r
117   static void MapGet(Local<String> name,\r
118                      const PropertyCallbackInfo<Value>& info);\r
119   static void MapSet(Local<String> name,\r
120                      Local<Value> value,\r
121                      const PropertyCallbackInfo<Value>& info);\r
122 \r
123   // Utility methods for wrapping C++ objects as JavaScript objects,\r
124   // and going back again.\r
125   Handle<Object> WrapMap(map<string, string>* obj);\r
126   static map<string, string>* UnwrapMap(Handle<Object> obj);\r
127   Handle<Object> WrapRequest(HttpRequest* obj);\r
128   static HttpRequest* UnwrapRequest(Handle<Object> obj);\r
129 \r
130   Isolate* GetIsolate() { return isolate_; }\r
131 \r
132   Isolate* isolate_;\r
133   Handle<String> script_;\r
134   Persistent<Context> context_;\r
135   Persistent<Function> process_;\r
136   static Persistent<ObjectTemplate> request_template_;\r
137   static Persistent<ObjectTemplate> map_template_;\r
138 };\r
139 \r
140 \r
141 // -------------------------\r
142 // --- P r o c e s s o r ---\r
143 // -------------------------\r
144 \r
145 \r
146 static void LogCallback(const v8::FunctionCallbackInfo<v8::Value>& args) {\r
147   if (args.Length() < 1) return;\r
148   HandleScope scope(args.GetIsolate());\r
149   Handle<Value> arg = args[0];\r
150   String::Utf8Value value(arg);\r
151   HttpRequestProcessor::Log(*value);\r
152 }\r
153 \r
154 static void MethodCallback(const v8::FunctionCallbackInfo<v8::Value>& args) {\r
155         if (args.Length() < 1) return;\r
156         HandleScope scope(args.GetIsolate());\r
157         Handle<Value> arg = args[0];\r
158         String::Utf8Value value(arg);\r
159         HttpRequestProcessor::Log(*value);\r
160 }\r
161 \r
162 static void MyJSObjectCallback(const v8::FunctionCallbackInfo<v8::Value>& args) {\r
163 \r
164         HandleScope scope(args.GetIsolate());\r
165         if(args.IsConstructCall())\r
166         {\r
167                 //args.GetReturnValue().Set(String::NewFromUtf8(\r
168                 //      args.GetIsolate(), "HelloWorld", String::kNormalString,\r
169                 //      static_cast<int>(10)));\r
170                 args.This();\r
171         }\r
172 }\r
173 \r
174 void Getter(\r
175         Local<String> property,\r
176         const PropertyCallbackInfo<Value>& info)\r
177 {\r
178 //      info.GetReturnValue().Set(String::NewFromUtf8(\r
179 //              info.GetIsolate(), "HelloWorld", String::kNormalString,\r
180 //              static_cast<int>(10)));\r
181 }\r
182 \r
183 \r
184 // Execute the script and fetch the Process method.\r
185 bool JsHttpRequestProcessor::Initialize(map<string, string>* opts,\r
186                                         map<string, string>* output) {\r
187   // Create a handle scope to hold the temporary references.\r
188   HandleScope handle_scope(GetIsolate());\r
189 \r
190   // Create a template for the global object where we set the\r
191   // built-in global functions.\r
192   Handle<ObjectTemplate> global = ObjectTemplate::New(GetIsolate());\r
193   global->Set(String::NewFromUtf8(GetIsolate(), "log"),\r
194               FunctionTemplate::New(GetIsolate(), LogCallback));\r
195 \r
196 \r
197 //   Local<FunctionTemplate> funJSObj = FunctionTemplate::New(GetIsolate(), MyJSObjectCallback);\r
198 //   funJSObj->InstanceTemplate()->SetAccessor(String::NewFromUtf8(GetIsolate(), "activeDocs"), Getter, 0, Handle<Value>(), ALL_CAN_READ|PROHIBITS_OVERWRITING);\r
199 //   funJSObj->InstanceTemplate()->Set(String::NewFromUtf8(GetIsolate(), "method1"),FunctionTemplate::New(GetIsolate(), MethodCallback));\r
200 // \r
201 //   global->Set(String::NewFromUtf8(GetIsolate(), "app"), funJSObj);\r
202 \r
203   // Each processor gets its own context so different processors don't\r
204   // affect each other. Context::New returns a persistent handle which\r
205   // is what we need for the reference to remain after we return from\r
206   // this method. That persistent handle has to be disposed in the\r
207   // destructor.\r
208   v8::Handle<v8::Context> context = Context::New(GetIsolate(), NULL, global);\r
209   context_.Reset(GetIsolate(), context);\r
210 \r
211   // Enter the new context so all the following operations take place\r
212   // within it.\r
213   Context::Scope context_scope(context);\r
214 \r
215   Local<ObjectTemplate> ObjTemp = ObjectTemplate::New(GetIsolate());\r
216   ObjTemp->SetAccessor(String::NewFromUtf8(GetIsolate(), "activeDocs"), Getter, 0, Handle<Value>(), PROHIBITS_OVERWRITING);\r
217   Local<Object> ins =  ObjTemp->NewInstance();\r
218   context->Global()->Set(String::NewFromUtf8(GetIsolate(), "app"), ins);\r
219 \r
220 \r
221   // Make the options mapping available within the context\r
222   if (!InstallMaps(opts, output))\r
223     return false;\r
224 \r
225   // Compile and run the script\r
226   if (!ExecuteScript(script_))\r
227     return false;\r
228 \r
229   // The script compiled and ran correctly.  Now we fetch out the\r
230   // Process function from the global object.\r
231   Handle<String> process_name = String::NewFromUtf8(GetIsolate(), "Process");\r
232   Handle<Value> process_val = context->Global()->Get(process_name);\r
233 \r
234   // If there is no Process function, or if it is not a function,\r
235   // bail out\r
236   if (!process_val->IsFunction()) return false;\r
237 \r
238   // It is a function; cast it to a Function\r
239   Handle<Function> process_fun = Handle<Function>::Cast(process_val);\r
240 \r
241   // Store the function in a Persistent handle, since we also want\r
242   // that to remain after this call returns\r
243   process_.Reset(GetIsolate(), process_fun);\r
244 \r
245   // All done; all went well\r
246   return true;\r
247 }\r
248 \r
249 \r
250 bool JsHttpRequestProcessor::ExecuteScript(Handle<String> script) {\r
251   HandleScope handle_scope(GetIsolate());\r
252 \r
253   // We're just about to compile the script; set up an error handler to\r
254   // catch any exceptions the script might throw.\r
255   TryCatch try_catch;\r
256 \r
257   // Compile the script and check for errors.\r
258   Handle<Script> compiled_script = Script::Compile(script);\r
259   if (compiled_script.IsEmpty()) {\r
260     String::Utf8Value error(try_catch.Exception());\r
261     Log(*error);\r
262     // The script failed to compile; bail out.\r
263     return false;\r
264   }\r
265 \r
266   // Run the script!\r
267   Handle<Value> result = compiled_script->Run();\r
268   if (result.IsEmpty()) {\r
269     // The TryCatch above is still in effect and will have caught the error.\r
270     String::Utf8Value error(try_catch.Exception());\r
271     Log(*error);\r
272     // Running the script failed; bail out.\r
273     return false;\r
274   }\r
275   return true;\r
276 }\r
277 \r
278 \r
279 bool JsHttpRequestProcessor::InstallMaps(map<string, string>* opts,\r
280                                          map<string, string>* output) {\r
281   HandleScope handle_scope(GetIsolate());\r
282 \r
283   // Wrap the map object in a JavaScript wrapper\r
284   Handle<Object> opts_obj = WrapMap(opts);\r
285 \r
286   v8::Local<v8::Context> context =\r
287       v8::Local<v8::Context>::New(GetIsolate(), context_);\r
288 \r
289   // Set the options object as a property on the global object.\r
290   context->Global()->Set(String::NewFromUtf8(GetIsolate(), "options"),\r
291                          opts_obj);\r
292 \r
293   Handle<Object> output_obj = WrapMap(output);\r
294   context->Global()->Set(String::NewFromUtf8(GetIsolate(), "output"),\r
295                          output_obj);\r
296 \r
297   return true;\r
298 }\r
299 \r
300 \r
301 bool JsHttpRequestProcessor::Process(HttpRequest* request) {\r
302   // Create a handle scope to keep the temporary object references.\r
303   HandleScope handle_scope(GetIsolate());\r
304 \r
305   v8::Local<v8::Context> context =\r
306       v8::Local<v8::Context>::New(GetIsolate(), context_);\r
307 \r
308   // Enter this processor's context so all the remaining operations\r
309   // take place there\r
310   Context::Scope context_scope(context);\r
311 \r
312   // Wrap the C++ request object in a JavaScript wrapper\r
313   Handle<Object> request_obj = WrapRequest(request);\r
314 \r
315   // Set up an exception handler before calling the Process function\r
316   TryCatch try_catch;\r
317 \r
318   // Invoke the process function, giving the global object as 'this'\r
319   // and one argument, the request.\r
320   const int argc = 1;\r
321   Handle<Value> argv[argc] = { request_obj };\r
322   v8::Local<v8::Function> process =\r
323       v8::Local<v8::Function>::New(GetIsolate(), process_);\r
324   Handle<Value> result = process->Call(context->Global(), argc, argv);\r
325   if (result.IsEmpty()) {\r
326     String::Utf8Value error(try_catch.Exception());\r
327     Log(*error);\r
328     return false;\r
329   } else {\r
330     return true;\r
331   }\r
332 }\r
333 \r
334 \r
335 JsHttpRequestProcessor::~JsHttpRequestProcessor() {\r
336   // Dispose the persistent handles.  When noone else has any\r
337   // references to the objects stored in the handles they will be\r
338   // automatically reclaimed.\r
339   context_.Reset();\r
340   process_.Reset();\r
341 }\r
342 \r
343 \r
344 Persistent<ObjectTemplate> JsHttpRequestProcessor::request_template_;\r
345 Persistent<ObjectTemplate> JsHttpRequestProcessor::map_template_;\r
346 \r
347 \r
348 // -----------------------------------\r
349 // --- A c c e s s i n g   M a p s ---\r
350 // -----------------------------------\r
351 \r
352 // Utility function that wraps a C++ http request object in a\r
353 // JavaScript object.\r
354 Handle<Object> JsHttpRequestProcessor::WrapMap(map<string, string>* obj) {\r
355   // Handle scope for temporary handles.\r
356   EscapableHandleScope handle_scope(GetIsolate());\r
357 \r
358   // Fetch the template for creating JavaScript map wrappers.\r
359   // It only has to be created once, which we do on demand.\r
360   if (map_template_.IsEmpty()) {\r
361     Handle<ObjectTemplate> raw_template = MakeMapTemplate(GetIsolate());\r
362     map_template_.Reset(GetIsolate(), raw_template);\r
363   }\r
364   Handle<ObjectTemplate> templ =\r
365       Local<ObjectTemplate>::New(GetIsolate(), map_template_);\r
366 \r
367   // Create an empty map wrapper.\r
368   Local<Object> result = templ->NewInstance();\r
369 \r
370   // Wrap the raw C++ pointer in an External so it can be referenced\r
371   // from within JavaScript.\r
372   Handle<External> map_ptr = External::New(GetIsolate(), obj);\r
373 \r
374   // Store the map pointer in the JavaScript wrapper.\r
375   result->SetInternalField(0, map_ptr);\r
376 \r
377   // Return the result through the current handle scope.  Since each\r
378   // of these handles will go away when the handle scope is deleted\r
379   // we need to call Close to let one, the result, escape into the\r
380   // outer handle scope.\r
381   return handle_scope.Escape(result);\r
382 }\r
383 \r
384 \r
385 // Utility function that extracts the C++ map pointer from a wrapper\r
386 // object.\r
387 map<string, string>* JsHttpRequestProcessor::UnwrapMap(Handle<Object> obj) {\r
388   Handle<External> field = Handle<External>::Cast(obj->GetInternalField(0));\r
389   void* ptr = field->Value();\r
390   return static_cast<map<string, string>*>(ptr);\r
391 }\r
392 \r
393 \r
394 // Convert a JavaScript string to a std::string.  To not bother too\r
395 // much with string encodings we just use ascii.\r
396 string ObjectToString(Local<Value> value) {\r
397   String::Utf8Value utf8_value(value);\r
398   return string(*utf8_value);\r
399 }\r
400 \r
401 \r
402 void JsHttpRequestProcessor::MapGet(Local<String> name,\r
403                                     const PropertyCallbackInfo<Value>& info) {\r
404   // Fetch the map wrapped by this object.\r
405   map<string, string>* obj = UnwrapMap(info.Holder());\r
406 \r
407   // Convert the JavaScript string to a std::string.\r
408   string key = ObjectToString(name);\r
409 \r
410   // Look up the value if it exists using the standard STL ideom.\r
411   map<string, string>::iterator iter = obj->find(key);\r
412 \r
413   // If the key is not present return an empty handle as signal\r
414   if (iter == obj->end()) return;\r
415 \r
416   // Otherwise fetch the value and wrap it in a JavaScript string\r
417   const string& value = (*iter).second;\r
418   info.GetReturnValue().Set(String::NewFromUtf8(\r
419       info.GetIsolate(), value.c_str(), String::kNormalString,\r
420       static_cast<int>(value.length())));\r
421 }\r
422 \r
423 \r
424 void JsHttpRequestProcessor::MapSet(Local<String> name,\r
425                                     Local<Value> value_obj,\r
426                                     const PropertyCallbackInfo<Value>& info) {\r
427   // Fetch the map wrapped by this object.\r
428   map<string, string>* obj = UnwrapMap(info.Holder());\r
429 \r
430   // Convert the key and value to std::strings.\r
431   string key = ObjectToString(name);\r
432   string value = ObjectToString(value_obj);\r
433 \r
434   // Update the map.\r
435   (*obj)[key] = value;\r
436 \r
437   // Return the value; any non-empty handle will work.\r
438   info.GetReturnValue().Set(value_obj);\r
439 }\r
440 \r
441 \r
442 Handle<ObjectTemplate> JsHttpRequestProcessor::MakeMapTemplate(\r
443     Isolate* isolate) {\r
444   EscapableHandleScope handle_scope(isolate);\r
445 \r
446   Local<ObjectTemplate> result = ObjectTemplate::New(isolate);\r
447   result->SetInternalFieldCount(1);\r
448   result->SetNamedPropertyHandler(MapGet, MapSet);\r
449 \r
450   // Again, return the result through the current handle scope.\r
451   return handle_scope.Escape(result);\r
452 }\r
453 \r
454 \r
455 // -------------------------------------------\r
456 // --- A c c e s s i n g   R e q u e s t s ---\r
457 // -------------------------------------------\r
458 \r
459 /**\r
460  * Utility function that wraps a C++ http request object in a\r
461  * JavaScript object.\r
462  */\r
463 Handle<Object> JsHttpRequestProcessor::WrapRequest(HttpRequest* request) {\r
464   // Handle scope for temporary handles.\r
465   EscapableHandleScope handle_scope(GetIsolate());\r
466 \r
467   // Fetch the template for creating JavaScript http request wrappers.\r
468   // It only has to be created once, which we do on demand.\r
469   if (request_template_.IsEmpty()) {\r
470     Handle<ObjectTemplate> raw_template = MakeRequestTemplate(GetIsolate());\r
471     request_template_.Reset(GetIsolate(), raw_template);\r
472   }\r
473   Handle<ObjectTemplate> templ =\r
474       Local<ObjectTemplate>::New(GetIsolate(), request_template_);\r
475 \r
476   // Create an empty http request wrapper.\r
477   Local<Object> result = templ->NewInstance();\r
478 \r
479   // Wrap the raw C++ pointer in an External so it can be referenced\r
480   // from within JavaScript.\r
481   Handle<External> request_ptr = External::New(GetIsolate(), request);\r
482 \r
483   // Store the request pointer in the JavaScript wrapper.\r
484   result->SetInternalField(0, request_ptr);\r
485 \r
486   // Return the result through the current handle scope.  Since each\r
487   // of these handles will go away when the handle scope is deleted\r
488   // we need to call Close to let one, the result, escape into the\r
489   // outer handle scope.\r
490   return handle_scope.Escape(result);\r
491 }\r
492 \r
493 \r
494 /**\r
495  * Utility function that extracts the C++ http request object from a\r
496  * wrapper object.\r
497  */\r
498 HttpRequest* JsHttpRequestProcessor::UnwrapRequest(Handle<Object> obj) {\r
499   Handle<External> field = Handle<External>::Cast(obj->GetInternalField(0));\r
500   void* ptr = field->Value();\r
501   return static_cast<HttpRequest*>(ptr);\r
502 }\r
503 \r
504 \r
505 void JsHttpRequestProcessor::GetPath(Local<String> name,\r
506                                      const PropertyCallbackInfo<Value>& info) {\r
507   // Extract the C++ request object from the JavaScript wrapper.\r
508   HttpRequest* request = UnwrapRequest(info.Holder());\r
509 \r
510   // Fetch the path.\r
511   const string& path = request->Path();\r
512 \r
513   // Wrap the result in a JavaScript string and return it.\r
514   info.GetReturnValue().Set(String::NewFromUtf8(\r
515       info.GetIsolate(), path.c_str(), String::kNormalString,\r
516       static_cast<int>(path.length())));\r
517 }\r
518 \r
519 \r
520 void JsHttpRequestProcessor::GetReferrer(\r
521     Local<String> name,\r
522     const PropertyCallbackInfo<Value>& info) {\r
523   HttpRequest* request = UnwrapRequest(info.Holder());\r
524   const string& path = request->Referrer();\r
525   info.GetReturnValue().Set(String::NewFromUtf8(\r
526       info.GetIsolate(), path.c_str(), String::kNormalString,\r
527       static_cast<int>(path.length())));\r
528 }\r
529 \r
530 \r
531 void JsHttpRequestProcessor::GetHost(Local<String> name,\r
532                                      const PropertyCallbackInfo<Value>& info) {\r
533   HttpRequest* request = UnwrapRequest(info.Holder());\r
534   const string& path = request->Host();\r
535   info.GetReturnValue().Set(String::NewFromUtf8(\r
536       info.GetIsolate(), path.c_str(), String::kNormalString,\r
537       static_cast<int>(path.length())));\r
538 }\r
539 \r
540 \r
541 void JsHttpRequestProcessor::GetUserAgent(\r
542     Local<String> name,\r
543     const PropertyCallbackInfo<Value>& info) {\r
544   HttpRequest* request = UnwrapRequest(info.Holder());\r
545   const string& path = request->UserAgent();\r
546   info.GetReturnValue().Set(String::NewFromUtf8(\r
547       info.GetIsolate(), path.c_str(), String::kNormalString,\r
548       static_cast<int>(path.length())));\r
549 }\r
550 \r
551 \r
552 Handle<ObjectTemplate> JsHttpRequestProcessor::MakeRequestTemplate(\r
553     Isolate* isolate) {\r
554   EscapableHandleScope handle_scope(isolate);\r
555 \r
556   Local<ObjectTemplate> result = ObjectTemplate::New(isolate);\r
557   result->SetInternalFieldCount(1);\r
558 \r
559   // Add accessors for each of the fields of the request.\r
560   result->SetAccessor(\r
561       String::NewFromUtf8(isolate, "path", String::kInternalizedString),\r
562       GetPath);\r
563   result->SetAccessor(\r
564       String::NewFromUtf8(isolate, "referrer", String::kInternalizedString),\r
565       GetReferrer);\r
566   result->SetAccessor(\r
567       String::NewFromUtf8(isolate, "host", String::kInternalizedString),\r
568       GetHost);\r
569   result->SetAccessor(\r
570       String::NewFromUtf8(isolate, "userAgent", String::kInternalizedString),\r
571       GetUserAgent);\r
572 \r
573   // Again, return the result through the current handle scope.\r
574   return handle_scope.Escape(result);\r
575 }\r
576 \r
577 \r
578 // --- Test ---\r
579 \r
580 \r
581 void HttpRequestProcessor::Log(const char* event) {\r
582   printf("Logged: %s\n", event);\r
583 }\r
584 \r
585 \r
586 /**\r
587  * A simplified http request.\r
588  */\r
589 class StringHttpRequest : public HttpRequest {\r
590  public:\r
591   StringHttpRequest(const string& path,\r
592                     const string& referrer,\r
593                     const string& host,\r
594                     const string& user_agent);\r
595   virtual const string& Path() { return path_; }\r
596   virtual const string& Referrer() { return referrer_; }\r
597   virtual const string& Host() { return host_; }\r
598   virtual const string& UserAgent() { return user_agent_; }\r
599  private:\r
600   string path_;\r
601   string referrer_;\r
602   string host_;\r
603   string user_agent_;\r
604 };\r
605 \r
606 \r
607 StringHttpRequest::StringHttpRequest(const string& path,\r
608                                      const string& referrer,\r
609                                      const string& host,\r
610                                      const string& user_agent)\r
611     : path_(path),\r
612       referrer_(referrer),\r
613       host_(host),\r
614       user_agent_(user_agent) { }\r
615 \r
616 \r
617 void ParseOptions(int argc,\r
618                   char* argv[],\r
619                   map<string, string>& options,\r
620                   string* file) {\r
621   for (int i = 1; i < argc; i++) {\r
622     string arg = argv[i];\r
623     size_t index = arg.find('=', 0);\r
624     if (index == string::npos) {\r
625       *file = arg;\r
626     } else {\r
627       string key = arg.substr(0, index);\r
628       string value = arg.substr(index+1);\r
629       options[key] = value;\r
630     }\r
631   }\r
632 }\r
633 \r
634 \r
635 // Reads a file into a v8 string.\r
636 Handle<String> ReadFile(Isolate* isolate, const string& name) {\r
637   FILE* file = fopen(name.c_str(), "rb");\r
638   if (file == NULL) return Handle<String>();\r
639 \r
640   fseek(file, 0, SEEK_END);\r
641   int size = ftell(file);\r
642   rewind(file);\r
643 \r
644   char* chars = new char[size + 1];\r
645   chars[size] = '\0';\r
646   for (int i = 0; i < size;) {\r
647     int read = static_cast<int>(fread(&chars[i], 1, size - i, file));\r
648     i += read;\r
649   }\r
650   fclose(file);\r
651   Handle<String> result =\r
652       String::NewFromUtf8(isolate, chars, String::kNormalString, size);\r
653   delete[] chars;\r
654   return result;\r
655 }\r
656 \r
657 \r
658 const int kSampleSize = 6;\r
659 StringHttpRequest kSampleRequests[kSampleSize] = {\r
660   StringHttpRequest("/process.cc", "localhost", "google.com", "firefox"),\r
661   StringHttpRequest("/", "localhost", "google.net", "firefox"),\r
662   StringHttpRequest("/", "localhost", "google.org", "safari"),\r
663   StringHttpRequest("/", "localhost", "yahoo.com", "ie"),\r
664   StringHttpRequest("/", "localhost", "yahoo.com", "safari"),\r
665   StringHttpRequest("/", "localhost", "yahoo.com", "firefox")\r
666 };\r
667 \r
668 \r
669 bool ProcessEntries(HttpRequestProcessor* processor, int count,\r
670                     StringHttpRequest* reqs) {\r
671   for (int i = 0; i < count; i++) {\r
672     if (!processor->Process(&reqs[i]))\r
673       return false;\r
674   }\r
675   return true;\r
676 }\r
677 \r
678 \r
679 void PrintMap(map<string, string>* m) {\r
680   for (map<string, string>::iterator i = m->begin(); i != m->end(); i++) {\r
681     pair<string, string> entry = *i;\r
682     printf("%s: %s\n", entry.first.c_str(), entry.second.c_str());\r
683   }\r
684 }\r
685 \r
686 \r
687 int main(int argc, char* argv[]) {\r
688   v8::V8::InitializeICU();\r
689   map<string, string> options;\r
690   string file;\r
691   ParseOptions(argc, argv, options, &file);\r
692   if (file.empty()) {\r
693     fprintf(stderr, "No script was specified.\n");\r
694     return 1;\r
695   }\r
696   Isolate* isolate = Isolate::GetCurrent();\r
697   HandleScope scope(isolate);\r
698   Handle<String> source = ReadFile(isolate, file);\r
699   if (source.IsEmpty()) {\r
700     fprintf(stderr, "Error reading '%s'.\n", file.c_str());\r
701     return 1;\r
702   }\r
703   JsHttpRequestProcessor processor(isolate, source);\r
704   map<string, string> output;\r
705   if (!processor.Initialize(&options, &output)) {\r
706     fprintf(stderr, "Error initializing processor.\n");\r
707     return 1;\r
708   }\r
709   if (!ProcessEntries(&processor, kSampleSize, kSampleRequests))\r
710     return 1;\r
711   PrintMap(&output);\r
712 }\r