- add sources.
[platform/framework/web/crosswalk.git] / src / ppapi / native_client / src / trusted / plugin / pnacl_resources.cc
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "ppapi/native_client/src/trusted/plugin/pnacl_resources.h"
6
7 #include "native_client/src/include/portability_io.h"
8 #include "native_client/src/shared/platform/nacl_check.h"
9 #include "native_client/src/trusted/desc/nacl_desc_wrapper.h"
10 #include "ppapi/c/pp_errors.h"
11 #include "ppapi/native_client/src/trusted/plugin/file_utils.h"
12 #include "ppapi/native_client/src/trusted/plugin/manifest.h"
13 #include "ppapi/native_client/src/trusted/plugin/plugin.h"
14 #include "ppapi/native_client/src/trusted/plugin/pnacl_coordinator.h"
15 #include "ppapi/native_client/src/trusted/plugin/utility.h"
16 #include "third_party/jsoncpp/source/include/json/reader.h"
17 #include "third_party/jsoncpp/source/include/json/value.h"
18
19 namespace plugin {
20
21 static const char kPnaclComponentScheme[] = "pnacl-component://";
22 const char PnaclUrls::kResourceInfoUrl[] = "pnacl.json";
23
24 nacl::string PnaclUrls::GetBaseUrl() {
25   return nacl::string(kPnaclComponentScheme);
26 }
27
28 nacl::string PnaclUrls::PrependPlatformPrefix(const nacl::string& url) {
29   return nacl::string(GetSandboxISA()) + "/" + url;
30 }
31
32 // Determine if a URL is for a pnacl-component file, or if it is some other
33 // type of URL (e.g., http://, https://, chrome-extension://).
34 // The URL could be one of the other variants for shared libraries
35 // served from the web.
36 bool PnaclUrls::IsPnaclComponent(const nacl::string& full_url) {
37   return full_url.find(kPnaclComponentScheme, 0) == 0;
38 }
39
40 // Convert a URL to a filename accepted by GetReadonlyPnaclFd.
41 // Must be kept in sync with chrome/browser/nacl_host/nacl_file_host.
42 nacl::string PnaclUrls::PnaclComponentURLToFilename(
43     const nacl::string& full_url) {
44   // strip component scheme.
45   nacl::string r = full_url.substr(
46       nacl::string(kPnaclComponentScheme).length());
47
48   // Use white-listed-chars.
49   size_t replace_pos;
50   static const char* white_list = "abcdefghijklmnopqrstuvwxyz0123456789_";
51   replace_pos = r.find_first_not_of(white_list);
52   while(replace_pos != nacl::string::npos) {
53     r = r.replace(replace_pos, 1, "_");
54     replace_pos = r.find_first_not_of(white_list);
55   }
56   return r;
57 }
58
59 //////////////////////////////////////////////////////////////////////
60
61 PnaclResources::~PnaclResources() {
62   for (std::map<nacl::string, nacl::DescWrapper*>::iterator
63            i = resource_wrappers_.begin(), e = resource_wrappers_.end();
64        i != e;
65        ++i) {
66     delete i->second;
67   }
68   resource_wrappers_.clear();
69 }
70
71 // static
72 int32_t PnaclResources::GetPnaclFD(Plugin* plugin, const char* filename) {
73   PP_FileHandle file_handle =
74       plugin->nacl_interface()->GetReadonlyPnaclFd(filename);
75   if (file_handle == PP_kInvalidFileHandle)
76     return -1;
77
78 #if NACL_WINDOWS
79   //////// Now try the posix view.
80   int32_t posix_desc = _open_osfhandle(reinterpret_cast<intptr_t>(file_handle),
81                                        _O_RDONLY | _O_BINARY);
82   if (posix_desc == -1) {
83     PLUGIN_PRINTF((
84         "PnaclResources::GetPnaclFD failed to convert HANDLE to posix\n"));
85     // Close the Windows HANDLE if it can't be converted.
86     CloseHandle(file_handle);
87   }
88   return posix_desc;
89 #else
90   return file_handle;
91 #endif
92 }
93
94 nacl::DescWrapper* PnaclResources::WrapperForUrl(const nacl::string& url) {
95   CHECK(resource_wrappers_.find(url) != resource_wrappers_.end());
96   return resource_wrappers_[url];
97 }
98
99 void PnaclResources::ReadResourceInfo(
100     const nacl::string& resource_info_url,
101     const pp::CompletionCallback& resource_info_read_cb) {
102   PLUGIN_PRINTF(("PnaclResources::ReadResourceInfo\n"));
103
104   nacl::string full_url;
105   ErrorInfo error_info;
106   if (!manifest_->ResolveURL(resource_info_url, &full_url, &error_info)) {
107     ReadResourceInfoError(nacl::string("failed to resolve ") +
108                           resource_info_url + ": " +
109                           error_info.message() + ".");
110     return;
111   }
112   PLUGIN_PRINTF(("Resolved resources info url: %s\n", full_url.c_str()));
113   nacl::string resource_info_filename =
114     PnaclUrls::PnaclComponentURLToFilename(full_url);
115
116   PLUGIN_PRINTF(("Pnacl-converted resources info url: %s\n",
117                  resource_info_filename.c_str()));
118
119   int32_t fd = GetPnaclFD(plugin_, resource_info_filename.c_str());
120   if (fd < 0) {
121     // File-open failed. Assume this means that the file is
122     // not actually installed.
123     ReadResourceInfoError(
124         nacl::string("The Portable Native Client (pnacl) component is not "
125                      "installed. Please consult chrome://components for more "
126                      "information."));
127     return;
128   }
129
130   nacl::string json_buffer;
131   file_utils::StatusCode status = file_utils::SlurpFile(fd, json_buffer);
132   if (status != file_utils::PLUGIN_FILE_SUCCESS) {
133     ReadResourceInfoError(
134         nacl::string("PnaclResources::ReadResourceInfo reading "
135                      "failed for: ") + resource_info_filename);
136     return;
137   }
138
139   // Finally, we have the resource info JSON data in json_buffer.
140   PLUGIN_PRINTF(("Resource info JSON data:\n%s\n", json_buffer.c_str()));
141   nacl::string error_message;
142   if (!ParseResourceInfo(json_buffer, error_message)) {
143     ReadResourceInfoError(nacl::string("Parsing resource info failed: ") +
144                           error_message + "\n");
145     return;
146   }
147
148   // Done. Queue the completion callback.
149   pp::Core* core = pp::Module::Get()->core();
150   core->CallOnMainThread(0, resource_info_read_cb, PP_OK);
151 }
152
153 void PnaclResources::ReadResourceInfoError(const nacl::string& msg) {
154   coordinator_->ReportNonPpapiError(ERROR_PNACL_RESOURCE_FETCH, msg);
155 }
156
157 bool PnaclResources::ParseResourceInfo(const nacl::string& buf,
158                                        nacl::string& errmsg) {
159   // Expect the JSON file to contain a top-level object (dictionary).
160   Json::Reader json_reader;
161   Json::Value json_data;
162   if (!json_reader.parse(buf, json_data)) {
163     errmsg = nacl::string("JSON parse error: ") +
164              json_reader.getFormatedErrorMessages();
165     return false;
166   }
167
168   if (!json_data.isObject()) {
169     errmsg = nacl::string("Malformed JSON dictionary");
170     return false;
171   }
172
173   if (json_data.isMember("pnacl-llc-name")) {
174     Json::Value json_name = json_data["pnacl-llc-name"];
175     if (json_name.isString()) {
176       llc_tool_name = json_name.asString();
177       PLUGIN_PRINTF(("Set llc_tool_name=%s\n", llc_tool_name.c_str()));
178     }
179   }
180
181   if (json_data.isMember("pnacl-ld-name")) {
182     Json::Value json_name = json_data["pnacl-ld-name"];
183     if (json_name.isString()) {
184       ld_tool_name = json_name.asString();
185       PLUGIN_PRINTF(("Set ld_tool_name=%s\n", ld_tool_name.c_str()));
186     }
187   }
188
189   return true;
190 }
191
192 void PnaclResources::StartLoad(
193     const pp::CompletionCallback& all_loaded_callback) {
194   PLUGIN_PRINTF(("PnaclResources::StartLoad\n"));
195
196   std::vector<nacl::string> resource_urls;
197   resource_urls.push_back(GetLlcUrl());
198   resource_urls.push_back(GetLdUrl());
199
200   PLUGIN_PRINTF(("PnaclResources::StartLoad -- local install of PNaCl.\n"));
201   // Do a blocking load of each of the resources.
202   int32_t result = PP_OK;
203   for (size_t i = 0; i < resource_urls.size(); ++i) {
204     const nacl::string& url_with_platform_prefix =
205       PnaclUrls::PrependPlatformPrefix(resource_urls[i]);
206     nacl::string full_url;
207     ErrorInfo error_info;
208     if (!manifest_->ResolveURL(url_with_platform_prefix, &full_url,
209                                &error_info)) {
210       coordinator_->ReportNonPpapiError(
211           ERROR_PNACL_RESOURCE_FETCH,
212           nacl::string("failed to resolve ") +
213           url_with_platform_prefix + ": " +
214           error_info.message() + ".");
215       break;
216     }
217     nacl::string filename = PnaclUrls::PnaclComponentURLToFilename(full_url);
218
219     int32_t fd = PnaclResources::GetPnaclFD(plugin_, filename.c_str());
220     if (fd < 0) {
221       // File-open failed. Assume this means that the file is
222       // not actually installed. This shouldn't actually occur since
223       // ReadResourceInfo() should happen first, and error out.
224       coordinator_->ReportNonPpapiError(
225           ERROR_PNACL_RESOURCE_FETCH,
226         nacl::string("The Portable Native Client (pnacl) component is not "
227                      "installed. Please consult chrome://components for more "
228                      "information."));
229       result = PP_ERROR_FILENOTFOUND;
230       break;
231     } else {
232       resource_wrappers_[resource_urls[i]] =
233           plugin_->wrapper_factory()->MakeFileDesc(fd, O_RDONLY);
234     }
235   }
236   // We're done!  Queue the callback.
237   pp::Core* core = pp::Module::Get()->core();
238   core->CallOnMainThread(0, all_loaded_callback, result);
239 }
240
241 }  // namespace plugin