OLD | NEW |
| (Empty) |
1 /* | |
2 * Copyright (c) 2011 The Native Client Authors. All rights reserved. | |
3 * Use of this source code is governed by a BSD-style license that can be | |
4 * found in the LICENSE file. | |
5 */ | |
6 | |
7 // The portable representation of an instance and root scriptable object. | |
8 // The PPAPI version of the plugin instantiates a subclass of this class. | |
9 | |
10 #ifndef NATIVE_CLIENT_SRC_TRUSTED_PLUGIN_PLUGIN_H_ | |
11 #define NATIVE_CLIENT_SRC_TRUSTED_PLUGIN_PLUGIN_H_ | |
12 | |
13 #include <stdio.h> | |
14 #include <map> | |
15 #include <set> | |
16 #include <queue> | |
17 #include <string> | |
18 #include <vector> | |
19 | |
20 #include "native_client/src/include/nacl_macros.h" | |
21 #include "native_client/src/include/nacl_scoped_ptr.h" | |
22 #include "native_client/src/include/nacl_string.h" | |
23 #include "native_client/src/trusted/plugin/file_downloader.h" | |
24 #include "native_client/src/trusted/plugin/method_map.h" | |
25 #include "native_client/src/trusted/plugin/nacl_subprocess.h" | |
26 #include "native_client/src/trusted/plugin/pnacl_coordinator.h" | |
27 #include "native_client/src/trusted/plugin/service_runtime.h" | |
28 #include "native_client/src/trusted/plugin/utility.h" | |
29 | |
30 #include "ppapi/cpp/private/var_private.h" | |
31 // for pp::VarPrivate | |
32 #include "ppapi/cpp/private/instance_private.h" | |
33 #include "ppapi/cpp/rect.h" | |
34 #include "ppapi/cpp/url_loader.h" | |
35 #include "ppapi/cpp/var.h" | |
36 | |
37 struct NaClSrpcChannel; | |
38 struct NaClDesc; | |
39 | |
40 namespace nacl { | |
41 class DescWrapper; | |
42 class DescWrapperFactory; | |
43 } // namespace nacl | |
44 | |
45 namespace pp { | |
46 class Find_Dev; | |
47 class Printing_Dev; | |
48 class Selection_Dev; | |
49 class URLLoader; | |
50 class WidgetClient_Dev; | |
51 class URLUtil_Dev; | |
52 class Zoom_Dev; | |
53 } | |
54 | |
55 namespace ppapi_proxy { | |
56 class BrowserPpp; | |
57 } | |
58 | |
59 namespace plugin { | |
60 | |
61 class ErrorInfo; | |
62 class Manifest; | |
63 class PnaclCoordinator; | |
64 class ProgressEvent; | |
65 class ScriptableHandle; | |
66 | |
67 typedef enum { | |
68 METHOD_CALL = 0, | |
69 PROPERTY_GET, | |
70 PROPERTY_SET | |
71 } CallType; | |
72 | |
73 | |
74 class Plugin : public pp::InstancePrivate { | |
75 public: | |
76 // Factory method for creation. | |
77 static Plugin* New(PP_Instance instance); | |
78 | |
79 // ----- Methods inherited from pp::Instance: | |
80 | |
81 // Initializes this plugin with <embed/object ...> tag attribute count |argc|, | |
82 // names |argn| and values |argn|. Returns false on failure. | |
83 // Gets called by the browser right after New(). | |
84 virtual bool Init(uint32_t argc, const char* argn[], const char* argv[]); | |
85 | |
86 // Handles view changes from the browser. | |
87 virtual void DidChangeView(const pp::Rect& position, const pp::Rect& clip); | |
88 | |
89 // Handles gaining or losing focus. | |
90 virtual void DidChangeFocus(bool has_focus); | |
91 | |
92 // Handles input events delivered from the browser to this plugin element. | |
93 virtual bool HandleInputEvent(const pp::InputEvent& event); | |
94 | |
95 // Handles gaining or losing focus. | |
96 virtual bool HandleDocumentLoad(const pp::URLLoader& url_loader); | |
97 | |
98 // Returns a scriptable reference to this plugin element. | |
99 // Called by JavaScript document.getElementById(plugin_id). | |
100 virtual pp::Var GetInstanceObject(); | |
101 | |
102 // Handles postMessage from browser | |
103 virtual void HandleMessage(const pp::Var& message); | |
104 | |
105 // ----- Plugin interface support. | |
106 | |
107 // Load support. | |
108 // NaCl module can be loaded given a DescWrapper. | |
109 // | |
110 // Starts NaCl module but does not wait until low-level | |
111 // initialization (e.g., ld.so dynamic loading of manifest files) is | |
112 // done. The module will become ready later, asynchronously. Other | |
113 // event handlers should block until the module is ready before | |
114 // trying to communicate with it, i.e., until nacl_ready_state is | |
115 // DONE. Note, however, we already have another mechanism that | |
116 // prevents event delivery: StartJSObjectProxy plumbs through | |
117 // NaClSubprocess to SrpcClient which upcalls | |
118 // Plugin::StartProxiedExecution, which sets ppapi_proxy_. And NULL | |
119 // == ppapi_proxy_ prevents events from being delivered, even if | |
120 // nacl_ready_state is DONE. | |
121 // | |
122 // NB: currently we do not time out, so if the untrusted code | |
123 // does not signal that it is ready, then we will deadlock the main | |
124 // thread of the renderer on this subsequent event delivery. We | |
125 // should include a time-out at which point we declare the | |
126 // nacl_ready_state to be done, and let the normal crash detection | |
127 // mechanism(s) take over. | |
128 // | |
129 // Updates nacl_module_origin() and nacl_module_url(). | |
130 bool LoadNaClModule(nacl::DescWrapper* wrapper, ErrorInfo* error_info, | |
131 pp::CompletionCallback init_done_cb); | |
132 | |
133 // Finish hooking interfaces up, after low-level initialization is | |
134 // complete. | |
135 bool LoadNaClModuleContinuationIntern(ErrorInfo* error_info); | |
136 | |
137 // Continuation for starting SRPC/JSProxy services as appropriate. | |
138 // This is invoked as a callback when the NaCl module makes the | |
139 // init_done reverse RPC to tell us that low-level initialization | |
140 // such as ld.so processing is done. That initialization requires | |
141 // that the main thread be free in order to do Pepper | |
142 // main-thread-only operations such as file processing. | |
143 bool LoadNaClModuleContinuation(int32_t pp_error); | |
144 | |
145 // Load support. | |
146 // A helper SRPC NaCl module can be loaded given a DescWrapper. | |
147 // Blocks until the helper module signals initialization is done. | |
148 // Does not update nacl_module_origin(). | |
149 // Returns kInvalidNaClSubprocessId or the ID of the new helper NaCl module. | |
150 NaClSubprocessId LoadHelperNaClModule(nacl::DescWrapper* wrapper, | |
151 ErrorInfo* error_info); | |
152 | |
153 // Returns the argument value for the specified key, or NULL if not found. | |
154 // The callee retains ownership of the result. | |
155 char* LookupArgument(const char* key); | |
156 | |
157 enum LengthComputable { | |
158 LENGTH_IS_NOT_COMPUTABLE = 0, | |
159 LENGTH_IS_COMPUTABLE = 1 | |
160 }; | |
161 // Report successful loading of a module. | |
162 void ReportLoadSuccess(LengthComputable length_computable, | |
163 uint64_t loaded_bytes, | |
164 uint64_t total_bytes); | |
165 // Report an error that was encountered while loading a module. | |
166 void ReportLoadError(const ErrorInfo& error_info); | |
167 // Report loading a module was aborted, typically due to user action. | |
168 void ReportLoadAbort(); | |
169 // Dispatch a JavaScript event to indicate a key step in loading. | |
170 // |event_type| is a character string indicating which type of progress | |
171 // event (loadstart, progress, error, abort, load, loadend). Events are | |
172 // enqueued on the JavaScript event loop, which then calls back through | |
173 // DispatchProgressEvent. | |
174 void EnqueueProgressEvent(const char* event_type, | |
175 LengthComputable length_computable, | |
176 uint64_t loaded_bytes, | |
177 uint64_t total_bytes); | |
178 | |
179 // Report the error code that sel_ldr produces when starting a nexe. | |
180 void ReportSelLdrLoadStatus(int status); | |
181 | |
182 // Report nexe death after load to JS and shut down the proxy. | |
183 void ReportDeadNexe(); | |
184 | |
185 // The embed/object tag argument list. | |
186 int argc() const { return argc_; } | |
187 char** argn() const { return argn_; } | |
188 char** argv() const { return argv_; } | |
189 | |
190 BrowserInterface* browser_interface() const { return browser_interface_; } | |
191 Plugin* plugin() const { return const_cast<Plugin*>(this); } | |
192 | |
193 // URL resolution support. | |
194 // plugin_base_url is the URL used for resolving relative URLs used in | |
195 // src="...". | |
196 nacl::string plugin_base_url() const { return plugin_base_url_; } | |
197 void set_plugin_base_url(nacl::string url) { plugin_base_url_ = url; } | |
198 // manifest_base_url is the URL used for resolving relative URLs mentioned | |
199 // in manifest files. If the manifest is a data URI, this is an empty string. | |
200 nacl::string manifest_base_url() const { return manifest_base_url_; } | |
201 void set_manifest_base_url(nacl::string url) { manifest_base_url_ = url; } | |
202 | |
203 // The URL of the manifest file as set by the "src" attribute. | |
204 // It is not the fully resolved URL if it was set as relative. | |
205 const nacl::string& manifest_url() const { return manifest_url_; } | |
206 void set_manifest_url(const nacl::string& manifest_url) { | |
207 manifest_url_ = manifest_url; | |
208 } | |
209 | |
210 // The state of readiness of the plugin. | |
211 enum ReadyState { | |
212 // The trusted plugin begins in this ready state. | |
213 UNSENT = 0, | |
214 // The manifest file has been requested, but not yet received. | |
215 OPENED = 1, | |
216 // This state is unused. | |
217 HEADERS_RECEIVED = 2, | |
218 // The manifest file has been received and the nexe successfully requested. | |
219 LOADING = 3, | |
220 // The nexe has been loaded and the proxy started, so it is ready for | |
221 // interaction with the page. | |
222 DONE = 4 | |
223 }; | |
224 ReadyState nacl_ready_state() const { return nacl_ready_state_; } | |
225 void set_nacl_ready_state(ReadyState nacl_ready_state) { | |
226 nacl_ready_state_ = nacl_ready_state; | |
227 } | |
228 | |
229 // Get the NaCl module subprocess that was assigned the ID |id|. | |
230 NaClSubprocess* nacl_subprocess(NaClSubprocessId id) const { | |
231 if (kInvalidNaClSubprocessId == id) { | |
232 return NULL; | |
233 } | |
234 return nacl_subprocesses_[id]; | |
235 } | |
236 NaClSubprocessId next_nacl_subprocess_id() const { | |
237 return static_cast<NaClSubprocessId>(nacl_subprocesses_.size()); | |
238 } | |
239 | |
240 nacl::DescWrapperFactory* wrapper_factory() const { return wrapper_factory_; } | |
241 | |
242 // Requests a NaCl manifest download from a |url| relative to the page origin. | |
243 void RequestNaClManifest(const nacl::string& url); | |
244 | |
245 // Start up proxied execution of the browser API. | |
246 bool StartProxiedExecution(NaClSrpcChannel* srpc_channel, | |
247 ErrorInfo* error_info); | |
248 | |
249 // Determines whether experimental APIs are usable. | |
250 static bool ExperimentalJavaScriptApisAreEnabled(); | |
251 | |
252 // Methods for method and property dispatch. | |
253 bool InitParams(uintptr_t method_id, CallType call_type, SrpcParams* params); | |
254 bool HasMethod(uintptr_t method_id, CallType call_type); | |
255 bool Invoke(uintptr_t method_id, CallType call_type, SrpcParams* params); | |
256 std::vector<uintptr_t>* GetPropertyIdentifiers() { | |
257 return property_get_methods_.Keys(); | |
258 } | |
259 | |
260 // The size returned when a file download operation is unable to determine | |
261 // the size of the file to load. W3C ProgressEvents specify that unknown | |
262 // sizes return 0. | |
263 static const uint64_t kUnknownBytes = 0; | |
264 | |
265 // Getter for PPAPI proxy interface. | |
266 ppapi_proxy::BrowserPpp* ppapi_proxy() const { return ppapi_proxy_; } | |
267 | |
268 // Called back by CallOnMainThread. Dispatches the first enqueued progress | |
269 // event. | |
270 void DispatchProgressEvent(int32_t result); | |
271 | |
272 // Requests a URL asynchronously resulting in a call to pp_callback with | |
273 // a PP_Error indicating status. On success an open file descriptor | |
274 // corresponding to the url body is recorded for further lookup. | |
275 // This is used by SRPC-based StreamAsFile(). | |
276 bool StreamAsFile(const nacl::string& url, PP_CompletionCallback pp_callback); | |
277 // Returns an open POSIX file descriptor retrieved by StreamAsFile() | |
278 // or NACL_NO_FILE_DESC. The caller must take ownership of the descriptor. | |
279 int32_t GetPOSIXFileDesc(const nacl::string& url); | |
280 | |
281 // A helper function that gets the scheme type for |url|. Uses URLUtil_Dev | |
282 // interface which this class has as a member. | |
283 UrlSchemeType GetUrlScheme(const std::string& url); | |
284 | |
285 // Get the text description of the last error reported by the plugin. | |
286 const nacl::string& last_error_string() const { return last_error_string_; } | |
287 void set_last_error_string(const nacl::string& error) { | |
288 last_error_string_ = error; | |
289 } | |
290 | |
291 // The MIME type used to instantiate this instance of the NaCl plugin. | |
292 // Typically, the MIME type will be application/x-nacl. However, if the NEXE | |
293 // is being used as a content type handler for another content type (such as | |
294 // PDF), then this function will return that type. | |
295 const nacl::string& mime_type() const { return mime_type_; } | |
296 // The default MIME type for the NaCl plugin. | |
297 static const char* const kNaClMIMEType; | |
298 // Tests if the MIME type is not a NaCl MIME type. | |
299 bool IsForeignMIMEType() const; | |
300 // Returns true if PPAPI Dev interfaces should be allowed. | |
301 bool enable_dev_interface() { return enable_dev_interface_; } | |
302 | |
303 Manifest const* manifest() const { return manifest_.get(); } | |
304 | |
305 private: | |
306 NACL_DISALLOW_COPY_AND_ASSIGN(Plugin); | |
307 #ifndef HACK_FOR_MACOS_HANG_REMOVED | |
308 void XYZZY(const nacl::string& url, pp::VarPrivate js_callback); | |
309 #endif // HACK_FOR_MACOS_HANG_REMOVED | |
310 // Prevent construction and destruction from outside the class: | |
311 // must use factory New() method instead. | |
312 explicit Plugin(PP_Instance instance); | |
313 // The browser will invoke the destructor via the pp::Instance | |
314 // pointer to this object, not from base's Delete(). | |
315 ~Plugin(); | |
316 | |
317 bool Init(BrowserInterface* browser_interface, | |
318 int argc, | |
319 char* argn[], | |
320 char* argv[]); | |
321 void LoadMethods(); | |
322 // Shuts down socket connection, service runtime, and receive thread, | |
323 // in this order, for all spun up NaCl module subprocesses. | |
324 void ShutDownSubprocesses(); | |
325 | |
326 ScriptableHandle* scriptable_handle() const { return scriptable_handle_; } | |
327 void set_scriptable_handle(ScriptableHandle* scriptable_handle) { | |
328 scriptable_handle_ = scriptable_handle; | |
329 } | |
330 | |
331 // Access the service runtime for the main NaCl subprocess. | |
332 ServiceRuntime* main_service_runtime() const { | |
333 return main_subprocess_.service_runtime(); | |
334 } | |
335 | |
336 // Setting the properties and methods exported. | |
337 void AddPropertyGet(RpcFunction function_ptr, | |
338 const char* name, | |
339 const char* outs); | |
340 | |
341 // Help load a nacl module, from the file specified in wrapper. | |
342 // This will fully initialize the |subprocess| if the load was successful. | |
343 bool LoadNaClModuleCommon(nacl::DescWrapper* wrapper, | |
344 NaClSubprocess* subprocess, | |
345 ErrorInfo* error_info, | |
346 pp::CompletionCallback init_done_cb); | |
347 bool StartSrpcServices(NaClSubprocess* subprocess, ErrorInfo* error_info); | |
348 bool StartSrpcServicesCommon(NaClSubprocess* subprocess, | |
349 ErrorInfo* error_info); | |
350 bool StartJSObjectProxy(NaClSubprocess* subprocess, ErrorInfo* error_info); | |
351 | |
352 MethodInfo* GetMethodInfo(uintptr_t method_id, CallType call_type); | |
353 | |
354 // Check url and decide if PPAPI Dev interfaces are required. | |
355 bool RequiresDevInterface(const nacl::string& manifest_url); | |
356 | |
357 // Callback used when getting the URL for the .nexe file. If the URL loading | |
358 // is successful, the file descriptor is opened and can be passed to sel_ldr | |
359 // with the sandbox on. | |
360 void NexeFileDidOpen(int32_t pp_error); | |
361 void NexeFileDidOpenContinuation(int32_t pp_error); | |
362 | |
363 // Callback used when a .nexe is translated from bitcode. If the translation | |
364 // is successful, the file descriptor is opened and can be passed to sel_ldr | |
365 // with the sandbox on. | |
366 void BitcodeDidTranslate(int32_t pp_error); | |
367 void BitcodeDidTranslateContinuation(int32_t pp_error); | |
368 | |
369 // NaCl ISA selection manifest file support. The manifest file is specified | |
370 // using the "nacl" attribute in the <embed> tag. First, the manifest URL (or | |
371 // data: URI) is fetched, then the JSON is parsed. Once a valid .nexe is | |
372 // chosen for the sandbox ISA, any current service runtime is shut down, the | |
373 // .nexe is loaded and run. | |
374 | |
375 // Callback used when getting the manifest file as a buffer (e.g., data URIs) | |
376 void NaClManifestBufferReady(int32_t pp_error); | |
377 | |
378 // Callback used when getting the manifest file as a local file descriptor. | |
379 void NaClManifestFileDidOpen(int32_t pp_error); | |
380 | |
381 // Processes the JSON manifest string and starts loading the nexe. | |
382 void ProcessNaClManifest(const nacl::string& manifest_json); | |
383 | |
384 // Parses the JSON in |manifest_json| and retains a Manifest in | |
385 // |manifest_| for use by subsequent resource lookups. | |
386 // On success, |true| is returned and |manifest_| is updated to | |
387 // contain a Manifest that is used by SelectNexeURLFromManifest. | |
388 // On failure, |false| is returned, and |manifest_| is unchanged. | |
389 bool SetManifestObject(const nacl::string& manifest_json, | |
390 ErrorInfo* error_info); | |
391 | |
392 // Determines the URL of the program module appropriate for the NaCl sandbox | |
393 // implemented by the installed sel_ldr. The URL is determined from the | |
394 // Manifest in |manifest_|. On success, |true| is returned and |result| is | |
395 // set to the URL to use for the program, and |is_portable| is set to | |
396 // |true| if the program is portable bitcode. | |
397 // On failure, |false| is returned. | |
398 bool SelectProgramURLFromManifest(nacl::string* result, | |
399 ErrorInfo* error_info, | |
400 bool* is_portable); | |
401 | |
402 // TODO(jvoung): get rid of these once we find a better way to store / install | |
403 // the pnacl translator nexes. | |
404 bool SelectLLCURLFromManifest(nacl::string* result, | |
405 ErrorInfo* error_info); | |
406 bool SelectLDURLFromManifest(nacl::string* result, | |
407 ErrorInfo* error_info); | |
408 | |
409 // Logs timing information to a UMA histogram, and also logs the same timing | |
410 // information divided by the size of the nexe to another histogram. | |
411 void HistogramStartupTimeSmall(const std::string& name, float dt); | |
412 void HistogramStartupTimeMedium(const std::string& name, float dt); | |
413 | |
414 // Determines the appropriate nexe for the sandbox and requests a load. | |
415 void RequestNexeLoad(); | |
416 | |
417 // Callback used when loading a URL for SRPC-based StreamAsFile(). | |
418 void UrlDidOpenForStreamAsFile(int32_t pp_error, | |
419 FileDownloader*& url_downloader, | |
420 PP_CompletionCallback pp_callback); | |
421 | |
422 // Shuts down the proxy for PPAPI nexes. | |
423 void ShutdownProxy(); // Nexe shutdown + proxy deletion. | |
424 | |
425 BrowserInterface* browser_interface_; | |
426 ScriptableHandle* scriptable_handle_; | |
427 | |
428 int argc_; | |
429 char** argn_; | |
430 char** argv_; | |
431 | |
432 // Keep track of the NaCl module subprocesses that were spun up in the plugin. | |
433 NaClSubprocess main_subprocess_; | |
434 std::vector<NaClSubprocess*> nacl_subprocesses_; | |
435 | |
436 nacl::string plugin_base_url_; | |
437 nacl::string manifest_base_url_; | |
438 nacl::string manifest_url_; | |
439 ReadyState nacl_ready_state_; | |
440 | |
441 nacl::DescWrapperFactory* wrapper_factory_; | |
442 | |
443 MethodMap property_get_methods_; | |
444 | |
445 // File download support. |nexe_downloader_| can be opened with a specific | |
446 // callback to run when the file has been downloaded and is opened for | |
447 // reading. We use one downloader for all URL downloads to prevent issuing | |
448 // multiple GETs that might arrive out of order. For example, this will | |
449 // prevent a GET of a NaCl manifest while a .nexe GET is pending. Note that | |
450 // this will also prevent simultaneous handling of multiple .nexes on a page. | |
451 FileDownloader nexe_downloader_; | |
452 pp::CompletionCallbackFactory<Plugin> callback_factory_; | |
453 | |
454 PnaclCoordinator pnacl_; | |
455 | |
456 // The manifest dictionary. Used for looking up resources to be loaded. | |
457 nacl::scoped_ptr<Manifest> manifest_; | |
458 // URL processing interface for use in looking up resources in manifests. | |
459 const pp::URLUtil_Dev* url_util_; | |
460 | |
461 // A string containing the text description of the last error produced by | |
462 // this plugin. | |
463 nacl::string last_error_string_; | |
464 | |
465 // A pointer to the browser end of a proxy pattern connecting the | |
466 // NaCl plugin to the PPAPI .nexe's PPP interface | |
467 // (InitializeModule, Shutdown, and GetInterface). | |
468 // TODO(sehr): this should be a scoped_ptr for shutdown. | |
469 ppapi_proxy::BrowserPpp* ppapi_proxy_; | |
470 | |
471 // PPAPI Dev interfaces are disabled by default. | |
472 bool enable_dev_interface_; | |
473 | |
474 // If we get a DidChangeView event before the nexe is loaded, we store it and | |
475 // replay it to nexe after it's loaded. | |
476 bool replayDidChangeView; | |
477 pp::Rect replayDidChangeViewPosition; | |
478 pp::Rect replayDidChangeViewClip; | |
479 | |
480 // If we get a HandleDocumentLoad event before the nexe is loaded, we store | |
481 // it and replay it to nexe after it's loaded. | |
482 bool replayHandleDocumentLoad; | |
483 pp::URLLoader replayHandleDocumentLoadURLLoader; | |
484 | |
485 nacl::string mime_type_; | |
486 | |
487 // Keep track of the FileDownloaders created to fetch urls. | |
488 std::set<FileDownloader*> url_downloaders_; | |
489 // Keep track of file descriptors opened by StreamAsFile(). | |
490 // These are owned by the browser. | |
491 std::map<nacl::string, int32_t> url_fd_map_; | |
492 | |
493 // Pending progress events. | |
494 std::queue<ProgressEvent*> progress_events_; | |
495 | |
496 // Adapter class constructors require a reference to 'this', so we can't | |
497 // contain them directly. | |
498 nacl::scoped_ptr<pp::Find_Dev> find_adapter_; | |
499 nacl::scoped_ptr<pp::Printing_Dev> printing_adapter_; | |
500 nacl::scoped_ptr<pp::Selection_Dev> selection_adapter_; | |
501 nacl::scoped_ptr<pp::WidgetClient_Dev> widget_client_adapter_; | |
502 nacl::scoped_ptr<pp::Zoom_Dev> zoom_adapter_; | |
503 | |
504 // used for NexeFileDidOpenContinuation | |
505 int64_t load_start_; | |
506 | |
507 int64_t init_time_; | |
508 int64_t ready_time_; | |
509 size_t nexe_size_; | |
510 }; | |
511 | |
512 } // namespace plugin | |
513 | |
514 #endif // NATIVE_CLIENT_SRC_TRUSTED_PLUGIN_PLUGIN_H_ | |
OLD | NEW |