Chromium Code Reviews| Index: mojo/python/content_handler/content_handler_main.cc |
| diff --git a/mojo/python/content_handler/content_handler_main.cc b/mojo/python/content_handler/content_handler_main.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..53df8254e0560ac45c3a20e32843d0800417e196 |
| --- /dev/null |
| +++ b/mojo/python/content_handler/content_handler_main.cc |
| @@ -0,0 +1,197 @@ |
| +// Copyright 2014 The Chromium Authors. All rights reserved. |
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +#include <python2.7/Python.h> |
| +#include <dlfcn.h> |
| + |
| +#include "base/files/file_path.h" |
| +#include "base/files/scoped_temp_dir.h" |
| +#include "base/i18n/icu_util.h" |
| +#include "mojo/application/application_runner_chromium.h" |
| +#include "mojo/application/content_handler_factory.h" |
| +#include "mojo/common/common_type_converters.h" |
| +#include "mojo/common/data_pipe_utils.h" |
| +#include "mojo/public/c/system/main.h" |
| +#include "mojo/public/cpp/application/application_delegate.h" |
| +#include "mojo/public/cpp/application/application_impl.h" |
| +#include "mojo/public/cpp/application/interface_factory_impl.h" |
| +#include "third_party/zlib/google/zip_reader.h" |
| + |
| +char kMojoContext[] = "mojo"; |
| +char kMojoSystemContext[] = "mojo.system"; |
| +char kMojoSystemImplContext[] = "mojo.system_impl"; |
| +char kSystem[] = "system"; |
| +char kSystemImpl[] = "system_impl"; |
| +char kMojoMain[] = "MojoMain"; |
| + |
| +extern "C" { |
| + void initsystem(); |
| + void initsystem_impl(); |
| + extern char* _Py_PackageContext; |
| +} |
| + |
| +namespace mojo { |
| +namespace python { |
| + |
| +class PythonContentHandler : public ApplicationDelegate, |
| + public ContentHandlerFactory::Delegate { |
| + public: |
| + PythonContentHandler() : content_handler_factory_(this) {} |
| + |
| + private: |
| + // Overridden from ApplicationDelegate: |
| + bool ConfigureIncomingConnection(ApplicationConnection* connection) override { |
| + connection->AddService(&content_handler_factory_); |
| + return true; |
| + } |
| + |
| + // Extracts the target application into a temporary directory. This directory |
| + // is deleted at the end of the life of the PythonContentHandler object. |
| + void ExtractApplication(URLResponsePtr response) { |
| + MOJO_CHECK(temp_dir_.CreateUniqueTempDir()); |
|
qsr
2015/01/07 15:14:46
You cannot use a class member for temp_dir_, as th
etiennej
2015/01/08 11:23:13
Done.
|
| + |
| + zip::ZipReader reader; |
| + const std::string input_data = CopyToString(response->body.Pass()); |
| + MOJO_DCHECK(reader.OpenFromString(input_data)); |
| + base::FilePath temp_dir_path = temp_dir_.path(); |
| + while (reader.HasMore()) { |
| + MOJO_DCHECK(reader.OpenCurrentEntryInZip()); |
| + MOJO_DCHECK(reader.ExtractCurrentEntryIntoDirectory(temp_dir_path)); |
| + MOJO_DCHECK(reader.AdvanceToNextEntry()); |
| + } |
| + } |
| + |
| + // Sets up the Python interpreter and loads mojo system modules. This method |
| + // returns the global dictionary for the python environment that should be |
| + // used for subsequent calls. Takes as input the path of the unpacked |
| + // application files. |
| + PyObject* SetupPythonEnvironment(const std::string& application_path) { |
|
qsr
2015/01/07 15:14:46
Could you use there and below the helper scoped cl
etiennej
2015/01/08 11:23:13
I am not sure what you mean by "helper scoped clas
etiennej
2015/01/08 17:14:28
Done.
|
| + dlopen("libpython2.7.so", RTLD_LAZY | RTLD_GLOBAL); |
| + |
| + Py_Initialize(); |
| + |
| + PyObject *m, *d; |
| + m = PyImport_AddModule("__main__"); |
| + if (m == NULL) |
| + return NULL; |
| + d = PyModule_GetDict(m); |
| + |
| + // Inject the application path into the python search path so that imports |
| + // from the application work as expected. |
| + std::string search_path_py_command = |
| + "import sys; sys.path.append('" + application_path + "'); import mojo"; |
| + PyObject* result = |
| + PyRun_String(search_path_py_command.c_str(), Py_file_input, d, d); |
| + |
| + if (result == nullptr) { |
| + LOG(ERROR) << "Error while configuring path"; |
| + PyErr_Print(); |
| + return NULL; |
| + } |
| + Py_DECREF(result); |
| + |
| + // Initializing dummy mojo module |
| + char* oldcontext = _Py_PackageContext; |
| + _Py_PackageContext = kMojoContext; |
| + PyObject* mojo_module = Py_InitModule(kMojoContext, nullptr); |
| + |
| + // Initializing injected mojo.system_impl |
| + _Py_PackageContext = kMojoSystemImplContext; |
| + initsystem_impl(); |
| + _Py_PackageContext = oldcontext; |
| + PyObject* system_impl_module = |
| + PyDict_GetItemString(PyImport_GetModuleDict(), kMojoSystemImplContext); |
| + PyObject_SetAttrString(mojo_module, kSystemImpl, system_impl_module); |
| + |
| + // Initializing injected mojo.system |
| + _Py_PackageContext = kMojoSystemContext; |
| + initsystem(); |
| + _Py_PackageContext = oldcontext; |
| + PyObject* system_module = |
| + PyDict_GetItemString(PyImport_GetModuleDict(), kMojoSystemContext); |
| + PyObject_SetAttrString(mojo_module, kSystem, system_module); |
| + |
| + return d; |
| + } |
| + |
| + // Overridden from ContentHandlerFactory::ManagedDelegate: |
| + void RunApplication(ShellPtr shell, URLResponsePtr response) override { |
| + ExtractApplication(response.Pass()); |
| + base::FilePath directory_path = temp_dir_.path(); |
| + |
| + PyObject* d = SetupPythonEnvironment(directory_path.value()); |
| + MOJO_DCHECK(d); |
| + |
| + base::FilePath entry_path = directory_path.Append("__mojo__.py"); |
| + |
| + FILE* entry_file = base::OpenFile(entry_path, "r"); |
| + MOJO_DCHECK(entry_file); |
| + |
| + // Load the __mojo__.py file into the interpreter. MojoMain hasn't run yet. |
| + PyObject* result = |
| + PyRun_File(entry_file, entry_path.value().c_str(), Py_file_input, d, d); |
| + if (result == nullptr) { |
| + LOG(ERROR) << "Error while loading script"; |
| + PyErr_Print(); |
| + return; |
| + } |
| + Py_DECREF(result); |
| + |
| + // Find MojoMain. |
| + PyObject* py_function = PyMapping_GetItemString(d, kMojoMain); |
| + |
| + if (py_function == NULL) { |
| + LOG(ERROR) << "Locals size: " << PyMapping_Size(d); |
| + LOG(ERROR) << "MojoMain not found"; |
| + PyErr_Print(); |
| + return; |
| + } |
| + |
| + if (PyCallable_Check(py_function)) { |
| + MojoHandle shell_handle = shell.PassMessagePipe().release().value(); |
| + PyObject* py_input = PyInt_FromLong(shell_handle); |
| + PyObject* py_arguments = PyTuple_New(1); |
| + // py_input reference is stolen by py_arguments |
| + PyTuple_SetItem(py_arguments, 0, py_input); |
| + // Run MojoMain with shell_handle as the first and only argument. |
| + PyObject* py_output = PyObject_CallObject(py_function, py_arguments); |
| + |
| + Py_DECREF(py_function); |
| + Py_DECREF(py_arguments); |
| + |
| + if (py_output) { |
| + Py_DECREF(py_output); |
| + } else { |
| + LOG(ERROR) << "Error while executing MojoMain"; |
| + PyErr_Print(); |
| + return; |
| + } |
| + } else { |
| + LOG(ERROR) << "MojoMain is not callable; it should be a function"; |
| + } |
| + |
| + Py_Finalize(); |
| + } |
| + |
| + std::string CopyToString(ScopedDataPipeConsumerHandle body) { |
| + std::string body_str; |
| + bool result = common::BlockingCopyToString(body.Pass(), &body_str); |
| + DCHECK(result); |
| + return body_str; |
| + } |
| + |
| + ContentHandlerFactory content_handler_factory_; |
| + base::ScopedTempDir temp_dir_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(PythonContentHandler); |
| +}; |
| + |
| +} // namespace python |
| +} // namespace mojo |
| + |
| +MojoResult MojoMain(MojoHandle shell_handle) { |
| + mojo::ApplicationRunnerChromium runner( |
| + new mojo::python::PythonContentHandler()); |
| + return runner.Run(shell_handle); |
| +} |