Merge pull request #149 from maqnouch/patch-1
Fix typos in README.md
diff --git a/.gitignore b/.gitignore
index 5e2cce2..6b133e2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -11,7 +11,6 @@
.pydevproject
.checkstyle
cscope.*
-packages/
Session.vim
tags
Thumbs.db
@@ -68,3 +67,7 @@
/third_party/yasm/source/patched-yasm/
/tools/grit/
/v8/
+# dart packages directories
+/mojo/dart/mojom/packages
+/mojo/dart/mojom/bin/packages
+/mojo/dart/apptest/packages
diff --git a/DEPS b/DEPS
index b16c545..9771e0a 100644
--- a/DEPS
+++ b/DEPS
@@ -25,8 +25,8 @@
'v8_revision': '230d131d173ab2d60291d303177bc04ec3f6e519',
'angle_revision': 'bdd419f9f5b006e913606e7363125942c8ae06bc',
'buildtools_revision': '15308f469a704c45d15567fa69cd94ce07ad0e1b',
- 'dart_revision': '45304',
- 'dart_observatory_packages_revision': '43830',
+ 'dart_revision': '45606',
+ 'dart_observatory_packages_revision': '45565',
'pdfium_revision': 'b0115665b0f33971f1b7077740d51e155583cec0',
'boringssl_revision': '642f1498d056dbba3e50ed5a232ab2f482626dec',
'lss_revision': 'e079768b7e3a94dcbe7d338496c0c3bde7151b6e',
@@ -256,6 +256,17 @@
],
},
{
+ 'name': 'gn_mac',
+ 'pattern': '.',
+ 'action': [ 'download_from_google_storage',
+ '--no_resume',
+ '--platform=darwin',
+ '--no_auth',
+ '--bucket', 'chromium-gn',
+ '-s', 'src/buildtools/mac/gn.sha1',
+ ],
+ },
+ {
'name': 'gn_win',
'pattern': '.',
'action': [ 'download_from_google_storage',
@@ -278,6 +289,17 @@
'-s', 'src/buildtools/linux64/clang-format.sha1',
],
},
+ {
+ 'name': 'clang_format_mac',
+ 'pattern': '.',
+ 'action': [ 'download_from_google_storage',
+ '--no_resume',
+ '--platform=darwin',
+ '--no_auth',
+ '--bucket', 'chromium-clang-format',
+ '-s', 'src/buildtools/mac/clang-format.sha1',
+ ],
+ },
# Pull binutils for linux, enabled debug fission for faster linking /
# debugging when used with clang on Ubuntu Precise.
# https://code.google.com/p/chromium/issues/detail?id=352046
diff --git a/WATCHLISTS b/WATCHLISTS
index 10c22fe..fc4adbd 100644
--- a/WATCHLISTS
+++ b/WATCHLISTS
@@ -31,6 +31,7 @@
'darin@chromium.org',
'qsr+mojo@chromium.org',
'viettrungluu+watch@chromium.org',
+ 'gregsimon@chromium.org',
'yzshen+watch@chromium.org'],
'services': ['qsr+mojo@chromium.org',
'yzshen+watch@chromium.org'],
@@ -38,6 +39,7 @@
'yzshen+watch@chromium.org'],
'sky': ['abarth@chromium.org',
'ojan@chromium.org',
+ 'gregsimon@chromium.org',
'qsr+mojo@chromium.org'],
},
}
diff --git a/mojo/BUILD.gn b/mojo/BUILD.gn
index ee4850d..d4ac711 100644
--- a/mojo/BUILD.gn
+++ b/mojo/BUILD.gn
@@ -53,7 +53,6 @@
deps = [
"//mojo/common:mojo_common_unittests",
"//mojo/converters/surfaces/tests:mojo_surfaces_lib_unittests",
- "//mojo/edk/js:tests",
"//mojo/edk/system:tests",
"//mojo/edk/test:public_tests",
"//mojo/dart/embedder/test:dart_unittests",
@@ -65,9 +64,8 @@
"//services/dart/dart_apptests",
"//services/files:apptests",
"//services/http_server:apptests",
- "//services/js:js_apptests",
- "//services/js:js_services_unittests",
"//services/reaper:tests",
+ "//services/url_response_disk_cache:tests",
"//services/view_manager:mojo_view_manager_client_apptests",
"//services/view_manager:view_manager_service_apptests",
"//services/view_manager:view_manager_service_unittests",
@@ -84,6 +82,16 @@
]
}
+ # TODO(jamesr): We only support building V8 snapshot data on a linux host since it
+ # needs a 32 bit toolchain and we don't have one configured for mac hosts.
+ if (host_os == "linux") {
+ deps += [
+ "//mojo/edk/js:tests",
+ "//services/js:js_apptests",
+ "//services/js:js_services_unittests",
+ ]
+ }
+
if (mojo_use_prebuilt_network_service) {
deps += [ "//mojo/public/tools:copy_network_service_apptests" ]
}
diff --git a/mojo/dart/embedder/BUILD.gn b/mojo/dart/embedder/BUILD.gn
index 95fcc46..e7ef21e 100644
--- a/mojo/dart/embedder/BUILD.gn
+++ b/mojo/dart/embedder/BUILD.gn
@@ -69,6 +69,7 @@
"//mojo/dart/embedder/io/socket_patch.dart",
"//mojo/dart/embedder/io/server_socket_patch.dart",
]
+ root_prefix = "//mojo/dart/embedder/"
output = "$target_gen_dir/dart_embedder_patch_resources.cc"
table_name = "dart_embedder_patch"
}
@@ -107,6 +108,7 @@
"//mojo/public/dart/src/application_connection.dart",
"//mojo/public/dart/src/buffer.dart",
"//mojo/public/dart/src/codec.dart",
+ "//mojo/public/dart/src/control_message.dart",
"//mojo/public/dart/src/data_pipe.dart",
"//mojo/public/dart/src/drain_data.dart",
"//mojo/public/dart/src/event_stream.dart",
@@ -143,6 +145,7 @@
"//mojo/dart/embedder/vmservice/resources.dart",
"//mojo/dart/embedder/vmservice/server.dart",
]
+ root_prefix = "//mojo/dart/embedder/"
input_directory = "$root_out_dir/observatory/deployed/web/"
output = "$target_gen_dir/dart_embedder_service_isolate_resources.cc"
table_name = "dart_embedder_service_isolate"
@@ -252,7 +255,7 @@
outputs = [
"$root_out_dir/observatory/build/web/index.html",
- "$root_out_dir/observatory/build/web/index.html_bootstrap.dart.js",
+ "$root_out_dir/observatory/build/web/index.html.polymer.bootstrap.dart.js",
]
}
@@ -266,7 +269,7 @@
inputs = [
script,
"$root_out_dir/observatory/build/web/index.html",
- "$root_out_dir/observatory/build/web/index.html_bootstrap.dart.js",
+ "$root_out_dir/observatory/build/web/index.html.polymer.bootstrap.dart.js",
]
args = [
@@ -281,7 +284,7 @@
outputs = [
"$root_out_dir/observatory/deployed/web/index.html",
- "$root_out_dir/observatory/deployed/web/index.html_bootstrap.dart.js",
+ "$root_out_dir/observatory/deployed/web/index.html.polymer.bootstrap.dart.js",
]
}
diff --git a/mojo/dart/embedder/dart_debugger.cc b/mojo/dart/embedder/dart_debugger.cc
index 31e9ee4..684acc5 100644
--- a/mojo/dart/embedder/dart_debugger.cc
+++ b/mojo/dart/embedder/dart_debugger.cc
@@ -2,12 +2,11 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include <vector>
-
+#include "mojo/dart/embedder/dart_debugger.h"
#include "dart/runtime/include/dart_api.h"
#include "dart/runtime/include/dart_debugger_api.h"
#include "dart/runtime/include/dart_native_api.h"
-#include "mojo/dart/embedder/dart_debugger.h"
+
namespace mojo {
namespace dart {
@@ -45,10 +44,9 @@
intptr_t bp_id,
const Dart_CodeLocation& loc) {
Dart_EnterScope();
- DartDebuggerIsolate* debugger_isolate =
- FindIsolateById(isolate_id);
- CHECK(debugger_isolate != nullptr);
- debugger_isolate->MessageLoop();
+ intptr_t isolate_index = FindIsolateIndexById(isolate_id);
+ CHECK(isolate_index != -1);
+ (*isolates_)[isolate_index]->MessageLoop();
Dart_ExitScope();
}
@@ -56,10 +54,9 @@
Dart_Handle exception,
Dart_StackTrace stack_trace) {
Dart_EnterScope();
- DartDebuggerIsolate* debugger_isolate =
- FindIsolateById(isolate_id);
- CHECK(debugger_isolate != nullptr);
- debugger_isolate->MessageLoop();
+ intptr_t isolate_index = FindIsolateIndexById(isolate_id);
+ CHECK(isolate_index != -1);
+ (*isolates_)[isolate_index]->MessageLoop();
Dart_ExitScope();
}
@@ -69,11 +66,10 @@
if (kind == Dart_IsolateEvent::kCreated) {
AddIsolate(isolate_id);
} else {
- DartDebuggerIsolate* debugger_isolate =
- FindIsolateById(isolate_id);
- CHECK(debugger_isolate != nullptr);
+ intptr_t isolate_index = FindIsolateIndexById(isolate_id);
+ CHECK(isolate_index != -1);
if (kind == Dart_IsolateEvent::kInterrupted) {
- debugger_isolate->MessageLoop();
+ (*isolates_)[isolate_index]->MessageLoop();
} else {
CHECK(kind == Dart_IsolateEvent::kShutdown);
RemoveIsolate(isolate_id);
@@ -85,10 +81,9 @@
void DartDebugger::NotifyIsolate(Dart_Isolate isolate) {
base::AutoLock al(*lock_);
Dart_IsolateId isolate_id = Dart_GetIsolateId(isolate);
- DartDebuggerIsolate* debugger_isolate =
- FindIsolateByIdLocked(isolate_id);
- if (debugger_isolate != nullptr) {
- debugger_isolate->Notify();
+ intptr_t isolate_index = FindIsolateIndexByIdLocked(isolate_id);
+ if (isolate_index >= 0) {
+ (*isolates_)[isolate_index]->Notify();
}
}
@@ -98,40 +93,38 @@
Dart_SetBreakpointResolvedHandler(BptResolvedHandler);
Dart_SetExceptionThrownHandler(ExceptionThrownHandler);
lock_ = new base::Lock();
+ isolates_ = new std::vector<std::unique_ptr<DartDebuggerIsolate>>();
}
-DartDebuggerIsolate* DartDebugger::FindIsolateById(Dart_IsolateId id) {
+intptr_t DartDebugger::FindIsolateIndexById(Dart_IsolateId id) {
base::AutoLock al(*lock_);
- return FindIsolateByIdLocked(id);
+ return FindIsolateIndexByIdLocked(id);
}
-DartDebuggerIsolate* DartDebugger::FindIsolateByIdLocked(
+intptr_t DartDebugger::FindIsolateIndexByIdLocked(
Dart_IsolateId id) {
lock_->AssertAcquired();
- for (size_t i = 0; i < isolates_.size(); i++) {
- DartDebuggerIsolate* isolate = isolates_[i];
- if (id == isolate->id()) {
- return isolate;
+ for (size_t i = 0; i < isolates_->size(); i++) {
+ if ((*isolates_)[i]->id() == id) {
+ return i;
}
}
- return nullptr;
+ return -1;
}
-DartDebuggerIsolate* DartDebugger::AddIsolate(Dart_IsolateId id) {
+void DartDebugger::AddIsolate(Dart_IsolateId id) {
base::AutoLock al(*lock_);
- CHECK(FindIsolateByIdLocked(id) == nullptr);
- DartDebuggerIsolate* debugger_isolate =
- new DartDebuggerIsolate(id);
- isolates_.push_back(debugger_isolate);
- return debugger_isolate;
+ CHECK(FindIsolateIndexByIdLocked(id) == -1);
+ std::unique_ptr<DartDebuggerIsolate> debugger_isolate =
+ std::unique_ptr<DartDebuggerIsolate>(new DartDebuggerIsolate(id));
+ isolates_->push_back(std::move(debugger_isolate));
}
void DartDebugger::RemoveIsolate(Dart_IsolateId id) {
base::AutoLock al(*lock_);
- for (size_t i = 0; i < isolates_.size(); i++) {
- DartDebuggerIsolate* isolate = isolates_[i];
- if (id == isolate->id()) {
- isolates_.erase(isolates_.begin() + i);
+ for (size_t i = 0; i < isolates_->size(); i++) {
+ if (id == (*isolates_)[i]->id()) {
+ isolates_->erase(isolates_->begin() + i);
return;
}
}
@@ -139,7 +132,8 @@
}
base::Lock* DartDebugger::lock_ = nullptr;
-std::vector<DartDebuggerIsolate*> DartDebugger::isolates_;
+std::vector<std::unique_ptr<DartDebuggerIsolate>>* DartDebugger::isolates_ =
+ nullptr;
-} // namespace apps
+} // namespace dart
} // namespace mojo
diff --git a/mojo/dart/embedder/dart_debugger.h b/mojo/dart/embedder/dart_debugger.h
index fe7ef2d..7db7986 100644
--- a/mojo/dart/embedder/dart_debugger.h
+++ b/mojo/dart/embedder/dart_debugger.h
@@ -5,6 +5,9 @@
#ifndef MOJO_DART_EMBEDDER_DART_DEBUGGER_H_
#define MOJO_DART_EMBEDDER_DART_DEBUGGER_H_
+#include <memory>
+#include <vector>
+
#include "dart/runtime/include/dart_api.h"
#include "dart/runtime/include/dart_debugger_api.h"
#include "dart/runtime/include/dart_native_api.h"
@@ -60,16 +63,16 @@
static void NotifyIsolate(Dart_Isolate isolate);
- static DartDebuggerIsolate* FindIsolateById(Dart_IsolateId id);
+ static intptr_t FindIsolateIndexById(Dart_IsolateId id);
- static DartDebuggerIsolate* FindIsolateByIdLocked(Dart_IsolateId id);
+ static intptr_t FindIsolateIndexByIdLocked(Dart_IsolateId id);
- static DartDebuggerIsolate* AddIsolate(Dart_IsolateId id);
+ static void AddIsolate(Dart_IsolateId id);
static void RemoveIsolate(Dart_IsolateId id);
static base::Lock* lock_;
- static std::vector<DartDebuggerIsolate*> isolates_;
+ static std::vector<std::unique_ptr<DartDebuggerIsolate>>* isolates_;
friend class DartDebuggerIsolate;
};
diff --git a/mojo/dart/embedder/embedder.gni b/mojo/dart/embedder/embedder.gni
index b5d3de3..dd9dcdf 100644
--- a/mojo/dart/embedder/embedder.gni
+++ b/mojo/dart/embedder/embedder.gni
@@ -48,6 +48,7 @@
# String output (name of output file)
# List inputs (list of input files to be included)
# String table_name (name of symbol for resource table)
+# String root_prefix (base directory of resources)
# Optional invoker inputs:
# String input_directory (directory of resources that are recursively added)
# List deps
@@ -71,6 +72,8 @@
inputs = [ script ] + invoker.inputs
+ root_prefix = rebase_path(invoker.root_prefix)
+
args = [
"--output",
rebase_path(output),
@@ -81,7 +84,7 @@
"--table_name",
invoker.table_name,
"--root_prefix",
- rebase_path("//mojo/dart/embedder/"),
+ root_prefix,
]
if (defined(invoker.input_directory)) {
args += [
diff --git a/mojo/dart/embedder/test/BUILD.gn b/mojo/dart/embedder/test/BUILD.gn
index 96320c9..6796c97 100644
--- a/mojo/dart/embedder/test/BUILD.gn
+++ b/mojo/dart/embedder/test/BUILD.gn
@@ -16,6 +16,7 @@
"//mojo/public/dart/src/application_connection.dart",
"//mojo/public/dart/src/buffer.dart",
"//mojo/public/dart/src/codec.dart",
+ "//mojo/public/dart/src/control_message.dart",
"//mojo/public/dart/src/data_pipe.dart",
"//mojo/public/dart/src/drain_data.dart",
"//mojo/public/dart/src/event_stream.dart",
diff --git a/mojo/dart/embedder/test/run_dart_tests.cc b/mojo/dart/embedder/test/run_dart_tests.cc
index d0efe61..8d3ae1f 100644
--- a/mojo/dart/embedder/test/run_dart_tests.cc
+++ b/mojo/dart/embedder/test/run_dart_tests.cc
@@ -140,6 +140,10 @@
RunTest("exception_test.dart", false, nullptr, 0);
}
+TEST(DartTest, control_messages_test) {
+ RunTest("control_messages_test.dart", false, nullptr, 0);
+}
+
TEST(DartTest, handle_finalizer_test) {
const int kNumArgs = 2;
const char* args[kNumArgs];
diff --git a/mojo/dart/embedder/vmservice/server.dart b/mojo/dart/embedder/vmservice/server.dart
index 188b532..4e069f9 100644
--- a/mojo/dart/embedder/vmservice/server.dart
+++ b/mojo/dart/embedder/vmservice/server.dart
@@ -35,17 +35,9 @@
}
}
- post(var serial, dynamic result) {
+ post(dynamic result) {
try {
- if (serial == null && result is! String) {
- socket.add(result);
- } else {
- Map map = {
- 'id': serial,
- 'result': result
- };
- socket.add(JSON.encode(map));
- }
+ socket.add(result);
} catch (_) {
print("Ignoring error posting over WebSocket.");
}
@@ -67,7 +59,7 @@
HttpRequestClient(this.request, VMService service) : super(service);
- post(var serial, String result) {
+ post(String result) {
request.response..headers.contentType = jsonContentType
..write(result)
..close();
diff --git a/mojo/dart/mojom/AUTHORS b/mojo/dart/mojom/AUTHORS
new file mode 100644
index 0000000..2efca50
--- /dev/null
+++ b/mojo/dart/mojom/AUTHORS
@@ -0,0 +1,518 @@
+# Names should be added to this file with this pattern:
+#
+# For individuals:
+# Name <email address>
+#
+# For organizations:
+# Organization <fnmatch pattern>
+#
+# See python fnmatch module documentation for more information.
+
+Aaron Jacobs <samusaaron3@gmail.com>
+Aaron Leventhal <aaronlevbugs@gmail.com>
+Aaron Randolph <aaron.randolph@gmail.com>
+Abhishek Agarwal <abhishek.a21@samsung.com>
+Adam Roben <adam@github.com>
+Adam Treat <adam.treat@samsung.com>
+Addanki Gandhi Kishor <kishor.ag@samsung.com>
+Adenilson Cavalcanti <a.cavalcanti@samsung.com>
+Aditya Bhargava <heuristicist@gmail.com>
+Ajay Berwal <ajay.berwal@samsung.com>
+Ajith Kumar V <ajith.v@samsung.com>
+Alex Gartrell <agartrell@cmu.edu>
+Alex Henrie <alexhenrie24@gmail.com>
+Alex Scheele <alexscheele@gmail.com>
+Alexander Sulfrian <alexander@sulfrian.net>
+Alexandre Abreu <wiss1976@gmail.com>
+Alexandru Chiculita <achicu@adobe.com>
+Alexey Korepanov <alexkorep@gmail.com>
+Alexis Menard <alexis.menard@intel.com>
+Alfredo Hernandez <ahernandez.miralles@gmail.com>
+Ali Vathi <ali.akbar@gmail.com>
+Amit Sarkar <amit.srkr@samsung.com>
+Amogh Bihani <amogh.bihani@samsung.com>
+Amruth Raj <amruthraj@motorola.com>
+Amruth Raj <ckqr36@motorola.com>
+Anand Ratn <anand.ratn@samsung.com>
+Anastasios Cassiotis <tom.cassiotis@gmail.com>
+Ancil George <ancilgeorge@samsung.com>
+Andrei Parvu <andrei.prv@gmail.com>
+Andrei Parvu <parvu@adobe.com>
+Andrew Brampton <me@bramp.net>
+Andrew Tulloch <andrew@tullo.ch>
+Anish Patankar <anish.p@samsung.com>
+Ankit Kumar <ankit2.kumar@samsung.com>
+Anssi Hannula <anssi.hannula@iki.fi>
+Antonio Gomes <a1.gomes@sisa.samsung.com>
+Anuj Kumar Sharma <anujk.sharma@samsung.com>
+Arnaud Renevier <a.renevier@samsung.com>
+Arpita Bahuguna <a.bah@samsung.com>
+Arthur Lussos <developer0420@gmail.com>
+Arun Kulkarni <kulkarni.a@samsung.com>
+Arun Kumar <arun87.kumar@samsung.com>
+Arun Mankuzhi <arun.m@samsung.com>
+Arunoday Sarkar <a.sarkar.arun@gmail.com>
+Arunprasad Rajkumar <ararunprasad@gmail.com>
+Arunprasad Rajkumar <arurajku@cisco.com>
+Attila Dusnoki <dati91@gmail.com>
+Avinaash Doreswamy <avi.nitk@samsung.com>
+Balazs Kelemen <b.kelemen@samsung.com>
+Behara Mani Shyam Patro <behara.ms@samsung.com>
+Bem Jones-Bey <bemajaniman@gmail.com>
+Bem Jones-Bey <bjonesbe@adobe.com>
+Ben Karel <eschew@gmail.com>
+Benjamin Jemlich <pcgod99@gmail.com>
+Bernard Cafarelli <voyageur@gentoo.org>
+Bhanukrushana Rout <b.rout@samsung.com>
+Bobby Powers <bobbypowers@gmail.com>
+Brendan Long <self@brendanlong.com>
+Brian G. Merrell <bgmerrell@gmail.com>
+Brian Konzman, SJ <b.g.konzman@gmail.com>
+Brian Merrell, Novell Inc. <bgmerrell@gmail.com>
+Bruno Calvignac <bruno@flock.com>
+Bruno de Oliveira Abinader <bruno.d@partner.samsung.com>
+Bryan Donlan <bdonlan@gmail.com>
+Byungwoo Lee <bw80.lee@samsung.com>
+Caio Marcelo de Oliveira Filho <caio.de.oliveira.filho@intel.com>
+Caitlin Potter <caitpotter88@gmail.com>
+Catalin Badea <badea@adobe.com>
+Cem Kocagil <cem.kocagil@gmail.com>
+Chamal De Silva <chamalsl@yahoo.com>
+Chandra Shekar Vallala <brk376@motorola.com>
+Chang Shu <c.shu@samsung.com>
+ChangSeok Oh <shivamidow@gmail.com>
+Changbin Shao <changbin.shao@intel.com>
+Changjun Yang <changjun.yang@intel.com>
+Chansik Yun <chansik.yun@gmail.com>
+Chaobin Zhang <zhchbin@gmail.com>
+Chris Greene <cwgreene@amazon.com>
+Chris Harrelson <chrishtr@gmail.com>
+Christophe Dumez <ch.dumez@samsung.com>
+Christopher Dale <chrelad@gmail.com>
+Clemens Fruhwirth <clemens@endorphin.org>
+Clement Scheelfeldt Skau <clementskau@gmail.com>
+Clinton Staley <clintstaley@chromium.org>
+Clinton Staley <clintstaley@gmail.com>
+Craig Schlenter <craig.schlenter@gmail.com>
+Daegyu Lee <na7jun8gi@gmail.com>
+Dai Chunyang <chunyang.dai@intel.com>
+Daniel Bomar <dbdaniel42@gmail.com>
+Daniel Imms <daniimms@amazon.com>
+Daniel Johnson <danielj41@gmail.com>
+Daniel Nishi <dhnishi@gmail.com>
+Daniel Shaulov <dshaulov@ptc.com>
+Daniel Trebbien <dtrebbien@gmail.com>
+Darshini KN <kn.darshini@samsung.com>
+David Benjamin <davidben@mit.edu>
+David Erceg <erceg.david@gmail.com>
+David Futcher <david.mike.futcher@gmail.com>
+David McAllister <mcdavid@amazon.com>
+Deepak Dilip Borade <deepak.db@samsung.com>
+Deepak Mittal <deepak.m1@samsung.com>
+Deepak Singla <deepak.sa@samsung.com>
+Derek Halman <d.halman@gmail.com>
+Devlin Cronin <rdevlin.cronin@gmail.com>
+Diego Ferreiro Val <elfogris@gmail.com>
+Dillon Sellars <dill.sellars@gmail.com>
+Divya Bansal <divya.bansal@samsung.com>
+Dominic Jodoin <dominic.jodoin@gmail.com>
+Dominik Röttsches <dominik.rottsches@intel.com>
+Don Woodward <woodward@adobe.com>
+Dongjun Kim <djmix.kim@samsung.com>
+Dongseong Hwang <dongseong.hwang@intel.com>
+Dongwoo Joshua Im <dw.im@samsung.com>
+Douglas F. Turner <doug.turner@gmail.com>
+Eduardo Lima (Etrunko) <eduardo.lima@intel.com>
+Edward Crossman <tedoc2000@gmail.com>
+Eero Häkkinen <e.hakkinen@samsung.com>
+Egor Starkov <egor.starkov@samsung.com>
+Ehsan Akhgari <ehsan.akhgari@gmail.com>
+Elan Ruusamäe <elan.ruusamae@gmail.com>
+Eric Ahn <byungwook.ahn@gmail.com>
+Eric Rescorla <ekr@rtfm.com>
+Erik Hill <erikghill@gmail.com>
+Erik Sjölund <erik.sjolund@gmail.com>
+Eriq Augustine <eriq.augustine@gmail.com>
+Etienne Laurin <etienne@atnnn.com>
+Evan Peterson <evan.peterson.ep@gmail.com>
+Evan Wallace <evan.exe@gmail.com>
+Evangelos Foutras <evangelos@foutrelis.com>
+Evgeniy Dushistov <dushistov@gmail.com>
+Evgeny Agafonchikov <evgeny.agafonchikov@akvelon.com>
+Fabien Tassin <fta@sofaraway.org>
+Felix H. Dahlke <fhd@ubercode.de>
+Fernando Jiménez Moreno <ferjmoreno@gmail.com>
+François Beaufort <beaufort.francois@gmail.com>
+Francois Kritzinger <francoisk777@gmail.com>
+Frédéric Wang <fred.wang@free.fr>
+Gaetano Mendola <mendola@gmail.com>
+Gajendra N <gajendra.n@samsung.com>
+Gajendra Singh <wxjg68@motorola.com>
+Gao Chun <chun.gao@intel.com>
+Gao Chun <gaochun.dev@gmail.com>
+George Liaskos <geo.liaskos@gmail.com>
+Giuseppe Iuculano <giuseppe@iuculano.it>
+Glenn Adams <glenn@chromium.org>
+Gnanasekar Somanathan <gnanasekar.s@samsung.com>
+Gordana Cmiljanovic <gordana.cmiljanovic@imgtec.com>
+Goutham Jagannatha <wrm364@motorola.com>
+Gregory Davis <gpdavis.chromium@gmail.com>
+Grzegorz Czajkowski <g.czajkowski@samsung.com>
+Guangzhen Li <guangzhen.li@intel.com>
+Gurpreet Kaur <k.gurpreet@samsung.com>
+Gyuyoung Kim <gyuyoung.kim@samsung.com>
+Habib Virji <habib.virji@samsung.com>
+Haitao Feng <haitao.feng@intel.com>
+Halton Huo <halton.huo@intel.com>
+Haojian Wu <hokein.wu@gmail.com>
+Harpreet Singh Khurana <harpreet.sk@samsung.com>
+Hautio Kari <khautio@gmail.com>
+Heejin R. Chung <heejin.r.chung@samsung.com>
+Heeyoun Lee <heeyoun.lee@samsung.com>
+Himanshu Joshi <h.joshi@samsung.com>
+Hong Zheng <hong.zheng@intel.com>
+Hongbo Min <hongbo.min@intel.com>
+Horia Olaru <horia.olaru@gmail.com>
+Horia Olaru <olaru@adobe.com>
+Hosung You <hosung.you@samsung.com>
+Hwanseung Lee <rucifer1217@gmail.com>
+Hyunki Baik <hyunki.baik@samsung.com>
+Hyungwook Lee <withlhw@gmail.com>
+Ibrar Ahmed <ibrar.ahmad@gmail.com>
+Ian Hickson <github.com@spam.hixie.ch>
+Ion Rosca <rosca@adobe.com>
+J. Ryan Stinnett <jryans@chromium.org>
+Jacob Mandelson <jacob@mandelson.org>
+Jaehun Lim <ljaehun.lim@samsung.com>
+Jaekyeom Kim <btapiz@gmail.com>
+Jaime Soriano Pastor <jsorianopastor@gmail.com>
+Jake Helfert <jake@helfert.us>
+Jakob Weigert <jakob.j.w@googlemail.com>
+James Choi <jchoi42@pha.jhu.edu>
+James Vega <vega.james@gmail.com>
+James Wei <james.wei@intel.com>
+James Willcox <jwillcox@litl.com>
+Janwar Dinata <j.dinata@gmail.com>
+Jared Shumway <jaredshumway94@gmail.com>
+Jared Wein <weinjared@gmail.com>
+Jay Soffian <jaysoffian@gmail.com>
+Jeado Ko <haibane84@gmail.com>
+Jeongeun Kim <je_julie.kim@samsung.com>
+Jesse Miller <jesse@jmiller.biz>
+Jesus Sanchez-Palencia <jesus.sanchez-palencia.fernandez.fil@intel.com>
+Jiajia Qin <jiajia.qin@intel.com>
+Jie Chen <jie.a.chen@intel.com>
+Jihun Brent Kim <devgrapher@gmail.com>
+Jin Yang <jin.a.yang@intel.com>
+Jincheol Jo <jincheol.jo@navercorp.com>
+Jingwei Liu <kingweiliu@gmail.com>
+Jingyi Wei <wjywbs@gmail.com>
+Jinho Bang <jinho.bang@samsung.com>
+Jinwoo Song <jinwoo7.song@samsung.com>
+Jitendra Kumar Sahoo <jitendra.ks@samsung.com>
+Joe Knoll <joe.knoll@workday.com>
+Joe Thomas <mhx348@motorola.com>
+Joel Stanley <joel@jms.id.au>
+Johannes Rudolph <johannes.rudolph@googlemail.com>
+John McCutchan <john@johnmccutchan.com>
+John Yani <vanuan@gmail.com>
+John Yoo <nearbyh13@gmail.com>
+Jonathan Frazer <listedegarde@gmail.com>
+Jonathan Hacker <jhacker@arcanefour.com>
+Jongsoo Lee <leejongsoo@gmail.com>
+Joone Hur <joone.hur@intel.com>
+Jorge Villatoro <jorge@tomatocannon.com>
+Joseph Gentle <josephg@gmail.com>
+Josh Triplett <josh@joshtriplett.org>
+Josh Triplett <josh.triplett@intel.com>
+Joshua Lock <joshua.lock@intel.com>
+Joshua Roesslein <jroesslein@gmail.com>
+Josué Ratelle <jorat1346@gmail.com>
+Juhui Lee <juhui24.lee@samsung.com>
+Julien Racle <jracle@logitech.com>
+Jun Jiang <jun.a.jiang@intel.com>
+JungJik Lee <jungjik.lee@samsung.com>
+Junmin Zhu <junmin.zhu@intel.com>
+Kal Conley <kcconley@gmail.com>
+Kalyan Kondapally <kalyan.kondapally@intel.com>
+Kamil Jiwa <kamil.jiwa@gmail.com>
+Kangil Han <kangil.han@samsung.com>
+Kangyuan Shu <kangyuan.shu@intel.com>
+Kaspar Brand <googlecontrib@velox.ch>
+Kaustubh Atrawalkar <kaustubh.ra@gmail.com>
+Kaustubh Atrawalkar <kaustubh.a@samsung.com>
+Keene Pan <keenepan@linpus.com>
+Kenneth Rohde Christiansen <kenneth.r.christiansen@intel.com>
+Keonho Kim <keonho07.kim@samsung.com>
+Kevin Lee Helpingstine <sig11@reprehensible.net>
+Kevin M. McCormick <mckev@amazon.com>
+Kihong Kwon <kihong.kwon@samsung.com>
+Kim Christensen <kimworking@gmail.com>
+Kingshuk Jana <kingshuk.j@samsung.com>
+Klemen Forstnerič <klemen.forstneric@gmail.com>
+Krishna Chaitanya <krish.botta@samsung.com>
+Krzysztof Wolanski <k.wolanski@samsung.com>
+Kunal Thakar <kunalt@gmail.com>
+Kushal Pisavadia <kushi.p@gmail.com>
+Kwangho Shin <k_h.shin@samsung.com>
+Kyle Nahrgang <kpn24@drexel.edu>
+Kyungtae Kim <ktf.kim@samsung.com>
+Kyung Yeol Kim <chitacan@gmail.com>
+Laszlo Gombos <l.gombos@samsung.com>
+Laszlo Radanyi <bekkra@gmail.com>
+Lauren Yeun Kim <lauren.yeun.kim@gmail.com>
+Lauri Oherd <lauri.oherd@gmail.com>
+Leith Bade <leith@leithalweapon.geek.nz>
+Li Yin <li.yin@intel.com>
+Lionel Landwerlin <lionel.g.landwerlin@intel.com>
+Lorenzo Stoakes <lstoakes@gmail.com>
+Lu Guanqun <guanqun.lu@gmail.com>
+Lucie Brozkova <lucinka.brozkova@gmail.com>
+Luke Inman-Semerau <luke.semerau@gmail.com>
+Luke Zarko <lukezarko@gmail.com>
+Maarten Lankhorst <m.b.lankhorst@gmail.com>
+Magnus Danielsson <fuzzac@gmail.com>
+Mahesh Kulkarni <mahesh.kk@samsung.com>
+Malcolm Wang <malcolm.2.wang@gmail.com>
+Manish Chhajer <chhajer.m@samsung.com>
+Manuel Braun <thembrown@gmail.com>
+Mao Yujie <maojie0924@gmail.com>
+Mao Yujie <yujie.mao@intel.com>
+Marco Rodrigues <gothicx@gmail.com>
+Mario Sanchez Prada <mario.prada@samsung.com>
+Mark Hahnenberg <mhahnenb@andrew.cmu.edu>
+Mark Seaborn <mrs@mythic-beasts.com>
+Martin Bednorz <m.s.bednorz@gmail.com>
+Masahiro Yado <yado.masa@gmail.com>
+Matheus Bratfisch <matheusbrat@gmail.com>
+Mathias Bynens <mathias@qiwi.be>
+Mathieu Meisser <mmeisser@logitech.com>
+Matt Arpidone <mma.public@gmail.com>
+Matthew Robertson <matthewrobertson03@gmail.com>
+Matthew Turk <matthewturk@gmail.com>
+Matthew Willis <appamatto@gmail.com>
+Matthias Reitinger <reimarvin@gmail.com>
+Max Perepelitsyn <pph34r@gmail.com>
+Max Vujovic <mvujovic@adobe.com>
+Mayur Kankanwadi <mayurk.vk@samsung.com>
+Michael Gilbert <floppymaster@gmail.com>
+Michael Schechter <mike.schechter@gmail.com>
+Michael Zugelder <michael@zugelder.org>
+Mihai Maerean <mmaerean@adobe.com>
+Mihai Tica <mihai.o.tica@gmail.com>
+Mihai Tica <mitica@adobe.com>
+Mike Tilburg <mtilburg@adobe.com>
+Mikhail Pozdnyakov <mikhail.pozdnyakov@intel.com>
+Mingmin Xie <melvinxie@gmail.com>
+Mirela Budaes <mbudaes@gmail.com>
+Mirela Budaes <mbudaes@adobe.com>
+Mitchell Rosen <mitchellwrosen@chromium.org>
+Miyoung Shin <myid.shin@samsung.com>
+Mohamed I. Hammad <ibraaaa@gmail.com>
+Mohamed Mansour <m0.interactive@gmail.com>
+Mohammed Wajahat Ali Siddiqui <wajahat.s@samsung.com>
+Mohan Reddy <mohan.reddy@samsung.com>
+Mrunal Kapade <mrunal.kapade@intel.com>
+Myles C. Maxfield <mymax@amazon.com>
+Nagarjuna Atluri <nagarjuna.a@samsung.com>
+Naiem Shaik <naiem.shaik@gmail.com>
+Naoki Takano <takano.naoki@gmail.com>
+Naveen Bobbili <naveenbobbili@motorola.com>
+Naveen Bobbili <qghc36@motorola.com>
+Naveen Kumar S G <naveensg@samsung.com>
+Nayan Kumar K <qtc746@motorola.com>
+Nedeljko Babic <nedeljko.babic@imgtec.com>
+Nikhil Bansal <n.bansal@samsung.com>
+Nikita Ofitserov <himikof@gmail.com>
+Ningxin Hu <ningxin.hu@intel.com>
+Nitish Mehrotra <nitish.m@samsung.com>
+Pan Deng <pan.deng@intel.com>
+Parag Radke <nrqv63@motorola.com>
+Patrasciuc Sorin Cristian <cristian.patrasciuc@gmail.com>
+Patrick Riordan <patrickriordan177@gmail.com>
+Paul Adolph <padolph@netflix.com>
+Paul Kehrer <paul.l.kehrer@gmail.com>
+Paul Lind <paul.lind@imgtec.com>
+Paul Nettleship <pnettleship@gmail.com>
+Paul Robinson <paulrobinson85@googlemail.com>
+Paul Roskell <blurrech@gmail.com>
+Paul Wicks <pwicks86@gmail.com>
+Pavan Kumar Emani <pavan.e@samsung.com>
+Pavel Ivanov <paivanof@gmail.com>
+Paweł Hajdan jr <phajdan.jr@gmail.com>
+Peng Jiang <leiyi.jp@gmail.com>
+Petar Jovanovic <petarj@mips.com>
+Peter Beverloo <peter@chromium.org>
+Peter Bright <drpizza@quiscalusmexicanus.org>
+Peter Brophy <pbrophy@adobe.com>
+Peter Collingbourne <peter@pcc.me.uk>
+Peter Gal <pgal.u-szeged@partner.samsung.com>
+Peter Molnar <pmolnar.u-szeged@partner.samsung.com>
+Philippe Beauchamp <philippe.beauchamp@gmail.com>
+Philippe Beaudoin <philippe.beaudoin@gmail.com>
+Pierre-Antoine LaFayette <pierre.lafayette@gmail.com>
+Po-Chun Chang <pochang0403@gmail.com>
+Pramod Begur Srinath <pramod.bs@samsung.com>
+Prashant Hiremath <prashhir@cisco.com>
+Prashant Nevase <prashant.n@samsung.com>
+Praveen Akkiraju <praveen.anp@samsung.com>
+Pritam Nikam <pritam.nikam@samsung.com>
+Puttaraju R <puttaraju.r@samsung.com>
+Qiankun Miao <qiankun.miao@intel.com>
+Qing Zhang <qing.zhang@intel.com>
+Radu Stavila <stavila@adobe.com>
+Raghavendra Ghatage <r.ghatage@samsung.com>
+Raman Tenneti <raman.tenneti@gmail.com>
+Ramkumar Gokarnesan <ramkumar.gokarnesan@gmail.com>
+Ramkumar Ramachandra <artagnon@gmail.com>
+Randy Posynick <randy.posynick@gmail.com>
+Raphael Kubo da Costa <raphael.kubo.da.costa@intel.com>
+Ravi Phaneendra Kasibhatla <r.kasibhatla@samsung.com>
+Ravi Phaneendra Kasibhatla <ravi.kasibhatla@motorola.com>
+Renata Hodovan <rhodovan.u-szeged@partner.samsung.com>
+Rene Bolldorf <rb@radix.io>
+Rene Ladan <r.c.ladan@gmail.com>
+Rijubrata Bhaumik <rijubrata.bhaumik@intel.com>
+Rob Buis <rob.buis@samsung.com>
+Rob Wu <rob@robwu.nl>
+Robert Bear Travis <bear.travis@gmail.com>
+Robert Bear Travis <betravis@adobe.com>
+Robert Bradford <robert.bradford@intel.com>
+Robert Goldberg <goldberg@adobe.com>
+Robert Hogan <robhogan@gmail.com>
+Robert Nagy <robert.nagy@gmail.com>
+Robert Sesek <rsesek@bluestatic.org>
+Roland Takacs <rtakacs.u-szeged@partner.samsung.com>
+Rosen Dash <nqk836@motorola.com>
+Rosen Dash <rosen.dash@gmail.com>
+ruben <chromium@hybridsource.org>
+Ruben Terrazas <rubentopo@gmail.com>
+Ruiyi Luo <luoruiyi2008@gmail.com>
+Ryan Norton <rnorton10@gmail.com>
+Ryan Sleevi <ryan-chromium-dev@sleevi.com>
+Ryuan Choi <ryuan.choi@samsung.com>
+Saikrishna Arcot <saiarcot895@gmail.com>
+Salvatore Iovene <salvatore.iovene@intel.com>
+Sam Larison <qufighter@gmail.com>
+Sam McDonald <sam@sammcd.com>
+Sanjoy Pal <ncj674@motorola.com>
+Sanjoy Pal <sanjoy.pal@samsung.com>
+Sanne Wouda <sanne.wouda@gmail.com>
+Sarath Singapati <s.singapati@samsung.com>
+Saravanan KR <sramajay@cisco.com>
+Sathish Kuppuswamy <sathish.kuppuswamy@intel.com>
+Satoshi Matsuzaki <satoshi.matsuzaki@gmail.com>
+Sayan Nayak <sayan.nayak@samsung.com>
+Scott Blomquist <sblom@microsoft.com>
+Sean Bryant <sean@cyberwang.net>
+Seo Sanghyeon <sanxiyn@gmail.com>
+Seokju Kwon <seokju.kwon@gmail.com>
+Sergey Putilin <p.sergey@samsung.com>
+Sergio Carlos Morales Angeles <carloschilazo@gmail.com>
+Sergiy Byelozyorov <rryk.ua@gmail.com>
+Seshadri Mahalingam <seshadri.mahalingam@gmail.com>
+Sevan Janiyan <venture37@geeklan.co.uk>
+ShankarGanesh K <blr.bmlab@gmail.com>
+Shez Baig <sbaig1@bloomberg.net>
+Shiliu Wang <aofdwsl@gmail.com>
+Shiliu Wang <shiliu.wang@intel.com>
+Shilpa Shri <shilpa.shri@samsung.com>
+Shouqun Liu <shouqun.liu@intel.com>
+Shreeram Kushwaha <shreeram.k@samsung.com>
+Shreyas Gopal <shreyas.g@samsung.com>
+Shreyas VA <v.a.shreyas@gmail.com>
+Siba Samal <siba.samal@samsung.com>
+Simon Arlott <simon.arlott@gmail.com>
+Siva Kumar Gunturi <siva.gunturi@samsung.com>
+Sohan Jyoti Ghosh <sohan.jyoti@samsung.com>
+Song YeWen <ffmpeg@gmail.com>
+Soren Dreijer <dreijerbit@gmail.com>
+Srirama Chandra Sekhar Mogali <srirama.m@samsung.com>
+Stephen Searles <stephen.searles@gmail.com>
+Steven Pennington <spenn@engr.uvic.ca>
+Subrahmanya Praveen Munukutla <sataya.m@samsung.com>
+Suchit Agrawal <a.suchit@samsung.com>
+Sudarsana Babu Nagineni <sudarsana.nagineni@intel.com>
+Sudarshan Parthasarathy <sudarshan.p@samsung.com>
+Sungguk Lim <limasdf@gmail.com>
+Sungmann Cho <sungmann.cho@gmail.com>
+Sungmann Cho <sungmann.cho@navercorp.com>
+Suyash Sengar <suyash.s@samsung.com>
+Sunil Ratnu <sunil.ratnu@samsung.com>
+Sylvain Zimmer <sylvinus@gmail.com>
+Szymon Piechowicz <szymonpiechowicz@o2.pl>
+Takeshi Kurosawa <taken.spc@gmail.com>
+Tapu Kumar Ghose <ghose.tapu@gmail.com>
+Taylor Price <trprice@gmail.com>
+Ted Kim <neot0000@gmail.com>
+Ted Vessenes <tedvessenes@gmail.com>
+Teodora Novkovic <teodora.petrovic@gmail.com>
+Thiago Farina <thiago.farina@gmail.com>
+Thiago Marcos P. Santos <thiago.santos@intel.com>
+Thomas Butter <tbutter@gmail.com>
+Tiago Vignatti <tiago.vignatti@intel.com>
+Tim Ansell <mithro@mithis.com>
+Timo Reimann <ttr314@googlemail.com>
+Torsten Kurbad <google@tk-webart.de>
+Trevor Perrin <unsafe@trevp.net>
+U. Artie Eoff <ullysses.a.eoff@intel.com>
+Umar Hansa <umar.hansa@gmail.com>
+Vamshikrishna Yellenki <vamshi@motorola.com>
+Vani Hegde <vani.hegde@samsung.com>
+Vedran Šajatović <vedran.sajatovic@gmail.com>
+Vernon Tang <vt@foilhead.net>
+Viatcheslav Ostapenko <sl.ostapenko@samsung.com>
+Victor Costan <costan@gmail.com>
+Viet-Trung Luu <viettrungluu@gmail.com>
+Vinay Anantharaman <vinaya@adobe.com>
+Vipul Bhasin <vipul.bhasin@gmail.com>
+Visa Putkinen <v.putkinen@partner.samsung.com>
+Vivek Galatage <vivek.vg@samsung.com>
+Will Hirsch <chromium@willhirsch.co.uk>
+Xiang Long <xiang.long@intel.com>
+Xinchao He <hexinchao@gmail.com>
+Xing Zhang <xzhang@adobe.com>
+Xu Samuel <samuel.xu@intel.com>
+Xuefei Ren <xrenishere@gmail.com>
+Xun Sun <xun.sun@intel.com>
+Yael Aharon <yael.aharon@intel.com>
+Yair Yogev <progame@chromium.org>
+Yang Gu <yang.gu@intel.com>
+Yarin Kaul <yarin.kaul@gmail.com>
+Ye Liu <cbakgly@gmail.com>
+Yi Shen <yi.shen@samsung.com>
+Yoav Weiss <yoav@yoav.ws>
+Yoav Zilberberg <yoav.zilberberg@gmail.com>
+Yong Shin <sy3620@gmail.com>
+Yongsheng Zhu <yongsheng.zhu@intel.com>
+Yoshinori Sano <yoshinori.sano@gmail.com>
+YoungKi Hong <simon.hong81@gmail.com>
+Youngsun Suh <zard17@gmail.com>
+Yumikiyo Osanai <yumios.art@gmail.com>
+Yuri Gorobets <yuri.gorobets@gmail.com>
+Zeno Albisser <zeno.albisser@digia.com>
+Zhaoze Zhou <zhaoze.zhou@partner.samsung.com>
+Zheng Chuang <zhengchuangscu@gmail.com>
+Zhenyu Liang <zhenyu.liang@intel.com>
+Zhenyu Shan <zhenyu.shan@intel.com>
+Ziran Sun <ziran.sun@samsung.com>
+Yongha Lee <yongha78.lee@samsung.com>
+方觉 (Fang Jue) <fangjue23303@gmail.com>
+Yupei Wang <perryuwang@tencent.com>
+Peng Hu <penghu@tencent.com>
+WenSheng He <wensheng.he@samsung.com>
+Raghu Ram Nagaraj <r.nagaraj@samsung.com>
+
+BlackBerry Limited <*@blackberry.com>
+Code Aurora Forum <*@codeaurora.org>
+Comodo CA Limited
+Google Inc. <*@google.com>
+Igalia S.L. <*@igalia.com>
+NVIDIA Corporation <*@nvidia.com>
+Opera Software ASA <*@opera.com>
+The Chromium Authors <*@chromium.org>
+The MathWorks, Inc. <binod.pant@mathworks.com>
+Torchmobile Inc.
+Venture 3 Systems LLC <*@venture3systems.com>
+Yandex LLC <*@yandex-team.ru>
+ARM Holdings <*@arm.com>
diff --git a/mojo/dart/mojom/CHANGELOG.md b/mojo/dart/mojom/CHANGELOG.md
new file mode 100644
index 0000000..9ab89f3
--- /dev/null
+++ b/mojo/dart/mojom/CHANGELOG.md
@@ -0,0 +1,3 @@
+## 0.0.1
+ * Initial placeholder package for generated mojom bindings.
+
diff --git a/mojo/dart/mojom/LICENSE b/mojo/dart/mojom/LICENSE
new file mode 100644
index 0000000..a32e00c
--- /dev/null
+++ b/mojo/dart/mojom/LICENSE
@@ -0,0 +1,27 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/mojo/dart/mojom/README.md b/mojo/dart/mojom/README.md
new file mode 100644
index 0000000..8932851
--- /dev/null
+++ b/mojo/dart/mojom/README.md
@@ -0,0 +1,17 @@
+mojom
+====
+
+This package is a placeholder for generated mojom bindings.
+
+Generated Mojo bindings in other pub packages should be installed into this
+package by saying the following after `pub get`:
+
+```
+$ pub run mojom:generate
+```
+If desired, additional directories holding .mojom.dart files can be specified;
+their contents will be installed to this package as well:
+
+```
+$ pub run mojom:generate -a </path/to/mojom/dir>
+```
diff --git a/mojo/dart/mojom/bin/generate.dart b/mojo/dart/mojom/bin/generate.dart
new file mode 100644
index 0000000..e579655
--- /dev/null
+++ b/mojo/dart/mojom/bin/generate.dart
@@ -0,0 +1,135 @@
+// Copyright 2015 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.
+
+/*
+* This script should be invoked via 'pub run' for an application that consumes
+* pub packages containing generated Mojo bindings (.mojom.dart files).
+*
+* It should be invoked as follows after 'pub get':
+* $ pub run mojom:generate
+*/
+
+import 'dart:async';
+import 'dart:io';
+
+import 'package:args/args.dart' as args;
+import 'package:path/path.dart' as path;
+
+bool verbose;
+bool dryRun;
+
+bool isMojomDart(String path) => path.endsWith('.mojom.dart');
+
+// Copies .mojom.dart files from a particular package's mojom directory to
+// the mojom package directory.
+//
+// mojomDirectory: A pub packages mojom directory, i.e. $PACKAGE/mojom.
+// mojomPackage: The mojom package directory.
+copyMojomDirContents(Directory mojomDirectory, Directory mojomPackage) async {
+ assert(await mojomDirectory.exists());
+ await for (var mojoms in mojomDirectory.list(recursive: true)) {
+ if (mojoms is! File) continue;
+ if (!isMojomDart(mojoms.path)) continue;
+ if (verbose) print("Found $mojoms");
+
+ final relative = path.relative(mojoms.path, from: mojomDirectory.path);
+ final dest = path.join(mojomPackage.path, relative);
+ final destDirectory = new Directory(path.dirname(dest));
+
+ if (verbose || dryRun) {
+ print('Copying $mojoms to $dest');
+ }
+
+ if (!dryRun) {
+ final File source = new File(mojoms.path);
+ if (verbose) print("Ensuring $destDirectory exists");
+ await destDirectory.create(recursive: true);
+ source.copy(dest);
+ }
+ }
+}
+
+// If a package has a mojom directory, uses copyMojomDirContents to install them
+// in the mojom package.
+//
+// package: The directory of a pub package in an application's packages
+// directory.
+// mojoPackage: The mojom package directory in an application's packages
+// directory
+installPackageMojoms(Directory package, Directory mojomPackage) async {
+ if (package.path == mojomPackage.path) return;
+ if (verbose) print("package = $package");
+ final mojomDirectory = new Directory(path.join(package.path, 'mojom'));
+ if (verbose) print("looking for = $mojomDirectory");
+ if (await mojomDirectory.exists()) {
+ copyMojomDirContents(mojomDirectory, mojomPackage);
+ } else if (verbose) {
+ print("$mojomDirectory not found");
+ }
+}
+
+main(List<String> arguments) async {
+ final parser = new args.ArgParser()
+ ..addOption('package-root',
+ abbr: 'p',
+ defaultsTo: path.join(Directory.current.path, 'packages'),
+ help: 'An absolute path to an application\'s package root')
+ ..addOption('additional-mojom-dir',
+ abbr: 'a',
+ allowMultiple: true,
+ help: 'Absolute path to an additional directory containing mojom.dart'
+ 'files to put in the mojom package. May be specified multiple times.')
+ ..addFlag('dry-run',
+ abbr: 'd',
+ defaultsTo: false,
+ help: 'Print the copy operations that would have been run, but'
+ 'do not copy anything.')
+ ..addFlag('verbose', abbr: 'v', defaultsTo: false);
+ final result = parser.parse(arguments);
+ verbose = result['verbose'];
+ dryRun = result['dry-run'];
+
+ final packages = new Directory(result['package-root']);
+ if (!packages.isAbsolute) {
+ print("The --package-root parameter should be an absolute path.");
+ exit(1);
+ }
+ if (verbose) print("packages = $packages");
+ if (!(await packages.exists())) {
+ print("The packages directory $packages does not exist");
+ exit(1);
+ }
+
+ var additional_mojom_dirs = [];
+ for (var mojom_dir_path in result['additional-mojom-dir']) {
+ final mojom_dir = new Directory(mojom_dir_path);
+ if (!mojom_dir.isAbsolute) {
+ print("All --additional-mojom-dir parameters should be absolute paths.");
+ exit(1);
+ }
+ if (!(await mojom_dir.exists())) {
+ print("The additional mojom directory $mojom_dir does not exist");
+ exit(1);
+ }
+ additional_mojom_dirs.add(mojom_dir);
+ }
+ if (verbose) print("additional_mojom_dirs = $additional_mojom_dirs");
+
+ final mojomPackage = new Directory(path.join(packages.path, 'mojom'));
+ if (verbose) print("mojom package = $mojomPackage");
+ if (!(await mojomPackage.exists())) {
+ print("The mojom package directory $mojomPackage does not exist");
+ exit(1);
+ }
+
+ await for (var package in packages.list()) {
+ if (package is Directory) {
+ installPackageMojoms(package, mojomPackage);
+ }
+ }
+
+ for (var mojom_dir in additional_mojom_dirs) {
+ copyMojomDirContents(mojom_dir, mojomPackage);
+ }
+}
diff --git a/mojo/dart/mojom/lib/README.md b/mojo/dart/mojom/lib/README.md
new file mode 100644
index 0000000..f608b86
--- /dev/null
+++ b/mojo/dart/mojom/lib/README.md
@@ -0,0 +1,8 @@
+mojom
+====
+
+Files will appear here after running the generate script (see the top-level
+documentation for this package).
+
+This file is a placeholder to ensure that the mojom package gets installed
+under "packages/" when it is added as a dependency.
diff --git a/mojo/dart/mojom/pubspec.lock b/mojo/dart/mojom/pubspec.lock
new file mode 100644
index 0000000..053fdb2
--- /dev/null
+++ b/mojo/dart/mojom/pubspec.lock
@@ -0,0 +1,11 @@
+# Generated by pub
+# See http://pub.dartlang.org/doc/glossary.html#lockfile
+packages:
+ args:
+ description: args
+ source: hosted
+ version: "0.13.0"
+ path:
+ description: path
+ source: hosted
+ version: "1.3.5"
diff --git a/mojo/dart/mojom/pubspec.yaml b/mojo/dart/mojom/pubspec.yaml
new file mode 100644
index 0000000..36bcd5f
--- /dev/null
+++ b/mojo/dart/mojom/pubspec.yaml
@@ -0,0 +1,10 @@
+name: mojom
+version: 0.0.3
+author: Chromium Authors <mojo-dev@googlegroups.com>
+description: Placeholder for mojom bindings.
+homepage: https://github.com/domokit/mojo
+dependencies:
+ path: '>=1.3.5 <2.0.0'
+ args: '>=0.13.0 <1.0.0'
+environment:
+ sdk: '>=1.9.0 <2.0.0'
diff --git a/mojo/dart/test/control_messages_test.dart b/mojo/dart/test/control_messages_test.dart
new file mode 100644
index 0000000..38cdf92
--- /dev/null
+++ b/mojo/dart/test/control_messages_test.dart
@@ -0,0 +1,108 @@
+// Copyright 2015 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.
+
+import 'dart:async';
+import 'dart:isolate';
+import 'dart:typed_data';
+
+import 'package:mojo/dart/testing/expect.dart';
+import 'package:mojo/public/dart/bindings.dart' as bindings;
+import 'package:mojo/public/dart/core.dart' as core;
+import 'package:mojom/sample/sample_interfaces.mojom.dart' as sample;
+
+// Bump this if sample_interfaces.mojom adds higher versions.
+const maxVersion = 3;
+
+// Implementation of IntegerAccessor.
+class IntegerAccessorImpl implements sample.IntegerAccessor {
+ // Some initial value.
+ int _value = 0;
+
+ Future<sample.IntegerAccessorGetIntegerResponseParams>
+ getInteger([Function responseFactory = null]) {
+ return new Future.value(responseFactory(_value, sample.Enum_VALUE));
+ }
+
+ void setInteger(int data, int type) {
+ Expect.equals(sample.Enum_VALUE, type);
+ // Update data.
+ _value = data;
+ }
+}
+
+// Returns [proxy, stub].
+List buildConnectedProxyAndStub() {
+ var pipe = new core.MojoMessagePipe();
+ var proxy = new sample.IntegerAccessorProxy.fromEndpoint(pipe.endpoints[0]);
+ var impl = new IntegerAccessorImpl();
+ var stub =
+ new sample.IntegerAccessorStub.fromEndpoint(pipe.endpoints[1], impl);
+ return [proxy, stub];
+}
+
+void closeProxyAndStub(List ps) {
+ var proxy = ps[0];
+ var stub = ps[1];
+ proxy.close();
+ stub.close();
+}
+
+testQueryVersion() async {
+ var ps = buildConnectedProxyAndStub();
+ var proxy = ps[0];
+ // The version starts at 0.
+ Expect.equals(0, proxy.version);
+ // We are talking to an implementation that supports version maxVersion.
+ var providedVersion = await proxy.queryVersion();
+ Expect.equals(maxVersion, providedVersion);
+ // The proxy's version has been updated.
+ Expect.equals(providedVersion, proxy.version);
+ closeProxyAndStub(ps);
+}
+
+testRequireVersionSuccess() async {
+ var ps = buildConnectedProxyAndStub();
+ var proxy = ps[0];
+ Expect.equals(0, proxy.version);
+ // Require version maxVersion.
+ proxy.requireVersion(maxVersion);
+ // Make a request and get a response.
+ var response = await proxy.ptr.getInteger();
+ Expect.equals(0, response.data);
+ closeProxyAndStub(ps);
+}
+
+testRequireVersionDisconnect() async {
+ var ps = buildConnectedProxyAndStub();
+ var proxy = ps[0];
+ Expect.equals(0, proxy.version);
+ // Require version maxVersion.
+ proxy.requireVersion(maxVersion);
+ Expect.equals(maxVersion, proxy.version);
+ // Set integer.
+ proxy.ptr.setInteger(34, sample.Enum_VALUE);
+ // Get integer.
+ var response = await proxy.ptr.getInteger();
+ Expect.equals(34, response.data);
+ // Require version maxVersion + 1
+ proxy.requireVersion(maxVersion + 1);
+ // Version number is updated synchronously.
+ Expect.equals(maxVersion + 1, proxy.version);
+ // Get integer, expect a failure.
+ bool exceptionCaught = false;
+ try {
+ response = await proxy.ptr.getInteger();
+ Expect.fail('Should have an exception.');
+ } catch(e) {
+ exceptionCaught = true;
+ }
+ Expect.isTrue(exceptionCaught);
+ closeProxyAndStub(ps);
+}
+
+main() async {
+ await testQueryVersion();
+ await testRequireVersionSuccess();
+ await testRequireVersionDisconnect();
+}
diff --git a/mojo/devtools/common/devtoolslib/apptest_dart.py b/mojo/devtools/common/devtoolslib/apptest_dart.py
index b21ac84..30946ab 100644
--- a/mojo/devtools/common/devtoolslib/apptest_dart.py
+++ b/mojo/devtools/common/devtoolslib/apptest_dart.py
@@ -13,12 +13,11 @@
def _dart_apptest_output_test(output):
- # Fail on output with dart unittests' "FAIL:"/"ERROR:" or a lack of "PASS:".
+ # Fail on output with dart unittests' "FAIL:" or a lack of "PASS:".
# The latter condition ensures failure on broken command lines or output.
# Check output instead of exit codes because mojo_shell always exits with 0.
if (not output or
'\nFAIL: ' in output or
- '\nERROR: ' in output or
'\nPASS: ' not in output):
return False
return True
diff --git a/mojo/public/cpp/application/application_impl.h b/mojo/public/cpp/application/application_impl.h
index 8bc970a..294cfff 100644
--- a/mojo/public/cpp/application/application_impl.h
+++ b/mojo/public/cpp/application/application_impl.h
@@ -99,6 +99,13 @@
// Quits the main run loop for this application.
static void Terminate();
+ protected:
+ // Application implementation.
+ void AcceptConnection(const String& requestor_url,
+ InterfaceRequest<ServiceProvider> services,
+ ServiceProviderPtr exposed_services,
+ const String& url) override;
+
private:
class ShellPtrWatcher;
@@ -111,11 +118,6 @@
}
// Application implementation.
- void AcceptConnection(const String& requestor_url,
- InterfaceRequest<ServiceProvider> services,
- ServiceProviderPtr exposed_services,
- const String& url) override;
-
void RequestQuit() override;
typedef std::vector<internal::ServiceRegistry*> ServiceRegistryList;
diff --git a/mojo/public/cpp/bindings/tests/union_unittest.cc b/mojo/public/cpp/bindings/tests/union_unittest.cc
index 3253232..65a23fa 100644
--- a/mojo/public/cpp/bindings/tests/union_unittest.cc
+++ b/mojo/public/cpp/bindings/tests/union_unittest.cc
@@ -4,6 +4,7 @@
#include <vector>
#include "mojo/public/cpp/bindings/array.h"
+#include "mojo/public/cpp/bindings/binding.h"
#include "mojo/public/cpp/bindings/lib/array_internal.h"
#include "mojo/public/cpp/bindings/lib/array_serialization.h"
#include "mojo/public/cpp/bindings/lib/bounds_checker.h"
@@ -11,6 +12,7 @@
#include "mojo/public/cpp/bindings/string.h"
#include "mojo/public/cpp/environment/environment.h"
#include "mojo/public/cpp/test_support/test_utils.h"
+#include "mojo/public/cpp/utility/run_loop.h"
#include "mojo/public/interfaces/bindings/tests/test_structs.mojom.h"
#include "mojo/public/interfaces/bindings/tests/test_unions.mojom.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -1043,5 +1045,90 @@
free(raw_buf);
}
+class SmallCacheImpl : public SmallCache {
+ public:
+ SmallCacheImpl() : int_value_(0) {}
+ ~SmallCacheImpl() override {}
+ int64_t int_value() const { return int_value_; }
+
+ private:
+ void SetIntValue(int64_t int_value) override { int_value_ = int_value; }
+ void GetIntValue(const GetIntValueCallback& callback) override {
+ callback.Run(int_value_);
+ }
+
+ int64_t int_value_;
+};
+
+TEST(UnionTest, InterfaceInUnion) {
+ Environment env;
+ RunLoop run_loop;
+ SmallCacheImpl impl;
+ SmallCachePtr ptr;
+ Binding<SmallCache> bindings(&impl, GetProxy(&ptr));
+
+ HandleUnionPtr handle(HandleUnion::New());
+ handle->set_f_small_cache(ptr.Pass());
+
+ handle->get_f_small_cache()->SetIntValue(10);
+ run_loop.RunUntilIdle();
+ EXPECT_EQ(10, impl.int_value());
+}
+
+TEST(UnionTest, InterfaceInUnionSerialization) {
+ Environment env;
+ RunLoop run_loop;
+ SmallCacheImpl impl;
+ SmallCachePtr ptr;
+ Binding<SmallCache> bindings(&impl, GetProxy(&ptr));
+
+ HandleUnionPtr handle(HandleUnion::New());
+ handle->set_f_small_cache(ptr.Pass());
+ size_t size = GetSerializedSize_(handle, false);
+ EXPECT_EQ(16U, size);
+
+ mojo::internal::FixedBuffer buf(size);
+ internal::HandleUnion_Data* data = nullptr;
+ SerializeUnion_(handle.Pass(), &buf, &data, false);
+
+ std::vector<Handle> handles;
+ data->EncodePointersAndHandles(&handles);
+ EXPECT_EQ(1U, handles.size());
+ data->DecodePointersAndHandles(&handles);
+
+ HandleUnionPtr handle2(HandleUnion::New());
+ Deserialize_(data, &handle2);
+
+ handle2->get_f_small_cache()->SetIntValue(10);
+ run_loop.RunUntilIdle();
+ EXPECT_EQ(10, impl.int_value());
+}
+
+class UnionInterfaceImpl : public UnionInterface {
+ public:
+ UnionInterfaceImpl() {}
+ ~UnionInterfaceImpl() override {}
+
+ private:
+ void Echo(PodUnionPtr in, const EchoCallback& callback) override {
+ callback.Run(in.Pass());
+ }
+};
+
+TEST(UnionTest, UnionInInterface) {
+ Environment env;
+ RunLoop run_loop;
+ UnionInterfaceImpl impl;
+ UnionInterfacePtr ptr;
+ Binding<UnionInterface> bindings(&impl, GetProxy(&ptr));
+
+ PodUnionPtr pod(PodUnion::New());
+ pod->set_f_int16(16);
+
+ ptr->Echo(pod.Pass(),
+ [](PodUnionPtr out) { EXPECT_EQ(16, out->get_f_int16()); });
+ run_loop.RunUntilIdle();
+}
+
} // namespace test
} // namespace mojo
diff --git a/mojo/public/dart/BUILD.gn b/mojo/public/dart/BUILD.gn
index 711eee3..6d0be78 100644
--- a/mojo/public/dart/BUILD.gn
+++ b/mojo/public/dart/BUILD.gn
@@ -9,6 +9,7 @@
"src/application.dart",
"src/buffer.dart",
"src/codec.dart",
+ "src/control_message.dart",
"src/data_pipe.dart",
"src/drain_data.dart",
"src/event_stream.dart",
@@ -29,5 +30,6 @@
sources = dart_mojo_sdk_sources
deps = [
"../interfaces/application",
+ "../interfaces/bindings",
]
}
diff --git a/mojo/public/dart/bindings.dart b/mojo/public/dart/bindings.dart
index e68afec..3447874 100644
--- a/mojo/public/dart/bindings.dart
+++ b/mojo/public/dart/bindings.dart
@@ -9,7 +9,9 @@
import 'dart:typed_data';
import 'package:mojo/public/dart/core.dart' as core;
+import 'package:mojom/mojo/interface_control_messages.mojom.dart' as icm;
+part 'src/control_message.dart';
part 'src/codec.dart';
part 'src/message.dart';
part 'src/proxy.dart';
diff --git a/mojo/public/dart/src/codec.dart b/mojo/public/dart/src/codec.dart
index 89e32c2..07bad35 100644
--- a/mojo/public/dart/src/codec.dart
+++ b/mojo/public/dart/src/codec.dart
@@ -201,11 +201,10 @@
void encodeInterface(
core.MojoEventStreamListener interface, int offset, bool nullable) {
- // Set the version field to 0 for now.
- encodeUint32(0, offset + kSerializedHandleSize);
-
if (interface == null) {
encodeInvalideHandle(offset, nullable);
+ // Set the version field to 0.
+ encodeUint32(0, offset + kSerializedHandleSize);
return;
}
if (interface is Stub) {
@@ -214,6 +213,8 @@
interface.bind(pipe.endpoints[0]);
interface.listen();
encodeMessagePipeHandle(pipe.endpoints[1], offset, nullable);
+ // Set the version to the version in the stub.
+ encodeUint32(interface.version, offset + kSerializedHandleSize);
} else if (interface is Proxy) {
assert(interface.isBound);
if (!interface.isOpen) {
@@ -222,6 +223,8 @@
interface.listen();
}
encodeMessagePipeHandle(interface.endpoint, offset, nullable);
+ // Set the version to the current version of the proxy.
+ encodeUint32(interface.version, offset + kSerializedHandleSize);
} else {
throw new MojoCodecError(
'Trying to encode an unknown MojoEventStreamListener');
@@ -594,9 +597,14 @@
ProxyBase decodeServiceInterface(
int offset, bool nullable, Function clientFactory) {
- // Ignore the version field for now.
var endpoint = decodeMessagePipeHandle(offset, nullable);
- return endpoint.handle.isValid ? clientFactory(endpoint) : null;
+ var version = decodeUint32(offset + kSerializedHandleSize);
+ if (!endpoint.handle.isValid) {
+ return null;
+ }
+ ProxyBase client = clientFactory(endpoint);
+ client.impl._version = version;
+ return client;
}
Stub decodeInterfaceRequest(
diff --git a/mojo/public/dart/src/control_message.dart b/mojo/public/dart/src/control_message.dart
new file mode 100644
index 0000000..de71a98
--- /dev/null
+++ b/mojo/public/dart/src/control_message.dart
@@ -0,0 +1,61 @@
+// Copyright 2015 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.
+
+part of bindings;
+
+// Handles InterfaceControlMessages for a stub.
+class ControlMessageHandler {
+ static bool isControlMessage(ServiceMessage message) =>
+ _isRun(message) || _isRunOrClose(message);
+
+ static bool _isRun(ServiceMessage message) =>
+ (message.header.type == icm.kRunMessageId);
+
+ static bool _isRunOrClose(ServiceMessage message) =>
+ (message.header.type == icm.kRunOrClosePipeMessageId);
+
+ static Future<Message> handleMessage(Stub stub,
+ int interface_version,
+ ServiceMessage message) {
+ assert(isControlMessage(message));
+ if (_isRun(message)) {
+ return _handleRun(stub, interface_version, message);
+ } else {
+ assert(_isRunOrClose(message));
+ return _handleRunOrClose(stub, interface_version, message);
+ }
+ }
+
+ static Future<Message> _handleRun(Stub stub,
+ int interface_version,
+ ServiceMessage message) {
+ // Construct RunMessage response.
+ var response = new icm.RunResponseMessageParams();
+ response.reserved0 = 16;
+ response.reserved1 = 0;
+ response.queryVersionResult = new icm.QueryVersionResult();
+ response.queryVersionResult.version = interface_version;
+ // Return response.
+ return new Future.value(
+ stub.buildResponseWithId(response,
+ icm.kRunMessageId,
+ message.header.requestId,
+ MessageHeader.kMessageIsResponse));
+ }
+
+ static Future<Message> _handleRunOrClose(Stub stub,
+ int interface_version,
+ ServiceMessage message) {
+ // Deserialize message.
+ var params = icm.RunOrClosePipeMessageParams.deserialize(message.payload);
+ // Grab required version.
+ var requiredVersion = params.requireVersion.version;
+ if (interface_version >= requiredVersion) {
+ // Stub meets the requirements.
+ return;
+ }
+ // Stub does not implement required version. Close the pipe immediately.
+ stub.close(immediate: true);
+ }
+}
diff --git a/mojo/public/dart/src/proxy.dart b/mojo/public/dart/src/proxy.dart
index 863e734..528e13a 100644
--- a/mojo/public/dart/src/proxy.dart
+++ b/mojo/public/dart/src/proxy.dart
@@ -13,6 +13,10 @@
abstract class Proxy extends core.MojoEventStreamListener {
Map<int, Completer> _completerMap;
int _nextId = 0;
+ int _version = 0;
+ /// Version of this interface that the remote side supports. Updated when a
+ /// call to [queryVersion] or [requireVersion] is made.
+ int get version => _version;
Proxy.fromEndpoint(core.MojoMessagePipeEndpoint endpoint)
: _completerMap = {},
@@ -39,6 +43,10 @@
result = endpoint.read(bytes, result.bytesRead, handles);
assert(result.status.isOk || result.status.isResourceExhausted);
var message = new ServiceMessage.fromMessage(new Message(bytes, handles));
+ if (ControlMessageHandler.isControlMessage(message)) {
+ _handleControlMessageResponse(message);
+ return;
+ }
handleResponse(message);
}
@@ -96,6 +104,63 @@
var superString = super.toString();
return "Proxy(${superString})";
}
+
+ /// Queries the max version that the remote side supports.
+ /// Updates [version].
+ Future<int> queryVersion() async {
+ var params = new icm.RunMessageParams();
+ params.reserved0 = 16;
+ params.reserved1 = 0;
+ params.queryVersion = new icm.QueryVersion();
+ var response = await
+ sendMessageWithRequestId(params,
+ icm.kRunMessageId,
+ -1,
+ MessageHeader.kMessageExpectsResponse);
+ _version = response.queryVersionResult.version;
+ return _version;
+ }
+
+ /// If the remote side doesn't support the [requiredVersion], it will close
+ /// its end of the message pipe asynchronously. This does nothing if it's
+ /// already known that the remote side supports [requiredVersion].
+ /// Updates [version].
+ void requireVersion(int requiredVersion) {
+ if (requiredVersion <= _version) {
+ // Already supported.
+ return;
+ }
+
+ // If the remote end doesn't close the pipe, we know that it supports
+ // required version.
+ _version = requiredVersion;
+
+ var params = new icm.RunOrClosePipeMessageParams();
+ params.reserved0 = 16;
+ params.reserved1 = 0;
+ params.requireVersion = new icm.RequireVersion();
+ params.requireVersion.version = requiredVersion;
+ // TODO(johnmccutchan): We've set _version above but if this sendMessage
+ // throws an exception we may not have sent the RunOrClose message. Should
+ // we reset _version in that case?
+ sendMessage(params, icm.kRunOrClosePipeMessageId);
+ }
+
+ _handleControlMessageResponse(ServiceMessage message) {
+ // We only expect to see Run messages.
+ assert(message.header.type == icm.kRunMessageId);
+ var response = icm.RunResponseMessageParams.deserialize(message.payload);
+ if (!message.header.hasRequestId) {
+ throw 'Expected a message with a valid request Id.';
+ }
+ Completer c = completerMap[message.header.requestId];
+ if (c == null) {
+ throw 'Message had unknown request Id: ${message.header.requestId}';
+ }
+ completerMap.remove(message.header.requestId);
+ assert(!c.isCompleted);
+ c.complete(response);
+ }
}
// Generated Proxy classes implement this interface.
diff --git a/mojo/public/dart/src/stub.dart b/mojo/public/dart/src/stub.dart
index 87298bf..babba68 100644
--- a/mojo/public/dart/src/stub.dart
+++ b/mojo/public/dart/src/stub.dart
@@ -125,4 +125,6 @@
var superString = super.toString();
return "Stub(${superString})";
}
+
+ int get version;
}
diff --git a/mojo/public/interfaces/bindings/tests/BUILD.gn b/mojo/public/interfaces/bindings/tests/BUILD.gn
index 5cdbda1..6604860 100644
--- a/mojo/public/interfaces/bindings/tests/BUILD.gn
+++ b/mojo/public/interfaces/bindings/tests/BUILD.gn
@@ -24,14 +24,16 @@
}
mojom("versioning_test_service_interfaces") {
- testonly = true
+ # FIXME: Dart packaged applications cannot depend on testonly mojoms.
+ # testonly = true
sources = [
"versioning_test_service.mojom",
]
}
mojom("versioning_test_client_interfaces") {
- testonly = true
+ # FIXME: Dart packaged applications cannot depend on testonly mojoms.
+ # testonly = true
sources = [
"versioning_test_client.mojom",
]
diff --git a/mojo/public/interfaces/bindings/tests/test_unions.mojom b/mojo/public/interfaces/bindings/tests/test_unions.mojom
index 4e7a540..28d7aa4 100644
--- a/mojo/public/interfaces/bindings/tests/test_unions.mojom
+++ b/mojo/public/interfaces/bindings/tests/test_unions.mojom
@@ -40,6 +40,7 @@
handle<data_pipe_consumer> f_data_pipe_consumer;
handle<data_pipe_producer> f_data_pipe_producer;
handle<shared_buffer> f_shared_buffer;
+ SmallCache f_small_cache;
};
struct DummyStruct {
@@ -61,3 +62,12 @@
struct SmallObjStruct {
ObjectUnion obj_union;
};
+
+interface SmallCache {
+ SetIntValue(int64 int_value);
+ GetIntValue() => (int64 int_value);
+};
+
+interface UnionInterface {
+ Echo(PodUnion in) => (PodUnion out);
+};
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/union_definition.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/union_definition.tmpl
index 487d2a5..087499a 100644
--- a/mojo/public/tools/bindings/generators/cpp_templates/union_definition.tmpl
+++ b/mojo/public/tools/bindings/generators/cpp_templates/union_definition.tmpl
@@ -62,8 +62,12 @@
case {{enum_name}}::{{field.name|upper}}: {
{%- if field.kind|is_object_kind %}
mojo::internal::Encode(&data.f_{{field.name}}, handles);
-{%- elif field.kind|is_any_handle_kind or field.kind|is_interface_kind %}
+{%- elif field.kind|is_any_handle_kind %}
mojo::internal::EncodeHandle(&data.f_{{field.name}}, handles);
+{%- elif field.kind|is_interface_kind %}
+ mojo::internal::EncodeHandle(
+ reinterpret_cast<mojo::internal::Interface_Data*>(
+ &data.f_{{field.name}}), handles);
{%- endif %}
return;
}
@@ -78,8 +82,12 @@
case {{enum_name}}::{{field.name|upper}}: {
{%- if field.kind|is_object_kind %}
mojo::internal::Decode(&data.f_{{field.name}}, handles);
-{%- elif field.kind|is_any_handle_kind or field.kind|is_interface_kind %}
+{%- elif field.kind|is_any_handle_kind %}
mojo::internal::DecodeHandle(&data.f_{{field.name}}, handles);
+{%- elif field.kind|is_interface_kind %}
+ mojo::internal::DecodeHandle(
+ reinterpret_cast<mojo::internal::Interface_Data*>(
+ &data.f_{{field.name}}), handles);
{%- endif %}
return;
}
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/union_serialization_definition.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/union_serialization_definition.tmpl
index c9cc307..f07c487 100644
--- a/mojo/public/tools/bindings/generators/cpp_templates/union_serialization_definition.tmpl
+++ b/mojo/public/tools/bindings/generators/cpp_templates/union_serialization_definition.tmpl
@@ -70,6 +70,12 @@
{% elif field.kind|is_any_handle_kind %}
result->data.f_{{field.name}} =
input_acc.data()->{{field.name}}->release().value();
+{% elif field.kind|is_interface_kind %}
+ mojo::internal::Interface_Data* {{field.name}} =
+ reinterpret_cast<mojo::internal::Interface_Data*>(
+ &result->data.f_{{field.name}});
+ mojo::internal::InterfacePointerToData(
+ input_acc.data()->{{field.name}}->Pass(), {{field.name}});
{% else %}
result->data.f_{{field.name}} = input_acc.data()->{{field.name}};
{%- endif %}
@@ -100,6 +106,14 @@
{{field.kind|cpp_wrapper_type}}* {{field.name}} =
reinterpret_cast<{{field.kind|cpp_wrapper_type}}*>(&input->data.f_{{field.name}});
result->set_{{field.name}}({{field.name}}->Pass());
+{% elif field.kind|is_interface_kind %}
+ {{field.kind|cpp_wrapper_type}} {{field.name}}_out;
+ mojo::internal::Interface_Data* {{field.name}}_in =
+ reinterpret_cast<mojo::internal::Interface_Data*>(
+ &input->data.f_{{field.name}});
+ mojo::internal::InterfaceDataToPointer(
+ {{field.name}}_in, &{{field.name}}_out);
+ result->set_{{field.name}}({{field.name}}_out.Pass());
{% elif field.kind|is_enum_kind %}
result->set_{{field.name}}(static_cast<{{field.kind|cpp_wrapper_type}}>(input->data.f_{{field.name}}));
{% else %}
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/wrapper_union_class_declaration.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/wrapper_union_class_declaration.tmpl
index 2f931bf..5e43c43 100644
--- a/mojo/public/tools/bindings/generators/cpp_templates/wrapper_union_class_declaration.tmpl
+++ b/mojo/public/tools/bindings/generators/cpp_templates/wrapper_union_class_declaration.tmpl
@@ -39,7 +39,8 @@
Union_() {}
~Union_() {}
{% for field in union.fields %}
-{% if field.kind|is_object_kind or field.kind|is_any_handle_kind -%}
+{% if field.kind|is_object_kind or field.kind|is_any_handle_kind
+ or field.kind|is_interface_kind -%}
{{field.kind|cpp_wrapper_type}}* {{field.name}};
{% else -%}
{{field.kind|cpp_wrapper_type}} {{field.name}};
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/wrapper_union_class_definition.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/wrapper_union_class_definition.tmpl
index cb4e785..0f19a15 100644
--- a/mojo/public/tools/bindings/generators/cpp_templates/wrapper_union_class_definition.tmpl
+++ b/mojo/public/tools/bindings/generators/cpp_templates/wrapper_union_class_definition.tmpl
@@ -43,7 +43,8 @@
switch (tag_) {
{% for field in union.fields %}
case Tag::{{field.name|upper}}:
-{% if field.kind|is_object_kind or field.kind|is_any_handle_kind %}
+{% if field.kind|is_object_kind or field.kind|is_any_handle_kind
+ or field.kind|is_interface_kind %}
return mojo::internal::ValueTraits<{{field.kind|cpp_wrapper_type}}>::Equals(*(data_.{{field.name}}), *(other.data_.{{field.name}}));
{%- else %}
return mojo::internal::ValueTraits<{{field.kind|cpp_wrapper_type}}>::Equals(data_.{{field.name}}, other.data_.{{field.name}});
@@ -61,7 +62,8 @@
{{field.kind|cpp_union_getter_return_type}} {{union.name}}::get_{{field.name}}() const {
MOJO_DCHECK(tag_ == Tag::{{field.name|upper}});
-{% if field.kind|is_object_kind or field.kind|is_any_handle_kind %}
+{% if field.kind|is_object_kind or field.kind|is_any_handle_kind
+ or field.kind|is_interface_kind %}
return *(data_.{{field.name}});
{%- else %}
return data_.{{field.name}};
@@ -72,7 +74,7 @@
SwitchActive(Tag::{{field.name|upper}});
{% if field.kind|is_string_kind %}
*(data_.{{field.name}}) = {{field.name}};
-{% elif field.kind|is_object_kind %}
+{% elif field.kind|is_object_kind or field.kind|is_interface_kind %}
*(data_.{{field.name}}) = {{field.name}}.Pass();
{% elif field.kind|is_any_handle_kind %}
data_.{{field.name}}->reset({{field.name}}.release());
@@ -95,7 +97,8 @@
switch (new_active) {
{% for field in union.fields %}
case Tag::{{field.name|upper}}:
-{% if field.kind|is_object_kind or field.kind|is_any_handle_kind %}
+{% if field.kind|is_object_kind or field.kind|is_any_handle_kind
+ or field.kind|is_interface_kind %}
data_.{{field.name}} = new {{field.kind|cpp_wrapper_type}}();
{%- endif %}
break;
@@ -109,7 +112,8 @@
switch (tag_) {
{% for field in union.fields %}
case Tag::{{field.name|upper}}:
-{% if field.kind|is_object_kind or field.kind|is_any_handle_kind %}
+{% if field.kind|is_object_kind or field.kind|is_any_handle_kind
+ or field.kind|is_interface_kind %}
delete data_.{{field.name}};
{%- endif %}
break;
diff --git a/mojo/public/tools/bindings/generators/dart_templates/interface_definition.tmpl b/mojo/public/tools/bindings/generators/dart_templates/interface_definition.tmpl
index 0fbcb49..6e3cae4 100644
--- a/mojo/public/tools/bindings/generators/dart_templates/interface_definition.tmpl
+++ b/mojo/public/tools/bindings/generators/dart_templates/interface_definition.tmpl
@@ -160,6 +160,14 @@
Future close({bool immediate: false}) => impl.close(immediate: immediate);
+ int get version => impl.version;
+
+ Future<int> queryVersion() => impl.queryVersion();
+
+ void requireVersion(int requiredVersion) {
+ impl.requireVersion(requiredVersion);
+ }
+
String toString() {
return "{{interface|name}}Proxy($impl)";
}
@@ -202,6 +210,11 @@
{%- endfor %}
Future<bindings.Message> handleMessage(bindings.ServiceMessage message) {
+ if (bindings.ControlMessageHandler.isControlMessage(message)) {
+ return bindings.ControlMessageHandler.handleMessage(this,
+ {{interface.version}},
+ message);
+ }
assert(_impl != null);
switch (message.header.type) {
{%- for method in interface.methods %}
@@ -241,7 +254,7 @@
}
{{interface|name}} get impl => _impl;
- set impl({{interface|name}} d) {
+ set impl({{interface|name}} d) {
assert(_impl == null);
_impl = d;
}
@@ -250,4 +263,6 @@
var superString = super.toString();
return "{{interface|name}}Stub($superString)";
}
+
+ int get version => {{interface.version}};
}
diff --git a/mojo/public/tools/bindings/generators/dart_templates/struct_definition.tmpl b/mojo/public/tools/bindings/generators/dart_templates/struct_definition.tmpl
index 9f62100..a883e0a 100644
--- a/mojo/public/tools/bindings/generators/dart_templates/struct_definition.tmpl
+++ b/mojo/public/tools/bindings/generators/dart_templates/struct_definition.tmpl
@@ -117,9 +117,12 @@
// Scan in reverse order to optimize for more recent versions.
for (int i = kVersions.length - 1; i >= 0; --i) {
if (mainDataHeader.version >= kVersions[i].version) {
- if (mainDataHeader.size != kVersions[i].size)
- throw new bindings.MojoCodecError(
- 'Header doesn\'t correspond to any known version.');
+ if (mainDataHeader.size == kVersions[i].size) {
+ // Found a match.
+ break;
+ }
+ throw new bindings.MojoCodecError(
+ 'Header size doesn\'t correspond to known version size.');
}
}
} else if (mainDataHeader.size < kVersions.last.size) {
diff --git a/mojo/public/tools/bindings/generators/mojom_cpp_generator.py b/mojo/public/tools/bindings/generators/mojom_cpp_generator.py
index 70dc94b..f321c44 100644
--- a/mojo/public/tools/bindings/generators/mojom_cpp_generator.py
+++ b/mojo/public/tools/bindings/generators/mojom_cpp_generator.py
@@ -230,6 +230,8 @@
def GetCppUnionFieldType(kind):
if mojom.IsAnyHandleKind(kind):
return "MojoHandle"
+ if mojom.IsInterfaceKind(kind):
+ return "uint64_t"
if mojom.IsEnumKind(kind):
return "int32_t"
if mojom.IsUnionKind(kind):
@@ -240,7 +242,7 @@
def GetUnionGetterReturnType(kind):
if (mojom.IsStructKind(kind) or mojom.IsUnionKind(kind) or
mojom.IsArrayKind(kind) or mojom.IsMapKind(kind) or
- mojom.IsAnyHandleKind(kind)):
+ mojom.IsAnyHandleKind(kind) or mojom.IsInterfaceKind(kind)):
return "%s&" % GetCppWrapperType(kind)
return GetCppResultWrapperType(kind)
diff --git a/mojo/public/tools/download_network_service.py b/mojo/public/tools/download_network_service.py
index 43b1172..05c28ba 100755
--- a/mojo/public/tools/download_network_service.py
+++ b/mojo/public/tools/download_network_service.py
@@ -15,10 +15,6 @@
sys.path.insert(0, os.path.join(_CURRENT_PATH, "pylib"))
import gs
-if not sys.platform.startswith("linux"):
- print "Not supported for your platform"
- sys.exit(0)
-
script_dir = os.path.dirname(os.path.realpath(__file__))
diff --git a/mojo/services/url_response_disk_cache/public/interfaces/BUILD.gn b/mojo/services/url_response_disk_cache/public/interfaces/BUILD.gn
new file mode 100644
index 0000000..aac931e
--- /dev/null
+++ b/mojo/services/url_response_disk_cache/public/interfaces/BUILD.gn
@@ -0,0 +1,24 @@
+# Copyright 2015 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.
+
+import("//build/module_args/mojo.gni")
+import("$mojo_sdk_root/mojo/public/tools/bindings/mojom.gni")
+
+mojom("interfaces") {
+ sources = [
+ "url_response_disk_cache.mojom",
+ ]
+
+ import_dirs = [ get_path_info("../../../", "abspath") ]
+
+ if (defined(mojo_network_service_root)) {
+ import_dirs += [ mojo_network_service_root ]
+ } else {
+ mojo_network_service_root = "../../.."
+ }
+
+ deps = [
+ "$mojo_network_service_root/network/public/interfaces",
+ ]
+}
diff --git a/mojo/services/url_response_disk_cache/public/interfaces/url_response_disk_cache.mojom b/mojo/services/url_response_disk_cache/public/interfaces/url_response_disk_cache.mojom
new file mode 100644
index 0000000..b808b6e
--- /dev/null
+++ b/mojo/services/url_response_disk_cache/public/interfaces/url_response_disk_cache.mojom
@@ -0,0 +1,35 @@
+// Copyright 2015 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.
+
+module mojo;
+
+import "mojo/services/network/public/interfaces/url_loader.mojom";
+
+// This service allows client to efficiently cache and retrieve url response
+// content on disk. In particular, it allows the shell and content handlers to
+// efficiently cache and retrieve mojo applications.
+// TODO(qsr): At the moment, the url response disk cache only handles ETag. If
+// either the cached version or the given response do not contain
+// ETags, the entry will be invalidated. It should be extended to
+// handle all the other http cache mechanisms for better performance
+// with http servers that do not support ETags.
+interface URLResponseDiskCache {
+
+ // Given a URLResponse, returns a pair of paths. |file_path| is a file
+ // containing the body of the response. |cache_dir_path| is a directory that
+ // the applicaton can use to store content. This service guarantee that
+ // |cache_dir_path| will be emptied when |file_path| content changes. For
+ // example, a content handler that is backed by a VM that compiles files
+ // could have the VM use this directory to cache the compiled files.
+ GetFile(mojo.URLResponse response) =>
+ (array<uint8>? file_path, array<uint8>? cache_dir_path);
+
+ // Given a URLResponse that is expected to have a zipped body, returns a
+ // pair of paths. |extracted_dir_path| is a directory containing the unzipped
+ // body of the response. |cache_dir_path| is a directory that the applicaton
+ // can use to store content. This service guarantee that |cache_dir_path|
+ // will be emptied when |extracted_dir_path| content changes.
+ GetExtractedContent(mojo.URLResponse response) =>
+ (array<uint8>? extracted_dir_path, array<uint8>? cache_dir_path);
+};
diff --git a/mojo/tools/data/apptests b/mojo/tools/data/apptests
index e5616bf..148bf79 100644
--- a/mojo/tools/data/apptests
+++ b/mojo/tools/data/apptests
@@ -121,4 +121,7 @@
# http server.
"test": "mojo:shell_apptests",
},
+ {
+ "test": "mojo:url_response_disk_cache_apptests",
+ },
]
diff --git a/services/BUILD.gn b/services/BUILD.gn
index 84876b8..ef45031 100644
--- a/services/BUILD.gn
+++ b/services/BUILD.gn
@@ -13,10 +13,10 @@
"//services/gles2:lib",
"//services/http_server",
"//services/icu_data",
- "//services/js",
"//services/kiosk_wm",
"//services/native_viewport",
"//services/reaper",
+ "//services/url_response_disk_cache",
"//services/surfaces",
"//services/test_service:bindings",
"//services/tracing",
@@ -36,4 +36,10 @@
if (is_linux) {
deps += [ "//services/python" ]
}
+
+ # TODO(jamesr): We only support building V8 snapshot data on a linux host since it
+ # needs a 32 bit toolchain and we don't have one configured for mac hosts.
+ if (host_os == "linux") {
+ deps += [ "//services/js" ]
+ }
}
diff --git a/services/dart/dart_apptests/BUILD.gn b/services/dart/dart_apptests/BUILD.gn
index 6b44108..8d2723b 100644
--- a/services/dart/dart_apptests/BUILD.gn
+++ b/services/dart/dart_apptests/BUILD.gn
@@ -11,9 +11,12 @@
"io_internet_address_apptests.dart",
"main.dart",
"pingpong_apptests.dart",
+ "versioning_apptests.dart",
]
deps = [
"//mojo/dart/apptest",
+ "//mojo/public/interfaces/bindings/tests:versioning_test_client_interfaces",
+ "//mojo/public/interfaces/bindings/tests:versioning_test_service_interfaces",
"//services/dart/test:echo_service_interface",
"//services/dart/test:pingpong_service_interface",
]
diff --git a/services/dart/dart_apptests/main.dart b/services/dart/dart_apptests/main.dart
index 3c165af..7f5dfe9 100644
--- a/services/dart/dart_apptests/main.dart
+++ b/services/dart/dart_apptests/main.dart
@@ -9,10 +9,12 @@
import 'pingpong_apptests.dart' as pingpong;
import 'io_http_apptests.dart' as io_http;
import 'io_internet_address_apptests.dart' as io_internet_address;
+import 'versioning_apptests.dart' as versioning;
main(List args) {
runAppTests(args[0], [echo.echoApptests,
io_internet_address.tests,
io_http.tests,
- pingpong.pingpongApptests]);
+ pingpong.pingpongApptests,
+ versioning.tests]);
}
diff --git a/services/dart/dart_apptests/versioning_apptests.dart b/services/dart/dart_apptests/versioning_apptests.dart
new file mode 100644
index 0000000..4365cb3
--- /dev/null
+++ b/services/dart/dart_apptests/versioning_apptests.dart
@@ -0,0 +1,139 @@
+// Copyright 2015 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.
+
+library versioning_apptests;
+
+import 'dart:async';
+
+import 'package:apptest/apptest.dart';
+import 'package:mojo/public/dart/application.dart';
+import 'package:mojo/public/dart/bindings.dart';
+import 'package:mojo/public/dart/core.dart';
+import 'package:mojom/mojo/test/versioning/versioning_test_client.mojom.dart';
+
+tests(Application application, String url) {
+ group('Versioning Apptests', () {
+ test('Struct', () async {
+ // The service side uses a newer version of Employee definition that
+ // includes the 'birthday' field.
+
+ // Connect to human resource database.
+ var databaseProxy = new HumanResourceDatabaseProxy.unbound();
+ application.connectToService("mojo:versioning_test_service",
+ databaseProxy);
+
+ // Query database and get a response back (even though the client does not
+ // know about the birthday field).
+ bool retrieveFingerPrint = true;
+ var response =
+ await databaseProxy.ptr.queryEmployee(1, retrieveFingerPrint);
+ expect(response.employee.employeeId, equals(1));
+ expect(response.employee.name, equals("Homer Simpson"));
+ expect(response.employee.department, equals(Department_DEV));
+ expect(response.fingerPrint, isNotNull);
+
+ // Pass an Employee struct to the service side that lacks the 'birthday'
+ // field.
+ var newEmployee = new Employee();
+ newEmployee.employeeId = 2;
+ newEmployee.name = "Marge Simpson";
+ newEmployee.department = Department_SALES;
+ response = await databaseProxy.ptr.addEmployee(newEmployee);
+ expect(response.success, isTrue);
+
+ // Query for employee #2.
+ retrieveFingerPrint = false;
+ response = await databaseProxy.ptr.queryEmployee(2, retrieveFingerPrint);
+ expect(response.employee.employeeId, equals(2));
+ expect(response.employee.name, equals("Marge Simpson"));
+ expect(response.employee.department, equals(Department_SALES));
+ expect(response.fingerPrint, isNull);
+
+ // Disconnect from database.
+ databaseProxy.close();
+ });
+
+ test('QueryVersion', () async {
+ // Connect to human resource database.
+ var databaseProxy = new HumanResourceDatabaseProxy.unbound();
+ application.connectToService("mojo:versioning_test_service",
+ databaseProxy);
+ // Query the version.
+ var version = await databaseProxy.queryVersion();
+ // Expect it to be 1.
+ expect(version, equals(1));
+ // Disconnect from database.
+ databaseProxy.close();
+ });
+
+ test('RequireVersion', () async {
+ // Connect to human resource database.
+ var databaseProxy = new HumanResourceDatabaseProxy.unbound();
+ application.connectToService("mojo:versioning_test_service",
+ databaseProxy);
+
+ // Require version 1.
+ databaseProxy.requireVersion(1);
+ expect(databaseProxy.version, equals(1));
+
+ // Query for employee #3.
+ var retrieveFingerPrint = false;
+ var response =
+ await databaseProxy.ptr.queryEmployee(3, retrieveFingerPrint);
+
+ // Got some kind of response.
+ expect(response, isNotNull);
+
+ // Require version 3 (which cannot be satisfied).
+ databaseProxy.requireVersion(3);
+ expect(databaseProxy.version, equals(3));
+
+ // Query for employee #1, observe that an exception was thrown.
+ bool exceptionCaught = false;
+ try {
+ response =
+ await databaseProxy.ptr.queryEmployee(1, retrieveFingerPrint);
+ fail('Exception should be thrown.');
+ } catch(e) {
+ exceptionCaught = true;
+ }
+ expect(exceptionCaught, isTrue);
+
+ // No need to disconnect from database because we were disconnected by
+ // the requireVersion control message.
+ });
+
+ test('CallNonexistentMethod', () async {
+ // Connect to human resource database.
+ var databaseProxy = new HumanResourceDatabaseProxy.unbound();
+ application.connectToService("mojo:versioning_test_service",
+ databaseProxy);
+ const fingerPrintLength = 128;
+ var fingerPrint = new List(fingerPrintLength);
+ for (var i = 0; i < fingerPrintLength; i++) {
+ fingerPrint[i] = i + 13;
+ }
+ // Although the client side doesn't know whether the service side supports
+ // version 1, calling a version 1 method succeeds as long as the service
+ // side supports version 1.
+ var response = await databaseProxy.ptr.attachFingerPrint(1, fingerPrint);
+ expect(response.success, isTrue);
+
+ // Calling a version 2 method (which the service side doesn't support)
+ // closes the pipe.
+ bool exceptionCaught = false;
+ try {
+ response =
+ await await databaseProxy.ptr.listEmployeeIds();
+ fail('Exception should be thrown.');
+ } catch(e) {
+ exceptionCaught = true;
+ }
+ expect(exceptionCaught, isTrue);
+
+ // No need to disconnect from database because we were disconnected by
+ // the call to a version 2 method.
+ });
+ });
+}
diff --git a/services/tracing/tracing.mojom b/services/tracing/tracing.mojom
index 7c53b43..fd95c07 100644
--- a/services/tracing/tracing.mojom
+++ b/services/tracing/tracing.mojom
@@ -10,6 +10,8 @@
// TraceDataCollector interface up until Stop() is called.
interface TraceController {
+ // Categories can either be the empty string to mean the default set of
+ // categories or a comma-delimited list of categories to trace.
StartTracing(string categories, TraceDataCollector collector);
StopTracing();
};
diff --git a/services/url_response_disk_cache/BUILD.gn b/services/url_response_disk_cache/BUILD.gn
new file mode 100644
index 0000000..92a4a32
--- /dev/null
+++ b/services/url_response_disk_cache/BUILD.gn
@@ -0,0 +1,90 @@
+# Copyright 2015 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.
+
+import("//build/module_args/mojo.gni")
+import("//mojo/public/mojo_application.gni")
+import("//mojo/public/tools/bindings/mojom.gni")
+import("//mojo/tools/embed/rules.gni")
+
+source_set("url_response_disk_cache") {
+ sources = [
+ "url_response_disk_cache_app.cc",
+ "url_response_disk_cache_app.h",
+ "url_response_disk_cache_impl.cc",
+ "url_response_disk_cache_impl.h",
+ ]
+
+ deps = [
+ ":bindings",
+ "//base",
+ "//mojo/application",
+ "//mojo/common",
+ "//mojo/environment:chromium",
+ "//mojo/public/cpp/application",
+ "//mojo/public/cpp/system",
+ "//mojo/services/url_response_disk_cache/public/interfaces",
+ "//third_party/zlib:zip",
+ "//url:url",
+ ]
+}
+
+mojom("bindings") {
+ sources = [
+ "url_response_disk_cache_entry.mojom",
+ ]
+}
+
+mojo_native_application("tests") {
+ output_name = "url_response_disk_cache_apptests"
+
+ testonly = true
+
+ sources = [
+ "url_response_disk_cache_apptest.cc",
+ ]
+
+ deps = [
+ ":embed_test_data",
+ "//base",
+ "//mojo/application",
+ "//mojo/application:test_support",
+ "//mojo/common",
+ "//mojo/public/cpp/bindings",
+ "//mojo/public/cpp/system:system",
+ "//mojo/services/network/public/interfaces:interfaces",
+ "//mojo/services/url_response_disk_cache/public/interfaces",
+ ]
+
+ data_deps = [ ":url_response_disk_cache" ]
+}
+
+action("test_data") {
+ script = "//mojo/public/tools/gn/zip.py"
+ inputs = [
+ "test_data/file1",
+ "test_data/file2",
+ ]
+ output = "$target_out_dir/test_data.zip"
+ outputs = [
+ output,
+ ]
+ rebase_base_dir = rebase_path("test_data", root_build_dir)
+ rebase_inputs = rebase_path(inputs, root_build_dir)
+ rebase_output = rebase_path(output, root_build_dir)
+ args = [
+ "--base-dir=$rebase_base_dir",
+ "--inputs=$rebase_inputs",
+ "--output=$rebase_output",
+ ]
+}
+
+embed_file("embed_test_data") {
+ source = "$target_out_dir/test_data.zip"
+ namespace = "mojo"
+ variable = "kTestData"
+
+ deps = [
+ ":test_data",
+ ]
+}
diff --git a/services/url_response_disk_cache/test_data/file1 b/services/url_response_disk_cache/test_data/file1
new file mode 100644
index 0000000..ce01362
--- /dev/null
+++ b/services/url_response_disk_cache/test_data/file1
@@ -0,0 +1 @@
+hello
diff --git a/services/url_response_disk_cache/test_data/file2 b/services/url_response_disk_cache/test_data/file2
new file mode 100644
index 0000000..cc628cc
--- /dev/null
+++ b/services/url_response_disk_cache/test_data/file2
@@ -0,0 +1 @@
+world
diff --git a/services/url_response_disk_cache/url_response_disk_cache_app.cc b/services/url_response_disk_cache/url_response_disk_cache_app.cc
new file mode 100644
index 0000000..5219b2d
--- /dev/null
+++ b/services/url_response_disk_cache/url_response_disk_cache_app.cc
@@ -0,0 +1,42 @@
+// Copyright 2015 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 "services/url_response_disk_cache/url_response_disk_cache_app.h"
+
+#include "services/url_response_disk_cache/url_response_disk_cache_impl.h"
+
+namespace mojo {
+
+namespace {
+
+const size_t kMaxBlockingPoolThreads = 3;
+
+} // namespace
+
+URLResponseDiskCacheApp::URLResponseDiskCacheApp() {
+}
+
+URLResponseDiskCacheApp::~URLResponseDiskCacheApp() {
+ if (worker_pool_)
+ worker_pool_->Shutdown();
+}
+
+bool URLResponseDiskCacheApp::ConfigureIncomingConnection(
+ ApplicationConnection* connection) {
+ connection->AddService<URLResponseDiskCache>(this);
+ return true;
+}
+
+void URLResponseDiskCacheApp::Create(
+ ApplicationConnection* connection,
+ InterfaceRequest<URLResponseDiskCache> request) {
+ if (!worker_pool_) {
+ worker_pool_ = new base::SequencedWorkerPool(kMaxBlockingPoolThreads,
+ "URLResponseDiskCachePool");
+ }
+ new URLResponseDiskCacheImpl(
+ worker_pool_, connection->GetRemoteApplicationURL(), request.Pass());
+}
+
+} // namespace mojo
diff --git a/services/url_response_disk_cache/url_response_disk_cache_app.h b/services/url_response_disk_cache/url_response_disk_cache_app.h
new file mode 100644
index 0000000..8e21eda
--- /dev/null
+++ b/services/url_response_disk_cache/url_response_disk_cache_app.h
@@ -0,0 +1,38 @@
+// Copyright 2015 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.
+
+#ifndef SERVICES_URL_RESPONSE_DISK_CACHE_URL_RESPONSE_DISK_CACHE_APP_H_
+#define SERVICES_URL_RESPONSE_DISK_CACHE_URL_RESPONSE_DISK_CACHE_APP_H_
+
+#include "base/macros.h"
+#include "base/threading/sequenced_worker_pool.h"
+#include "mojo/public/cpp/application/application_connection.h"
+#include "mojo/public/cpp/application/application_delegate.h"
+#include "mojo/public/cpp/application/interface_factory.h"
+#include "mojo/services/url_response_disk_cache/public/interfaces/url_response_disk_cache.mojom.h"
+
+namespace mojo {
+
+class URLResponseDiskCacheApp : public ApplicationDelegate,
+ public InterfaceFactory<URLResponseDiskCache> {
+ public:
+ URLResponseDiskCacheApp();
+ ~URLResponseDiskCacheApp() override;
+
+ private:
+ // ApplicationDelegate
+ bool ConfigureIncomingConnection(ApplicationConnection* connection) override;
+
+ // InterfaceFactory<URLResponseDiskCache>
+ void Create(ApplicationConnection* connection,
+ InterfaceRequest<URLResponseDiskCache> request) override;
+
+ scoped_refptr<base::SequencedWorkerPool> worker_pool_;
+
+ DISALLOW_COPY_AND_ASSIGN(URLResponseDiskCacheApp);
+};
+
+} // namespace mojo
+
+#endif // SERVICES_URL_RESPONSE_DISK_CACHE_URL_RESPONSE_DISK_CACHE_APP_H_
diff --git a/services/url_response_disk_cache/url_response_disk_cache_apptest.cc b/services/url_response_disk_cache/url_response_disk_cache_apptest.cc
new file mode 100644
index 0000000..6b543de
--- /dev/null
+++ b/services/url_response_disk_cache/url_response_disk_cache_apptest.cc
@@ -0,0 +1,267 @@
+// Copyright 2015 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 "base/files/file_util.h"
+#include "base/rand_util.h"
+#include "base/run_loop.h"
+#include "base/strings/stringprintf.h"
+#include "mojo/public/cpp/application/application_impl.h"
+#include "mojo/public/cpp/application/application_test_base.h"
+#include "mojo/public/cpp/system/data_pipe.h"
+#include "mojo/services/network/public/interfaces/url_loader.mojom.h"
+#include "mojo/services/url_response_disk_cache/public/interfaces/url_response_disk_cache.mojom.h"
+#include "services/url_response_disk_cache/kTestData.h"
+
+namespace mojo {
+
+namespace {
+
+class URLResponseDiskCacheAppTest : public mojo::test::ApplicationTestBase {
+ public:
+ URLResponseDiskCacheAppTest() : ApplicationTestBase() {}
+ ~URLResponseDiskCacheAppTest() override {}
+
+ void SetUp() override {
+ mojo::test::ApplicationTestBase::SetUp();
+ application_impl()->ConnectToService("mojo:url_response_disk_cache",
+ &url_response_disk_cache_);
+ }
+
+ protected:
+ URLResponseDiskCachePtr url_response_disk_cache_;
+
+ DISALLOW_COPY_AND_ASSIGN(URLResponseDiskCacheAppTest);
+};
+
+base::FilePath toPath(Array<uint8_t> path) {
+ if (path.is_null()) {
+ return base::FilePath();
+ }
+ return base::FilePath(
+ std::string(reinterpret_cast<char*>(&path.front()), path.size()));
+}
+
+} // namespace
+
+TEST_F(URLResponseDiskCacheAppTest, GetFile) {
+ URLResponsePtr url_response = mojo::URLResponse::New();
+ url_response->url = "http://www.example.com/1";
+ url_response->headers = Array<String>(1);
+ url_response->headers[0] = base::StringPrintf("ETag: %f", base::RandDouble());
+ DataPipe pipe;
+ std::string content = base::RandBytesAsString(32);
+ uint32_t num_bytes = content.size();
+ ASSERT_EQ(MOJO_RESULT_OK,
+ WriteDataRaw(pipe.producer_handle.get(), content.c_str(),
+ &num_bytes, MOJO_WRITE_DATA_FLAG_ALL_OR_NONE));
+ ASSERT_EQ(content.size(), num_bytes);
+ pipe.producer_handle.reset();
+ url_response->body = pipe.consumer_handle.Pass();
+ base::FilePath file;
+ base::FilePath cache_dir;
+ url_response_disk_cache_->GetFile(
+ url_response.Pass(),
+ [&file, &cache_dir](Array<uint8_t> received_file_path,
+ Array<uint8_t> received_cache_dir_path) {
+ file = toPath(received_file_path.Pass());
+ cache_dir = toPath(received_cache_dir_path.Pass());
+ });
+ url_response_disk_cache_.WaitForIncomingMethodCall();
+ ASSERT_FALSE(file.empty());
+ std::string received_content;
+ ASSERT_TRUE(base::ReadFileToString(file, &received_content));
+ EXPECT_EQ(content, received_content);
+}
+
+TEST_F(URLResponseDiskCacheAppTest, GetExtractedContent) {
+ URLResponsePtr url_response = mojo::URLResponse::New();
+ url_response->url = "http://www.example.com/2";
+ url_response->headers = Array<String>(1);
+ url_response->headers[0] = base::StringPrintf("ETag: %f", base::RandDouble());
+ DataPipe pipe;
+ std::string content = base::RandBytesAsString(32);
+ uint32_t num_bytes = kTestData.size;
+ ASSERT_EQ(MOJO_RESULT_OK,
+ WriteDataRaw(pipe.producer_handle.get(), kTestData.data, &num_bytes,
+ MOJO_WRITE_DATA_FLAG_ALL_OR_NONE));
+ ASSERT_EQ(kTestData.size, num_bytes);
+ pipe.producer_handle.reset();
+ url_response->body = pipe.consumer_handle.Pass();
+ base::FilePath extracted_dir;
+ base::FilePath cache_dir;
+ url_response_disk_cache_->GetExtractedContent(
+ url_response.Pass(),
+ [&extracted_dir, &cache_dir](Array<uint8_t> received_extracted_dir,
+ Array<uint8_t> received_cache_dir_path) {
+ extracted_dir = toPath(received_extracted_dir.Pass());
+ cache_dir = toPath(received_cache_dir_path.Pass());
+ });
+ url_response_disk_cache_.WaitForIncomingMethodCall();
+ ASSERT_FALSE(extracted_dir.empty());
+ base::FilePath file1 = extracted_dir.Append("file1");
+ std::string file_content;
+ ASSERT_TRUE(base::ReadFileToString(file1, &file_content));
+ EXPECT_EQ("hello\n", file_content);
+ base::FilePath file2 = extracted_dir.Append("file2");
+ ASSERT_TRUE(base::ReadFileToString(file2, &file_content));
+ EXPECT_EQ("world\n", file_content);
+}
+
+TEST_F(URLResponseDiskCacheAppTest, CacheTest) {
+ URLResponsePtr url_response = mojo::URLResponse::New();
+ url_response->url = "http://www.example.com/3";
+ url_response->headers = Array<String>(1);
+ std::string etag = base::StringPrintf("ETag: %f", base::RandDouble());
+ url_response->headers[0] = etag;
+ DataPipe pipe1;
+ std::string content = base::RandBytesAsString(32);
+ uint32_t num_bytes = content.size();
+ ASSERT_EQ(MOJO_RESULT_OK,
+ WriteDataRaw(pipe1.producer_handle.get(), content.c_str(),
+ &num_bytes, MOJO_WRITE_DATA_FLAG_ALL_OR_NONE));
+ ASSERT_EQ(content.size(), num_bytes);
+ pipe1.producer_handle.reset();
+ url_response->body = pipe1.consumer_handle.Pass();
+ base::FilePath file;
+ base::FilePath cache_dir;
+ url_response_disk_cache_->GetFile(
+ url_response.Pass(),
+ [&file, &cache_dir](Array<uint8_t> received_file_path,
+ Array<uint8_t> received_cache_dir_path) {
+ file = toPath(received_file_path.Pass());
+ cache_dir = toPath(received_cache_dir_path.Pass());
+ });
+ url_response_disk_cache_.WaitForIncomingMethodCall();
+ ASSERT_FALSE(file.empty());
+ std::string received_content;
+ ASSERT_TRUE(base::ReadFileToString(file, &received_content));
+ EXPECT_EQ(content, received_content);
+ base::FilePath saved_file = cache_dir.Append("file");
+ EXPECT_FALSE(base::PathExists(saved_file));
+ std::string cached_content = base::RandBytesAsString(32);
+ ASSERT_TRUE(base::WriteFile(saved_file, cached_content.data(),
+ cached_content.size()));
+
+ // Request using a response for the same URL, with the same etag, but a
+ // different content. The cached value should be returned.
+ url_response = mojo::URLResponse::New();
+ url_response->url = "http://www.example.com/3";
+ url_response->headers = Array<String>(1);
+ url_response->headers[0] = etag;
+ DataPipe pipe2;
+ std::string new_content = base::RandBytesAsString(32);
+ num_bytes = new_content.size();
+ ASSERT_EQ(MOJO_RESULT_OK,
+ WriteDataRaw(pipe2.producer_handle.get(), new_content.c_str(),
+ &num_bytes, MOJO_WRITE_DATA_FLAG_ALL_OR_NONE));
+ ASSERT_EQ(new_content.size(), num_bytes);
+ pipe2.producer_handle.reset();
+ url_response->body = pipe2.consumer_handle.Pass();
+ url_response_disk_cache_->GetFile(
+ url_response.Pass(),
+ [&file, &cache_dir](Array<uint8_t> received_file_path,
+ Array<uint8_t> received_cache_dir_path) {
+ file = toPath(received_file_path.Pass());
+ cache_dir = toPath(received_cache_dir_path.Pass());
+ });
+ url_response_disk_cache_.WaitForIncomingMethodCall();
+ ASSERT_FALSE(file.empty());
+ ASSERT_TRUE(base::ReadFileToString(file, &received_content));
+ EXPECT_EQ(content, received_content);
+ saved_file = cache_dir.Append("file");
+ EXPECT_TRUE(base::PathExists(saved_file));
+ std::string received_cached_content;
+ ASSERT_TRUE(base::ReadFileToString(saved_file, &received_cached_content));
+ EXPECT_EQ(cached_content, received_cached_content);
+
+ // Request using a response for the same URL, with the a different etag. Check
+ // that the new content is returned, and the cached files is deleted.
+ url_response = mojo::URLResponse::New();
+ url_response->url = "http://www.example.com/3";
+ url_response->headers = Array<String>(1);
+ url_response->headers[0] = base::StringPrintf("ETag: %f", base::RandDouble());
+ DataPipe pipe3;
+ new_content = base::RandBytesAsString(32);
+ num_bytes = new_content.size();
+ ASSERT_EQ(MOJO_RESULT_OK,
+ WriteDataRaw(pipe3.producer_handle.get(), new_content.c_str(),
+ &num_bytes, MOJO_WRITE_DATA_FLAG_ALL_OR_NONE));
+ ASSERT_EQ(new_content.size(), num_bytes);
+ pipe3.producer_handle.reset();
+ url_response->body = pipe3.consumer_handle.Pass();
+ url_response_disk_cache_->GetFile(
+ url_response.Pass(),
+ [&file, &cache_dir](Array<uint8_t> received_file_path,
+ Array<uint8_t> received_cache_dir_path) {
+ file = toPath(received_file_path.Pass());
+ cache_dir = toPath(received_cache_dir_path.Pass());
+ });
+ url_response_disk_cache_.WaitForIncomingMethodCall();
+ ASSERT_FALSE(file.empty());
+ ASSERT_TRUE(base::ReadFileToString(file, &received_content));
+ EXPECT_EQ(new_content, received_content);
+ saved_file = cache_dir.Append("file");
+ EXPECT_FALSE(base::PathExists(saved_file));
+ ASSERT_TRUE(base::WriteFile(saved_file, cached_content.data(),
+ cached_content.size()));
+
+ // Request using a response without an etag header. Check that the new
+ // content is returned, and the cached files is deleted.
+ url_response = mojo::URLResponse::New();
+ url_response->url = "http://www.example.com/3";
+ DataPipe pipe4;
+ new_content = base::RandBytesAsString(32);
+ num_bytes = new_content.size();
+ ASSERT_EQ(MOJO_RESULT_OK,
+ WriteDataRaw(pipe4.producer_handle.get(), new_content.c_str(),
+ &num_bytes, MOJO_WRITE_DATA_FLAG_ALL_OR_NONE));
+ ASSERT_EQ(new_content.size(), num_bytes);
+ pipe4.producer_handle.reset();
+ url_response->body = pipe4.consumer_handle.Pass();
+ url_response_disk_cache_->GetFile(
+ url_response.Pass(),
+ [&file, &cache_dir](Array<uint8_t> received_file_path,
+ Array<uint8_t> received_cache_dir_path) {
+ file = toPath(received_file_path.Pass());
+ cache_dir = toPath(received_cache_dir_path.Pass());
+ });
+ url_response_disk_cache_.WaitForIncomingMethodCall();
+ ASSERT_FALSE(file.empty());
+ ASSERT_TRUE(base::ReadFileToString(file, &received_content));
+ EXPECT_EQ(new_content, received_content);
+ saved_file = cache_dir.Append("file");
+ EXPECT_FALSE(base::PathExists(saved_file));
+ ASSERT_TRUE(base::WriteFile(saved_file, cached_content.data(),
+ cached_content.size()));
+
+ // Request using a response with an etag header while the cache version
+ // doesn't have one. Check that the new content is returned, and the cached
+ // files is deleted.
+ url_response = mojo::URLResponse::New();
+ url_response->url = "http://www.example.com/3";
+ DataPipe pipe5;
+ new_content = base::RandBytesAsString(32);
+ num_bytes = new_content.size();
+ ASSERT_EQ(MOJO_RESULT_OK,
+ WriteDataRaw(pipe5.producer_handle.get(), new_content.c_str(),
+ &num_bytes, MOJO_WRITE_DATA_FLAG_ALL_OR_NONE));
+ ASSERT_EQ(new_content.size(), num_bytes);
+ pipe5.producer_handle.reset();
+ url_response->body = pipe5.consumer_handle.Pass();
+ url_response_disk_cache_->GetFile(
+ url_response.Pass(),
+ [&file, &cache_dir](Array<uint8_t> received_file_path,
+ Array<uint8_t> received_cache_dir_path) {
+ file = toPath(received_file_path.Pass());
+ cache_dir = toPath(received_cache_dir_path.Pass());
+ });
+ url_response_disk_cache_.WaitForIncomingMethodCall();
+ ASSERT_FALSE(file.empty());
+ ASSERT_TRUE(base::ReadFileToString(file, &received_content));
+ EXPECT_EQ(new_content, received_content);
+ saved_file = cache_dir.Append("file");
+ EXPECT_FALSE(base::PathExists(saved_file));
+}
+
+} // namespace mojo
diff --git a/services/url_response_disk_cache/url_response_disk_cache_entry.mojom b/services/url_response_disk_cache/url_response_disk_cache_entry.mojom
new file mode 100644
index 0000000..c68fa2d
--- /dev/null
+++ b/services/url_response_disk_cache/url_response_disk_cache_entry.mojom
@@ -0,0 +1,12 @@
+// Copyright 2015 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.
+
+module mojo;
+
+// One entry in the service cache.
+struct CacheEntry {
+ string url;
+ string content_path;
+ array<string>? headers;
+};
diff --git a/services/url_response_disk_cache/url_response_disk_cache_impl.cc b/services/url_response_disk_cache/url_response_disk_cache_impl.cc
new file mode 100644
index 0000000..9804c91
--- /dev/null
+++ b/services/url_response_disk_cache/url_response_disk_cache_impl.cc
@@ -0,0 +1,328 @@
+// Copyright 2015 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 "services/url_response_disk_cache/url_response_disk_cache_impl.h"
+
+#include "base/bind.h"
+#include "base/files/file_util.h"
+#include "base/files/important_file_writer.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "mojo/common/data_pipe_utils.h"
+#include "mojo/public/cpp/application/application_connection.h"
+#include "mojo/public/cpp/bindings/lib/fixed_buffer.h"
+#include "services/url_response_disk_cache/url_response_disk_cache_entry.mojom.h"
+#include "third_party/zlib/google/zip_reader.h"
+#include "url/gurl.h"
+
+namespace mojo {
+
+namespace {
+
+const char kEtagHeader[] = "etag";
+
+template <typename T>
+void Serialize(T input, std::string* output) {
+ typedef typename mojo::internal::WrapperTraits<T>::DataType DataType;
+ size_t size = GetSerializedSize_(input);
+ mojo::internal::FixedBuffer buf(size);
+ DataType data_type;
+ Serialize_(input.Pass(), &buf, &data_type);
+ std::vector<Handle> handles;
+ data_type->EncodePointersAndHandles(&handles);
+ void* serialized_data = buf.Leak();
+ *output = std::string(static_cast<char*>(serialized_data), size);
+ free(serialized_data);
+}
+
+template <typename T>
+void Deserialize(std::string input, T* output) {
+ typedef typename mojo::internal::WrapperTraits<T>::DataType DataType;
+ DataType data_type = reinterpret_cast<DataType>(&input[0]);
+ std::vector<Handle> handles;
+ data_type->DecodePointersAndHandles(&handles);
+ Deserialize_(data_type, output);
+}
+
+Array<uint8_t> PathToArray(const base::FilePath& path) {
+ if (path.empty())
+ return Array<uint8_t>();
+ const std::string& string = path.value();
+ Array<uint8_t> result(string.size());
+ memcpy(&result.front(), string.data(), string.size());
+ return result.Pass();
+}
+
+// Encode a string in ascii. This uses # as an escape character. It also escapes
+// ':' because it is an usual path separator.
+std::string EncodeString(const std::string& string) {
+ std::string result = "";
+ for (size_t i = 0; i < string.size(); ++i) {
+ unsigned char c = string[i];
+ if (c >= 32 && c < 128 && c != '#' && c != ':') {
+ result += c;
+ } else {
+ result += base::StringPrintf("#%02x", c);
+ }
+ }
+ return result;
+}
+
+// This service use a directory under HOME to store all of its data,
+base::FilePath GetBaseDirectory() {
+ return base::FilePath(getenv("HOME")).Append(".mojo_url_response_disk_cache");
+}
+
+// Returns the directory used store cached data for the given |url|, under
+// |base_directory|.
+base::FilePath GetDirName(base::FilePath base_directory,
+ const std::string& url) {
+ // TODO(qsr): If the speed of directory traversal is problematic, this might
+ // need to change to use less directories.
+ return base_directory.Append(EncodeString(url));
+}
+
+// Returns the directory that the consumer can use to cache its own data.
+base::FilePath GetConsumerCacheDirectory(const base::FilePath& main_cache) {
+ return main_cache.Append("consumer_cache");
+}
+
+// Returns the path of the sentinel used to keep track of a zipped response has
+// already been extracted.
+base::FilePath GetExtractedSentinel(const base::FilePath& main_cache) {
+ return main_cache.Append("extracted_sentinel");
+}
+
+// Runs the given callback. If |success| is false, call back with an error.
+// Otherwise, store |entry| in |entry_path|, then call back with the given
+// paths.
+void RunCallbackWithSuccess(
+ const URLResponseDiskCacheImpl::FilePathPairCallback& callback,
+ const base::FilePath& content_path,
+ const base::FilePath& cache_dir,
+ const base::FilePath& entry_path,
+ CacheEntryPtr entry,
+ bool success) {
+ if (!success) {
+ callback.Run(base::FilePath(), base::FilePath());
+ return;
+ }
+ std::string serialized_entry;
+ Serialize(entry.Pass(), &serialized_entry);
+ // We can ignore write error, as it will just force to clear the cache on the
+ // next request.
+ base::ImportantFileWriter::WriteFileAtomically(entry_path, serialized_entry);
+ callback.Run(content_path, cache_dir);
+}
+
+// Run the given mojo callback with the given paths.
+void RunMojoCallback(
+ const Callback<void(Array<uint8_t>, Array<uint8_t>)>& callback,
+ const base::FilePath& path1,
+ const base::FilePath& path2) {
+ callback.Run(PathToArray(path1), PathToArray(path2));
+}
+
+// Returns the list of values for the given |header_name| in the given list of
+// headers.
+std::vector<std::string> GetHeaderValues(const std::string& header_name,
+ const Array<String>& headers) {
+ std::vector<std::string> result;
+ for (std::string header : headers.storage()) {
+ if (StartsWithASCII(header, header_name, false)) {
+ auto begin = header.begin();
+ auto end = header.end();
+ begin += header_name.size();
+ // Extract the content of the header by finding the remaining string after
+ // the ':' and stripping all spaces.
+ while (begin < end && *begin != ':')
+ begin++;
+ if (begin < end) {
+ begin++;
+ while (begin < end && *begin == ' ')
+ begin++;
+ while (end > begin && *(end - 1) == ' ')
+ end--;
+ if (begin < end) {
+ result.push_back(std::string(begin, end));
+ }
+ }
+ }
+ }
+ return result;
+}
+
+// Returns whether the given directory |dir| constains a valid entry file for
+// the given |response|. If this is the case and |output| is not |nullptr|, then
+// the deserialized entry is returned in |*output|.
+bool IsCacheEntryValid(const base::FilePath& dir,
+ URLResponse* response,
+ CacheEntryPtr* output) {
+ // Find the entry file, and deserialize it.
+ base::FilePath entry_path = dir.Append("entry");
+ if (!base::PathExists(entry_path))
+ return false;
+ std::string serialized_entry;
+ if (!ReadFileToString(entry_path, &serialized_entry))
+ return false;
+ CacheEntryPtr entry;
+ Deserialize(serialized_entry, &entry);
+
+ // If |entry| or |response| has not headers, it is not possible to check if
+ // the entry is valid, so returns |false|.
+ if (entry->headers.is_null() || response->headers.is_null())
+ return false;
+
+ // Only handle etag for the moment.
+ std::string etag_header_name = kEtagHeader;
+ std::vector<std::string> entry_etags =
+ GetHeaderValues(etag_header_name, entry->headers);
+ if (entry_etags.size() == 0)
+ return false;
+ std::vector<std::string> response_etags =
+ GetHeaderValues(etag_header_name, response->headers);
+ if (response_etags.size() == 0)
+ return false;
+
+ // Looking for the first etag header.
+ bool result = entry_etags[0] == response_etags[0];
+
+ // Returns |entry| if requested.
+ if (output)
+ *output = entry.Pass();
+
+ return result;
+}
+
+} // namespace
+
+URLResponseDiskCacheImpl::URLResponseDiskCacheImpl(
+ scoped_refptr<base::SequencedWorkerPool> worker_pool,
+ const std::string& remote_application_url,
+ InterfaceRequest<URLResponseDiskCache> request)
+ : worker_pool_(worker_pool), binding_(this, request.Pass()) {
+ base_directory_ = GetBaseDirectory();
+ // The cached files are shared only for application of the same origin.
+ if (remote_application_url != "") {
+ base_directory_ = base_directory_.Append(
+ EncodeString(GURL(remote_application_url).GetOrigin().spec()));
+ }
+}
+
+URLResponseDiskCacheImpl::~URLResponseDiskCacheImpl() {
+}
+
+void URLResponseDiskCacheImpl::GetFile(URLResponsePtr response,
+ const GetFileCallback& callback) {
+ return GetFileInternal(response.Pass(),
+ base::Bind(&RunMojoCallback, callback));
+}
+
+void URLResponseDiskCacheImpl::GetExtractedContent(
+ URLResponsePtr response,
+ const GetExtractedContentCallback& callback) {
+ base::FilePath dir = GetDirName(base_directory_, response->url);
+ base::FilePath extracted_dir = dir.Append("extracted");
+ if (IsCacheEntryValid(dir, response.get(), nullptr) &&
+ PathExists(GetExtractedSentinel(dir))) {
+ callback.Run(PathToArray(extracted_dir), PathToArray(dir));
+ return;
+ }
+
+ GetFileInternal(
+ response.Pass(),
+ base::Bind(&URLResponseDiskCacheImpl::GetExtractedContentInternal,
+ base::Unretained(this), base::Bind(&RunMojoCallback, callback),
+ extracted_dir));
+}
+
+void URLResponseDiskCacheImpl::GetFileInternal(
+ URLResponsePtr response,
+ const FilePathPairCallback& callback) {
+ base::FilePath dir = GetDirName(base_directory_, response->url);
+
+ // Check if the response is cached and valid. If that's the case, returns the
+ // cached value.
+ CacheEntryPtr entry;
+ if (IsCacheEntryValid(dir, response.get(), &entry)) {
+ callback.Run(base::FilePath(entry->content_path),
+ GetConsumerCacheDirectory(dir));
+ return;
+ }
+
+ // As the response was either not cached or the cached value is not valid, if
+ // the cache directory for the response exists, it needs to be cleaned.
+ if (base::PathExists(dir)) {
+ base::FilePath to_delete;
+ CHECK(CreateTemporaryDirInDir(base_directory_, "to_delete", &to_delete));
+ CHECK(Move(dir, to_delete));
+ worker_pool_->PostTask(
+ FROM_HERE,
+ base::Bind(base::IgnoreResult(&base::DeleteFile), to_delete, true));
+ }
+
+ // If the response has not a valid body, and it is not possible to create
+ // either the cache directory or the consumer cache directory, returns an
+ // error.
+ if (!response->body.is_valid() ||
+ !base::CreateDirectoryAndGetError(dir, nullptr) ||
+ !base::CreateDirectoryAndGetError(GetConsumerCacheDirectory(dir),
+ nullptr)) {
+ callback.Run(base::FilePath(), base::FilePath());
+ return;
+ }
+
+ // Fill the entry values for the request.
+ base::FilePath entry_path = dir.Append("entry");
+ base::FilePath content;
+ CHECK(CreateTemporaryFileInDir(dir, &content));
+ entry = CacheEntry::New();
+ entry->url = response->url;
+ entry->content_path = content.value();
+ entry->headers = response->headers.Pass();
+ // Asynchronously copy the response body to the cached file. The entry is send
+ // to the callback so that it is saved on disk only if the copy of the body
+ // succeded.
+ common::CopyToFile(response->body.Pass(), content, worker_pool_.get(),
+ base::Bind(&RunCallbackWithSuccess, callback, content,
+ GetConsumerCacheDirectory(dir), entry_path,
+ base::Passed(entry.Pass())));
+}
+
+void URLResponseDiskCacheImpl::GetExtractedContentInternal(
+ const FilePathPairCallback& callback,
+ const base::FilePath& extracted_dir,
+ const base::FilePath& content,
+ const base::FilePath& dir) {
+ // If it is not possible to get the cached file, returns an error.
+ if (content.empty()) {
+ callback.Run(base::FilePath(), base::FilePath());
+ return;
+ }
+
+ // Unzip the content to the extracted directory. In case of any error, returns
+ // an error.
+ zip::ZipReader reader;
+ if (!reader.Open(content)) {
+ callback.Run(base::FilePath(), base::FilePath());
+ return;
+ }
+ while (reader.HasMore()) {
+ bool success = reader.OpenCurrentEntryInZip();
+ success = success && reader.ExtractCurrentEntryIntoDirectory(extracted_dir);
+ success = success && reader.AdvanceToNextEntry();
+ if (!success) {
+ callback.Run(base::FilePath(), base::FilePath());
+ return;
+ }
+ }
+ // We can ignore write error, as it will just force to clear the cache on the
+ // next request.
+ WriteFile(GetExtractedSentinel(dir), nullptr, 0);
+ callback.Run(extracted_dir, GetConsumerCacheDirectory(dir));
+}
+
+} // namespace mojo
diff --git a/services/url_response_disk_cache/url_response_disk_cache_impl.h b/services/url_response_disk_cache/url_response_disk_cache_impl.h
new file mode 100644
index 0000000..1154edc
--- /dev/null
+++ b/services/url_response_disk_cache/url_response_disk_cache_impl.h
@@ -0,0 +1,58 @@
+// Copyright 2015 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.
+
+#ifndef SERVICES_URL_RESPONSE_DISK_CACHE_URL_RESPONSE_DISK_CACHE_IMPL_H_
+#define SERVICES_URL_RESPONSE_DISK_CACHE_URL_RESPONSE_DISK_CACHE_IMPL_H_
+
+#include "base/files/file_path.h"
+#include "base/macros.h"
+#include "base/threading/sequenced_worker_pool.h"
+#include "mojo/public/cpp/bindings/strong_binding.h"
+#include "mojo/services/url_response_disk_cache/public/interfaces/url_response_disk_cache.mojom.h"
+
+namespace mojo {
+
+class URLResponseDiskCacheImpl : public URLResponseDiskCache {
+ public:
+ using FilePathPairCallback =
+ base::Callback<void(const base::FilePath&, const base::FilePath&)>;
+
+ URLResponseDiskCacheImpl(scoped_refptr<base::SequencedWorkerPool> worker_pool,
+ const std::string& remote_application_url,
+ InterfaceRequest<URLResponseDiskCache> request);
+ ~URLResponseDiskCacheImpl() override;
+
+ private:
+ // URLResponseDiskCache
+ void GetFile(mojo::URLResponsePtr response,
+ const GetFileCallback& callback) override;
+ void GetExtractedContent(
+ mojo::URLResponsePtr response,
+ const GetExtractedContentCallback& callback) override;
+
+ // As |GetFile|, but uses FilePath instead of mojo arrays.
+ void GetFileInternal(mojo::URLResponsePtr response,
+ const FilePathPairCallback& callback);
+
+ // Internal implementation of |GetExtractedContent|. The parameters are:
+ // |callback|: The callback to return values to the caller. It uses FilePath
+ // instead of mojo arrays.
+ // |cache_dir|: The cache dir to return to the consumer.
+ // |content|: The content of the body of the response.
+ // |dir|: The base directory under which all data for the response is stored.
+ void GetExtractedContentInternal(const FilePathPairCallback& callback,
+ const base::FilePath& cache_dir,
+ const base::FilePath& content,
+ const base::FilePath& dir);
+
+ scoped_refptr<base::SequencedWorkerPool> worker_pool_;
+ base::FilePath base_directory_;
+ StrongBinding<URLResponseDiskCache> binding_;
+
+ DISALLOW_COPY_AND_ASSIGN(URLResponseDiskCacheImpl);
+};
+
+} // namespace mojo
+
+#endif // SERVICES_URL_RESPONSE_DISK_CACHE_URL_RESPONSE_DISK_CACHE_IMPL_H_
diff --git a/shell/BUILD.gn b/shell/BUILD.gn
index fd8ff5c..5827d86 100644
--- a/shell/BUILD.gn
+++ b/shell/BUILD.gn
@@ -123,6 +123,8 @@
# TODO(vtl): Split this target into parent/child/common libs.
source_set("lib") {
sources = [
+ "background_application_loader.cc",
+ "background_application_loader.h",
"child_process_host.cc",
"child_process_host.h",
"command_line_util.cc",
@@ -141,6 +143,8 @@
"tracer.h",
"url_resolver.cc",
"url_resolver.h",
+ "url_response_disk_cache_loader.cc",
+ "url_response_disk_cache_loader.h",
]
deps = [
@@ -156,6 +160,7 @@
"//mojo/public/interfaces/application",
"//mojo/services/network/public/interfaces",
"//shell/application_manager",
+ "//services/url_response_disk_cache",
"//services/tracing:bindings",
"//url",
]
@@ -170,8 +175,6 @@
"android/android_handler.h",
"android/android_handler_loader.cc",
"android/android_handler_loader.h",
- "android/background_application_loader.cc",
- "android/background_application_loader.h",
"android/intent_receiver_manager_factory.cc",
"android/intent_receiver_manager_factory.h",
"android/intent_receiver_manager_impl.cc",
@@ -376,6 +379,7 @@
test("mojo_shell_tests") {
sources = [
+ "background_application_loader_unittest.cc",
"child_process_host_unittest.cc",
"command_line_util_unittest.cc",
"context_unittest.cc",
@@ -412,8 +416,6 @@
]
if (is_android) {
- sources += [ "android/background_application_loader_unittest.cc" ]
-
deps += [ ":jni_headers" ]
apk_deps = [
diff --git a/shell/android/android_handler.cc b/shell/android/android_handler.cc
index 036be29..9d3181b 100644
--- a/shell/android/android_handler.cc
+++ b/shell/android/android_handler.cc
@@ -8,6 +8,8 @@
#include "base/android/jni_string.h"
#include "base/files/file_path.h"
#include "base/logging.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
#include "base/scoped_native_library.h"
#include "base/trace_event/trace_event.h"
#include "jni/AndroidHandler_jni.h"
@@ -43,8 +45,7 @@
// Load the library, so that we can set the application context there if
// needed.
// TODO(vtl): We'd use a ScopedNativeLibrary, but it doesn't have .get()!
- base::NativeLibrary app_library =
- LoadNativeApplication(app_path, NativeApplicationCleanup::DELETE);
+ base::NativeLibrary app_library = LoadNativeApplication(app_path);
if (!app_library)
return;
@@ -90,20 +91,38 @@
uintptr_t tracing_id = reinterpret_cast<uintptr_t>(this);
TRACE_EVENT_ASYNC_BEGIN1("android_handler", "AndroidHandler::RunApplication",
tracing_id, "url", std::string(response->url));
- ScopedJavaLocalRef<jstring> j_archive_path =
- Java_AndroidHandler_getNewTempArchivePath(env, GetApplicationContext());
- base::FilePath archive_path(
- ConvertJavaStringToUTF8(env, j_archive_path.obj()));
+ base::FilePath extracted_dir;
+ base::FilePath cache_dir;
+ {
+ base::MessageLoop loop;
+ handler_task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&AndroidHandler::ExtractApplication, base::Unretained(this),
+ base::Unretained(&extracted_dir),
+ base::Unretained(&cache_dir), base::Passed(response.Pass()),
+ base::Bind(base::IgnoreResult(
+ &base::SingleThreadTaskRunner::PostTask),
+ loop.task_runner(), FROM_HERE,
+ base::MessageLoop::QuitWhenIdleClosure())));
+ base::RunLoop().Run();
+ }
- mojo::common::BlockingCopyToFile(response->body.Pass(), archive_path);
+ ScopedJavaLocalRef<jstring> j_extracted_dir =
+ ConvertUTF8ToJavaString(env, extracted_dir.value());
+ ScopedJavaLocalRef<jstring> j_cache_dir =
+ ConvertUTF8ToJavaString(env, cache_dir.value());
RunAndroidApplicationFn run_android_application_fn = &RunAndroidApplication;
Java_AndroidHandler_bootstrap(
- env, GetApplicationContext(), tracing_id, j_archive_path.obj(),
+ env, GetApplicationContext(), tracing_id, j_extracted_dir.obj(),
+ j_cache_dir.obj(),
application_request.PassMessagePipe().release().value(),
reinterpret_cast<jlong>(run_android_application_fn));
}
void AndroidHandler::Initialize(mojo::ApplicationImpl* app) {
+ handler_task_runner_ = base::MessageLoop::current()->task_runner();
+ app->ConnectToService("mojo:url_response_disk_cache",
+ &url_response_disk_cache_);
}
bool AndroidHandler::ConfigureIncomingConnection(
@@ -113,6 +132,28 @@
return true;
}
+void AndroidHandler::ExtractApplication(base::FilePath* extracted_dir,
+ base::FilePath* cache_dir,
+ mojo::URLResponsePtr response,
+ const base::Closure& callback) {
+ url_response_disk_cache_->GetExtractedContent(
+ response.Pass(),
+ [extracted_dir, cache_dir, callback](mojo::Array<uint8_t> extracted_path,
+ mojo::Array<uint8_t> cache_path) {
+ if (extracted_path.is_null()) {
+ *extracted_dir = base::FilePath();
+ *cache_dir = base::FilePath();
+ } else {
+ *extracted_dir = base::FilePath(
+ std::string(reinterpret_cast<char*>(&extracted_path.front()),
+ extracted_path.size()));
+ *cache_dir = base::FilePath(std::string(
+ reinterpret_cast<char*>(&cache_path.front()), cache_path.size()));
+ }
+ callback.Run();
+ });
+}
+
bool RegisterAndroidHandlerJni(JNIEnv* env) {
return RegisterNativesImpl(env);
}
diff --git a/shell/android/android_handler.h b/shell/android/android_handler.h
index f2779ff..19adf36 100644
--- a/shell/android/android_handler.h
+++ b/shell/android/android_handler.h
@@ -7,10 +7,12 @@
#include <jni.h>
+#include "base/single_thread_task_runner.h"
#include "mojo/application/content_handler_factory.h"
#include "mojo/public/cpp/application/application_delegate.h"
#include "mojo/public/cpp/application/interface_factory_impl.h"
#include "mojo/services/content_handler/public/interfaces/content_handler.mojom.h"
+#include "mojo/services/url_response_disk_cache/public/interfaces/url_response_disk_cache.mojom.h"
#include "shell/android/intent_receiver_manager_factory.h"
namespace base {
@@ -36,8 +38,15 @@
mojo::InterfaceRequest<mojo::Application> application_request,
mojo::URLResponsePtr response) override;
+ void ExtractApplication(base::FilePath* extracted_dir,
+ base::FilePath* cache_dir,
+ mojo::URLResponsePtr response,
+ const base::Closure& callback);
+
mojo::ContentHandlerFactory content_handler_factory_;
IntentReceiverManagerFactory intent_receiver_manager_factory_;
+ mojo::URLResponseDiskCachePtr url_response_disk_cache_;
+ scoped_refptr<base::SingleThreadTaskRunner> handler_task_runner_;
MOJO_DISALLOW_COPY_AND_ASSIGN(AndroidHandler);
};
diff --git a/shell/android/apk/src/org/chromium/mojo/shell/AndroidHandler.java b/shell/android/apk/src/org/chromium/mojo/shell/AndroidHandler.java
index 138ed5a..9719f19 100644
--- a/shell/android/apk/src/org/chromium/mojo/shell/AndroidHandler.java
+++ b/shell/android/apk/src/org/chromium/mojo/shell/AndroidHandler.java
@@ -14,7 +14,6 @@
import org.chromium.base.TraceEvent;
import java.io.File;
-import java.io.IOException;
import java.lang.reflect.Constructor;
/**
@@ -36,33 +35,19 @@
// File extensions used to identify application libraries in the provided archive.
private static final String JAVA_LIBRARY_SUFFIX = ".dex.jar";
private static final String NATIVE_LIBRARY_SUFFIX = ".so";
- // Filename sections used for naming temporary files holding application files.
- private static final String ARCHIVE_PREFIX = "archive";
- private static final String ARCHIVE_SUFFIX = ".zip";
- // Directories used to hold temporary files. These are cleared when clearTemporaryFiles() is
- // called.
- private static final String DEX_OUTPUT_DIRECTORY = "dex_output";
- private static final String APP_DIRECTORY = "applications";
- private static final String ASSET_DIRECTORY = "assets";
-
- /**
- * Deletes directories holding the temporary files. This should be called early on shell startup
- * to clean up after the previous run.
- */
- static void clearTemporaryFiles(Context context) {
- FileHelper.deleteRecursively(getDexOutputDir(context));
- FileHelper.deleteRecursively(getAppDir(context));
- FileHelper.deleteRecursively(getAssetDir(context));
- }
-
- /**
- * Returns the path at which the native part should save the application archive.
- */
- @CalledByNative
- private static String getNewTempArchivePath(Context context) throws IOException {
- return File.createTempFile(ARCHIVE_PREFIX, ARCHIVE_SUFFIX,
- getAppDir(context)).getAbsolutePath();
+ // Recursively finds the first file in |dir| whose name ends with |suffix|. Returns |null| if
+ // none is found.
+ static File find(File dir, String suffix) {
+ for (File child : dir.listFiles()) {
+ if (child.isDirectory()) {
+ File result = find(child, suffix);
+ if (result != null) return result;
+ } else {
+ if (child.getName().endsWith(suffix)) return child;
+ }
+ }
+ return null;
}
/**
@@ -70,56 +55,59 @@
*
* @param context the application context
* @param tracingId opaque id, used for tracing.
- * @param archivePath the path of the archive containing the application to be run
+ * @param extractedPath the path of the directory containing the application to be run
+ * @param cachePath the path of a cache directory that can be used to extract assets
* @param handle handle to the shell to be passed to the native application. On the Java side
* this is opaque payload.
* @param runApplicationPtr pointer to the function that will set the native thunks and call
* into the application MojoMain. On the Java side this is opaque payload.
*/
@CalledByNative
- private static boolean bootstrap(Context context, long tracingId, String archivePath,
- int handle, long runApplicationPtr) {
- File bootstrap_java_library;
- File bootstrap_native_library;
- try {
- TraceEvent.begin("ExtractBootstrapJavaLibrary");
- bootstrap_java_library = FileHelper.extractFromAssets(context, BOOTSTRAP_JAVA_LIBRARY,
- getAssetDir(context), true);
- TraceEvent.end("ExtractBootstrapJavaLibrary");
- TraceEvent.begin("ExtractBootstrapNativeLibrary");
- bootstrap_native_library = FileHelper.extractFromAssets(context,
- BOOTSTRAP_NATIVE_LIBRARY, getAssetDir(context), true);
- TraceEvent.end("ExtractBootstrapNativeLibrary");
- } catch (Exception e) {
- Log.e(TAG, "Extraction of bootstrap files from assets failed.", e);
- return false;
- }
+ private static boolean bootstrap(Context context, long tracingId, String extractedPath,
+ String cachePath, int handle, long runApplicationPtr) {
+ File extractedDir = new File(extractedPath);
+ File cacheDir = new File(cachePath);
+ File compiledDexDir = new File(cacheDir, "dex");
+ File assetDir = new File(cacheDir, "asset");
+ assetDir.mkdirs();
+ File preparedSentinel = new File(cacheDir, "prepared");
- File application_java_library;
- File application_native_library;
- try {
- File archive = new File(archivePath);
- TraceEvent.begin("ExtractApplicationJavaLibrary");
- application_java_library = FileHelper.extractFromArchive(archive, JAVA_LIBRARY_SUFFIX,
- getAppDir(context));
- TraceEvent.end("ExtractApplicationJavaLibrary");
- TraceEvent.begin("ExtractApplicationNativeLibrary");
- application_native_library = FileHelper.extractFromArchive(archive,
- NATIVE_LIBRARY_SUFFIX, getAppDir(context));
- TraceEvent.end("ExtractApplicationNativeLibrary");
- } catch (Exception e) {
- Log.e(TAG, "Extraction of application files from the archive failed.", e);
- return false;
+ // If the sentinel doesn't exist, extract the assets from the apk.
+ if (!preparedSentinel.exists()) {
+ compiledDexDir.mkdirs();
+ try {
+ TraceEvent.begin("ExtractBootstrapJavaLibrary");
+ FileHelper.extractFromAssets(context, BOOTSTRAP_JAVA_LIBRARY, assetDir, false);
+ TraceEvent.end("ExtractBootstrapJavaLibrary");
+ TraceEvent.begin("ExtractBootstrapNativeLibrary");
+ FileHelper.extractFromAssets(context, BOOTSTRAP_NATIVE_LIBRARY, assetDir, false);
+ TraceEvent.end("ExtractBootstrapNativeLibrary");
+ TraceEvent.begin("MoveBootstrapNativeLibrary");
+ // Rename the bootstrap library to prevent dlopen to think it is alread opened.
+ new File(assetDir, BOOTSTRAP_NATIVE_LIBRARY)
+ .renameTo(File.createTempFile("bootstrap", ".so", assetDir));
+ TraceEvent.end("MoveBootstrapNativeLibrary");
+ new java.io.FileOutputStream(preparedSentinel).close();
+ } catch (Exception e) {
+ Log.e(TAG, "Extraction of bootstrap files from assets failed.", e);
+ return false;
+ }
}
+ // Find the 4 files needed to execute the android application.
+ File bootstrap_java_library = new File(assetDir, BOOTSTRAP_JAVA_LIBRARY);
+ File bootstrap_native_library = find(assetDir, NATIVE_LIBRARY_SUFFIX);
+ File application_java_library = find(extractedDir, JAVA_LIBRARY_SUFFIX);
+ File application_native_library = find(extractedDir, NATIVE_LIBRARY_SUFFIX);
+ // Compile the java files.
String dexPath = bootstrap_java_library.getAbsolutePath() + File.pathSeparator
+ application_java_library.getAbsolutePath();
TraceEvent.begin("CreateDexClassLoader");
DexClassLoader bootstrapLoader = new DexClassLoader(dexPath,
- getDexOutputDir(context).getAbsolutePath(), null,
- ClassLoader.getSystemClassLoader());
+ compiledDexDir.getAbsolutePath(), null, ClassLoader.getSystemClassLoader());
TraceEvent.end("CreateDexClassLoader");
+ // Create the instance of the Bootstrap class from the compiled java file and run it.
try {
Class<?> loadedClass = bootstrapLoader.loadClass(BOOTSTRAP_CLASS);
Class<? extends Runnable> bootstrapClass = loadedClass.asSubclass(Runnable.class);
@@ -136,15 +124,4 @@
return true;
}
- private static File getDexOutputDir(Context context) {
- return context.getDir(DEX_OUTPUT_DIRECTORY, Context.MODE_PRIVATE);
- }
-
- private static File getAppDir(Context context) {
- return context.getDir(APP_DIRECTORY, Context.MODE_PRIVATE);
- }
-
- private static File getAssetDir(Context context) {
- return context.getDir(ASSET_DIRECTORY, Context.MODE_PRIVATE);
- }
}
diff --git a/shell/android/apk/src/org/chromium/mojo/shell/MojoShellApplication.java b/shell/android/apk/src/org/chromium/mojo/shell/MojoShellApplication.java
index 18e4050..0dbdeab 100644
--- a/shell/android/apk/src/org/chromium/mojo/shell/MojoShellApplication.java
+++ b/shell/android/apk/src/org/chromium/mojo/shell/MojoShellApplication.java
@@ -23,24 +23,16 @@
@Override
public void onCreate() {
super.onCreate();
- clearTemporaryFiles();
initializeJavaUtils();
initializeNative();
}
/**
- * Deletes the temporary files and directories created in the previous run of the application.
- * This is important regardless of cleanups on exit, as the previous run could have crashed.
- */
- private void clearTemporaryFiles() {
- AndroidHandler.clearTemporaryFiles(this);
- }
-
- /**
* Initializes Java-side utils.
*/
private void initializeJavaUtils() {
- PathUtils.setPrivateDataDirectorySuffix(PRIVATE_DATA_DIRECTORY_SUFFIX, getApplicationContext());
+ PathUtils.setPrivateDataDirectorySuffix(
+ PRIVATE_DATA_DIRECTORY_SUFFIX, getApplicationContext());
}
/**
diff --git a/shell/android/apk/src/org/chromium/mojo/shell/ShellMain.java b/shell/android/apk/src/org/chromium/mojo/shell/ShellMain.java
index ab834af..e815967 100644
--- a/shell/android/apk/src/org/chromium/mojo/shell/ShellMain.java
+++ b/shell/android/apk/src/org/chromium/mojo/shell/ShellMain.java
@@ -29,6 +29,10 @@
private static final String NETWORK_LIBRARY_APP = "network_service.mojo";
// Directory where the child executable will be extracted.
private static final String CHILD_DIRECTORY = "child";
+ // Directory to set TMPDIR to.
+ private static final String TMP_DIRECTORY = "tmp";
+ // Directory to set HOME to.
+ private static final String HOME_DIRECTORY = "home";
// Name of the child executable.
private static final String MOJO_SHELL_CHILD_EXECUTABLE = "mojo_shell_child";
// Path to the default origin of mojo: apps.
@@ -69,7 +73,8 @@
nativeInit(applicationContext, mojoShellChild.getAbsolutePath(),
parametersList.toArray(new String[parametersList.size()]),
getLocalAppsDir(applicationContext).getAbsolutePath(),
- getTmpDir(applicationContext).getAbsolutePath());
+ getTmpDir(applicationContext).getAbsolutePath(),
+ getHomeDir(applicationContext).getAbsolutePath());
sInitialized = true;
} catch (Exception e) {
Log.e(TAG, "ShellMain initialization failed.", e);
@@ -109,7 +114,11 @@
}
private static File getTmpDir(Context context) {
- return new File(context.getCacheDir(), "tmp");
+ return new File(context.getCacheDir(), TMP_DIRECTORY);
+ }
+
+ private static File getHomeDir(Context context) {
+ return context.getDir(HOME_DIRECTORY, Context.MODE_PRIVATE);
}
@CalledByNative
@@ -121,7 +130,7 @@
* Initializes the native system. This API should be called only once per process.
**/
private static native void nativeInit(Context context, String mojoShellChildPath,
- String[] parameters, String bundledAppsDirectory, String tmpDir);
+ String[] parameters, String bundledAppsDirectory, String tmpDir, String homeDir);
private static native boolean nativeStart();
diff --git a/shell/android/main.cc b/shell/android/main.cc
index ac72bcb..122925b 100644
--- a/shell/android/main.cc
+++ b/shell/android/main.cc
@@ -24,10 +24,10 @@
#include "mojo/common/message_pump_mojo.h"
#include "mojo/services/window_manager/public/interfaces/window_manager.mojom.h"
#include "shell/android/android_handler_loader.h"
-#include "shell/android/background_application_loader.h"
#include "shell/android/native_viewport_application_loader.h"
#include "shell/android/ui_application_loader_android.h"
#include "shell/application_manager/application_loader.h"
+#include "shell/background_application_loader.h"
#include "shell/command_line_util.h"
#include "shell/context.h"
#include "shell/init.h"
@@ -181,17 +181,23 @@
jstring mojo_shell_child_path,
jobjectArray jparameters,
jstring j_local_apps_directory,
- jstring j_tmp_dir) {
+ jstring j_tmp_dir,
+ jstring j_home_dir) {
g_internal_data.Get().main_activity.Reset(env, activity);
// Initially, the shell runner is not ready.
g_internal_data.Get().shell_runner_ready.reset(
new base::WaitableEvent(true, false));
std::string tmp_dir = base::android::ConvertJavaStringToUTF8(env, j_tmp_dir);
- // Setting the TMPDIR environment variable so that applications can use it.
+ // Setting the TMPDIR and HOME environment variables so that applications can
+ // use it.
// TODO(qsr) We will need our subprocesses to inherit this.
int return_value = setenv("TMPDIR", tmp_dir.c_str(), 1);
DCHECK_EQ(return_value, 0);
+ return_value = setenv(
+ "HOME", base::android::ConvertJavaStringToUTF8(env, j_home_dir).c_str(),
+ 1);
+ DCHECK_EQ(return_value, 0);
base::android::ScopedJavaLocalRef<jobject> scoped_activity(env, activity);
base::android::InitApplicationContext(env, scoped_activity);
@@ -206,8 +212,12 @@
Tracer* tracer = new Tracer;
g_internal_data.Get().tracer.reset(tracer);
bool trace_startup = command_line->HasSwitch(switches::kTraceStartup);
- if (trace_startup)
- tracer->Start(command_line->GetSwitchValueASCII(switches::kTraceStartup));
+ if (trace_startup) {
+ tracer->Start(
+ command_line->GetSwitchValueASCII(switches::kTraceStartup),
+ command_line->GetSwitchValueASCII(switches::kTraceStartupDuration),
+ tmp_dir + "/mojo_shell.trace");
+ }
g_internal_data.Get().shell_runner.reset(
new MojoShellRunner(base::FilePath(base::android::ConvertJavaStringToUTF8(
@@ -228,14 +238,7 @@
g_internal_data.Get().java_message_loop.reset(new base::MessageLoopForUI);
base::MessageLoopForUI::current()->Start();
-
- if (trace_startup) {
- g_internal_data.Get().java_message_loop->PostDelayedTask(
- FROM_HERE,
- base::Bind(&Tracer::StopAndFlushToFile, base::Unretained(tracer),
- tmp_dir + "/mojo_shell.trace"),
- base::TimeDelta::FromSeconds(5));
- }
+ tracer->DidCreateMessageLoop();
// TODO(abarth): At which point should we switch to cross-platform
// initialization?
diff --git a/shell/application_manager/BUILD.gn b/shell/application_manager/BUILD.gn
index 72a90c8..e5ebb5d 100644
--- a/shell/application_manager/BUILD.gn
+++ b/shell/application_manager/BUILD.gn
@@ -33,6 +33,7 @@
"//mojo/common",
"//mojo/public/interfaces/application:application",
"//mojo/services/network/public/interfaces",
+ "//mojo/services/url_response_disk_cache/public/interfaces",
"//url",
]
deps = [
diff --git a/shell/application_manager/application_manager.cc b/shell/application_manager/application_manager.cc
index e1a6125..abaa11a 100644
--- a/shell/application_manager/application_manager.cc
+++ b/shell/application_manager/application_manager.cc
@@ -178,23 +178,19 @@
parameters);
if (resolved_url.SchemeIsFile()) {
- new LocalFetcher(
- resolved_url, GetBaseURLAndQuery(resolved_url, nullptr),
- base::Bind(callback, NativeApplicationCleanup::DONT_DELETE));
+ new LocalFetcher(resolved_url, GetBaseURLAndQuery(resolved_url, nullptr),
+ callback);
return;
}
- if (!network_service_)
+ if (!network_service_) {
ConnectToService(GURL("mojo:network_service"), &network_service_);
-
- const NativeApplicationCleanup cleanup =
- base::CommandLine::ForCurrentProcess()->HasSwitch(
- switches::kDontDeleteOnDownload)
- ? NativeApplicationCleanup::DONT_DELETE
- : NativeApplicationCleanup::DELETE;
+ ConnectToService(GURL("mojo:url_response_disk_cache"),
+ &url_response_disk_cache_);
+ }
new NetworkFetcher(disable_cache_, resolved_url, network_service_.get(),
- base::Bind(callback, cleanup));
+ url_response_disk_cache_.get(), callback);
}
bool ApplicationManager::ConnectToRunningApplication(
@@ -274,7 +270,6 @@
ServiceProviderPtr exposed_services,
const base::Closure& on_application_end,
const std::vector<std::string>& parameters,
- NativeApplicationCleanup cleanup,
scoped_ptr<Fetcher> fetcher) {
if (!fetcher) {
// Network error. Drop |application_request| to tell requestor.
@@ -342,13 +337,12 @@
blocking_pool_,
base::Bind(&ApplicationManager::RunNativeApplication,
weak_ptr_factory_.GetWeakPtr(), base::Passed(request.Pass()),
- options, cleanup, base::Passed(fetcher.Pass())));
+ options, base::Passed(fetcher.Pass())));
}
void ApplicationManager::RunNativeApplication(
InterfaceRequest<Application> application_request,
const NativeRunnerFactory::Options& options,
- NativeApplicationCleanup cleanup,
scoped_ptr<Fetcher> fetcher,
const base::FilePath& path,
bool path_exists) {
@@ -367,7 +361,7 @@
path.AsUTF8Unsafe());
NativeRunner* runner = native_runner_factory_->Create(options).release();
native_runners_.push_back(runner);
- runner->Start(path, cleanup, application_request.Pass(),
+ runner->Start(path, application_request.Pass(),
base::Bind(&ApplicationManager::CleanupRunner,
weak_ptr_factory_.GetWeakPtr(), runner));
}
diff --git a/shell/application_manager/application_manager.h b/shell/application_manager/application_manager.h
index ded733f..a638786 100644
--- a/shell/application_manager/application_manager.h
+++ b/shell/application_manager/application_manager.h
@@ -15,6 +15,7 @@
#include "mojo/public/interfaces/application/application.mojom.h"
#include "mojo/public/interfaces/application/service_provider.mojom.h"
#include "mojo/services/network/public/interfaces/network_service.mojom.h"
+#include "mojo/services/url_response_disk_cache/public/interfaces/url_response_disk_cache.mojom.h"
#include "shell/application_manager/application_loader.h"
#include "shell/application_manager/identity.h"
#include "shell/application_manager/native_runner.h"
@@ -188,13 +189,11 @@
mojo::ServiceProviderPtr exposed_services,
const base::Closure& on_application_end,
const std::vector<std::string>& parameters,
- NativeApplicationCleanup cleanup,
scoped_ptr<Fetcher> fetcher);
void RunNativeApplication(
mojo::InterfaceRequest<mojo::Application> application_request,
const NativeRunnerFactory::Options& options,
- NativeApplicationCleanup cleanup,
scoped_ptr<Fetcher> fetcher,
const base::FilePath& file_path,
bool path_exists);
@@ -232,6 +231,7 @@
base::SequencedWorkerPool* blocking_pool_;
mojo::NetworkServicePtr network_service_;
+ mojo::URLResponseDiskCachePtr url_response_disk_cache_;
MimeTypeToURLMap mime_type_to_url_;
ScopedVector<NativeRunner> native_runners_;
bool disable_cache_;
diff --git a/shell/application_manager/local_fetcher.cc b/shell/application_manager/local_fetcher.cc
index ad08913..58d2a2d 100644
--- a/shell/application_manager/local_fetcher.cc
+++ b/shell/application_manager/local_fetcher.cc
@@ -5,6 +5,7 @@
#include "shell/application_manager/local_fetcher.h"
#include "base/bind.h"
+#include "base/files/file_enumerator.h"
#include "base/files/file_util.h"
#include "base/format_macros.h"
#include "base/message_loop/message_loop.h"
@@ -13,6 +14,7 @@
#include "base/trace_event/trace_event.h"
#include "mojo/common/common_type_converters.h"
#include "mojo/common/data_pipe_utils.h"
+#include <sys/stat.h>
#include "url/url_util.h"
namespace shell {
@@ -56,11 +58,14 @@
response->url = mojo::String::From(url_);
mojo::DataPipe data_pipe;
response->body = data_pipe.consumer_handle.Pass();
- int64 file_size;
- if (base::GetFileSize(path_, &file_size)) {
- response->headers = mojo::Array<mojo::String>(1);
+ base::stat_wrapper_t stat_result;
+ if (stat64(path_.value().c_str(), &stat_result) == 0) {
+ response->headers = mojo::Array<mojo::String>(2);
response->headers[0] =
- base::StringPrintf("Content-Length: %" PRId64, file_size);
+ base::StringPrintf("Content-Length: %" PRId64, stat_result.st_size);
+ response->headers[1] = base::StringPrintf(
+ "ETag: \"%" PRId64 "-%" PRId64 "-%" PRId64 "\"", stat_result.st_dev,
+ stat_result.st_ino, static_cast<uint64_t>(stat_result.st_mtime));
}
mojo::common::CopyFromFile(path_, data_pipe.producer_handle.Pass(), skip,
task_runner, base::Bind(&IgnoreResult));
diff --git a/shell/application_manager/native_runner.h b/shell/application_manager/native_runner.h
index e18f214..e50bb08 100644
--- a/shell/application_manager/native_runner.h
+++ b/shell/application_manager/native_runner.h
@@ -24,16 +24,15 @@
virtual ~NativeRunner() {}
// Loads the app in the file at |app_path| and runs it on some other
- // thread/process. If |cleanup| is |DELETE|, this takes ownership of the file.
+ // thread/process.
// |app_completed_callback| is posted (to the thread on which |Start()| was
// called) after |MojoMain()| completes, or on any error (including if it
// fails to start).
- // TODO(vtl): |app_path| and |cleanup| should probably be moved to the
- // factory's Create(). Rationale: The factory may need information from the
- // file to decide what kind of NativeRunner to make.
+ // TODO(vtl): |app_path| should probably be moved to the factory's Create().
+ // Rationale: The factory may need information from the file to decide what
+ // kind of NativeRunner to make.
virtual void Start(
const base::FilePath& app_path,
- NativeApplicationCleanup cleanup,
mojo::InterfaceRequest<mojo::Application> application_request,
const base::Closure& app_completed_callback) = 0;
};
diff --git a/shell/application_manager/network_fetcher.cc b/shell/application_manager/network_fetcher.cc
index 50b9e11..ca5fdd8 100644
--- a/shell/application_manager/network_fetcher.cc
+++ b/shell/application_manager/network_fetcher.cc
@@ -26,13 +26,16 @@
namespace shell {
-NetworkFetcher::NetworkFetcher(bool disable_cache,
- const GURL& url,
- mojo::NetworkService* network_service,
- const FetchCallback& loader_callback)
+NetworkFetcher::NetworkFetcher(
+ bool disable_cache,
+ const GURL& url,
+ mojo::NetworkService* network_service,
+ mojo::URLResponseDiskCache* url_response_disk_cache,
+ const FetchCallback& loader_callback)
: Fetcher(loader_callback),
disable_cache_(false),
url_(url),
+ url_response_disk_cache_(url_response_disk_cache),
weak_ptr_factory_(this) {
StartNetworkRequest(url, network_service);
}
@@ -148,14 +151,17 @@
return base::Move(old_path, *new_path);
}
-void NetworkFetcher::CopyCompleted(
+void NetworkFetcher::OnFileRetrievedFromCache(
base::Callback<void(const base::FilePath&, bool)> callback,
- bool success) {
+ mojo::Array<uint8_t> path_as_array,
+ mojo::Array<uint8_t> cache_dir) {
+ bool success = !path_as_array.is_null();
if (success) {
+ path_ = base::FilePath(std::string(
+ reinterpret_cast<char*>(&path_as_array.front()), path_as_array.size()));
if (base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kPredictableAppFilenames)) {
// The copy completed, now move to $TMP/$APP_ID.mojo before the dlopen.
- success = false;
base::FilePath new_path;
if (RenameToAppId(url_, path_, &new_path)) {
if (base::PathExists(new_path)) {
@@ -176,17 +182,12 @@
void NetworkFetcher::AsPath(
base::TaskRunner* task_runner,
base::Callback<void(const base::FilePath&, bool)> callback) {
- if (!path_.empty() || !response_) {
- base::MessageLoop::current()->PostTask(
- FROM_HERE, base::Bind(callback, path_, base::PathExists(path_)));
- return;
- }
+ // This should only called once, when we have a response.
+ DCHECK(response_.get());
- base::CreateTemporaryFile(&path_);
- mojo::common::CopyToFile(
- response_->body.Pass(), path_, task_runner,
- base::Bind(&NetworkFetcher::CopyCompleted, weak_ptr_factory_.GetWeakPtr(),
- callback));
+ url_response_disk_cache_->GetFile(
+ response_.Pass(), base::Bind(&NetworkFetcher::OnFileRetrievedFromCache,
+ weak_ptr_factory_.GetWeakPtr(), callback));
}
std::string NetworkFetcher::MimeType() {
diff --git a/shell/application_manager/network_fetcher.h b/shell/application_manager/network_fetcher.h
index f8632ad..c512b70 100644
--- a/shell/application_manager/network_fetcher.h
+++ b/shell/application_manager/network_fetcher.h
@@ -10,6 +10,7 @@
#include "base/files/file_path.h"
#include "base/memory/weak_ptr.h"
#include "mojo/services/network/public/interfaces/url_loader.mojom.h"
+#include "mojo/services/url_response_disk_cache/public/interfaces/url_response_disk_cache.mojom.h"
#include "url/gurl.h"
namespace mojo {
@@ -24,6 +25,7 @@
NetworkFetcher(bool disable_cache,
const GURL& url,
mojo::NetworkService* network_service,
+ mojo::URLResponseDiskCache* url_response_disk_cache,
const FetchCallback& loader_callback);
~NetworkFetcher() override;
@@ -50,8 +52,10 @@
const base::FilePath& old_path,
base::FilePath* new_path);
- void CopyCompleted(base::Callback<void(const base::FilePath&, bool)> callback,
- bool success);
+ void OnFileRetrievedFromCache(
+ base::Callback<void(const base::FilePath&, bool)> callback,
+ mojo::Array<uint8_t> path_as_array,
+ mojo::Array<uint8_t> cache_dir);
void AsPath(
base::TaskRunner* task_runner,
@@ -70,6 +74,7 @@
bool disable_cache_;
const GURL url_;
+ mojo::URLResponseDiskCache* url_response_disk_cache_;
mojo::URLLoaderPtr url_loader_;
mojo::URLResponsePtr response_;
base::FilePath path_;
diff --git a/shell/android/background_application_loader.cc b/shell/background_application_loader.cc
similarity index 97%
rename from shell/android/background_application_loader.cc
rename to shell/background_application_loader.cc
index 849d312..476f60a 100644
--- a/shell/android/background_application_loader.cc
+++ b/shell/background_application_loader.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "shell/android/background_application_loader.h"
+#include "shell/background_application_loader.h"
#include "base/bind.h"
#include "base/run_loop.h"
diff --git a/shell/android/background_application_loader.h b/shell/background_application_loader.h
similarity index 92%
rename from shell/android/background_application_loader.h
rename to shell/background_application_loader.h
index 5362963..6d9cb6d 100644
--- a/shell/android/background_application_loader.h
+++ b/shell/background_application_loader.h
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef SHELL_ANDROID_BACKGROUND_APPLICATION_LOADER_H_
-#define SHELL_ANDROID_BACKGROUND_APPLICATION_LOADER_H_
+#ifndef SHELL_BACKGROUND_APPLICATION_LOADER_H_
+#define SHELL_BACKGROUND_APPLICATION_LOADER_H_
#include "base/macros.h"
#include "base/memory/scoped_ptr.h"
@@ -60,4 +60,4 @@
} // namespace shell
-#endif // SHELL_ANDROID_BACKGROUND_APPLICATION_LOADER_H_
+#endif // SHELL_BACKGROUND_APPLICATION_LOADER_H_
diff --git a/shell/android/background_application_loader_unittest.cc b/shell/background_application_loader_unittest.cc
similarity index 90%
rename from shell/android/background_application_loader_unittest.cc
rename to shell/background_application_loader_unittest.cc
index 17291b4..b9af70c 100644
--- a/shell/android/background_application_loader_unittest.cc
+++ b/shell/background_application_loader_unittest.cc
@@ -2,9 +2,10 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "shell/android/background_application_loader.h"
+#include "shell/background_application_loader.h"
#include "mojo/public/interfaces/application/application.mojom.h"
+#include "shell/context.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace shell {
@@ -31,6 +32,7 @@
// Tests that the loader can start and stop gracefully.
TEST(BackgroundApplicationLoaderTest, StartStop) {
+ Context::EnsureEmbedderIsInitialized();
scoped_ptr<ApplicationLoader> real_loader(new DummyLoader());
BackgroundApplicationLoader loader(real_loader.Pass(), "test",
base::MessageLoop::TYPE_DEFAULT);
@@ -39,6 +41,7 @@
// Tests that the loader can load a service that is well behaved (quits
// itself).
TEST(BackgroundApplicationLoaderTest, Load) {
+ Context::EnsureEmbedderIsInitialized();
scoped_ptr<ApplicationLoader> real_loader(new DummyLoader());
BackgroundApplicationLoader loader(real_loader.Pass(), "test",
base::MessageLoop::TYPE_DEFAULT);
diff --git a/shell/child_controller.mojom b/shell/child_controller.mojom
index 7313410..a0bf785 100644
--- a/shell/child_controller.mojom
+++ b/shell/child_controller.mojom
@@ -7,9 +7,8 @@
import "mojo/public/interfaces/application/application.mojom";
interface ChildController {
- // Starts the app at the given path (deleting it if |clean_app_path| is true).
+ // Starts the app at the given path.
StartApp(string app_path,
- bool clean_app_path,
mojo.Application& application_request) => (int32 result);
// Exits the child process now (with no cleanup), with the given exit code.
diff --git a/shell/child_main.cc b/shell/child_main.cc
index 6fc14b3..1e3834e 100644
--- a/shell/child_main.cc
+++ b/shell/child_main.cc
@@ -216,7 +216,6 @@
// |ChildController| methods:
void StartApp(const mojo::String& app_path,
- bool clean_app_path,
mojo::InterfaceRequest<mojo::Application> application_request,
const StartAppCallback& on_app_complete) override {
DVLOG(2) << "ChildControllerImpl::StartApp(" << app_path << ", ...)";
@@ -225,9 +224,6 @@
on_app_complete_ = on_app_complete;
unblocker_.Unblock(base::Bind(&ChildControllerImpl::StartAppOnMainThread,
base::FilePath::FromUTF8Unsafe(app_path),
- clean_app_path
- ? NativeApplicationCleanup::DELETE
- : NativeApplicationCleanup::DONT_DELETE,
base::Passed(&application_request)));
}
@@ -255,7 +251,6 @@
static void StartAppOnMainThread(
const base::FilePath& app_path,
- NativeApplicationCleanup cleanup,
mojo::InterfaceRequest<mojo::Application> application_request) {
// TODO(vtl): This is copied from in_process_native_runner.cc.
DVLOG(2) << "Loading/running Mojo app from " << app_path.value()
@@ -263,7 +258,7 @@
// We intentionally don't unload the native library as its lifetime is the
// same as that of the process.
- base::NativeLibrary app_library = LoadNativeApplication(app_path, cleanup);
+ base::NativeLibrary app_library = LoadNativeApplication(app_path);
RunNativeApplication(app_library, application_request.Pass());
}
diff --git a/shell/child_process_host.cc b/shell/child_process_host.cc
index d0ab5dd..8660162 100644
--- a/shell/child_process_host.cc
+++ b/shell/child_process_host.cc
@@ -61,14 +61,13 @@
void ChildProcessHost::StartApp(
const mojo::String& app_path,
- bool clean_app_path,
mojo::InterfaceRequest<mojo::Application> application_request,
const ChildController::StartAppCallback& on_app_complete) {
DCHECK(controller_);
on_app_complete_ = on_app_complete;
controller_->StartApp(
- app_path, clean_app_path, application_request.Pass(),
+ app_path, application_request.Pass(),
base::Bind(&ChildProcessHost::AppCompleted, base::Unretained(this)));
}
diff --git a/shell/child_process_host.h b/shell/child_process_host.h
index 86a6afa..c0cef79 100644
--- a/shell/child_process_host.h
+++ b/shell/child_process_host.h
@@ -52,7 +52,6 @@
// |on_app_complete| will *always* get called, even on connection error (or
// even if the child process failed to start at all).
void StartApp(const mojo::String& app_path,
- bool clean_app_path,
mojo::InterfaceRequest<mojo::Application> application_request,
const ChildController::StartAppCallback& on_app_complete);
void ExitNow(int32_t exit_code);
diff --git a/shell/child_process_host_unittest.cc b/shell/child_process_host_unittest.cc
index 31ace06..bb1817b 100644
--- a/shell/child_process_host_unittest.cc
+++ b/shell/child_process_host_unittest.cc
@@ -83,7 +83,7 @@
application_request.Bind(mp.handle0.Pass());
// This won't actually be called, but the callback should be run.
MojoResult result = MOJO_RESULT_INTERNAL;
- child_process_host.StartApp("/does_not_exist/cbvgyuio", false,
+ child_process_host.StartApp("/does_not_exist/cbvgyuio",
application_request.Pass(), [&result](int32_t r) {
result = r;
base::MessageLoop::current()->QuitWhenIdle();
diff --git a/shell/context.cc b/shell/context.cc
index 8aa8a7a..331efa1 100644
--- a/shell/context.cc
+++ b/shell/context.cc
@@ -31,12 +31,14 @@
#include "services/tracing/tracing.mojom.h"
#include "shell/application_manager/application_loader.h"
#include "shell/application_manager/application_manager.h"
+#include "shell/background_application_loader.h"
#include "shell/command_line_util.h"
#include "shell/filename_util.h"
#include "shell/in_process_native_runner.h"
#include "shell/out_of_process_native_runner.h"
#include "shell/switches.h"
#include "shell/tracer.h"
+#include "shell/url_response_disk_cache_loader.h"
#include "url/gurl.h"
using mojo::ServiceProvider;
@@ -258,6 +260,12 @@
mojo_shell_child_path_ = shell_child_path;
+ application_manager()->SetLoaderForURL(
+ make_scoped_ptr(new BackgroundApplicationLoader(
+ make_scoped_ptr(new URLResponseDiskCacheLoader()),
+ "url_response_disk_cache", base::MessageLoop::TYPE_DEFAULT)),
+ GURL("mojo:url_response_disk_cache"));
+
EnsureEmbedderIsInitialized();
task_runners_.reset(
new TaskRunners(base::MessageLoop::current()->message_loop_proxy()));
diff --git a/shell/desktop/main.cc b/shell/desktop/main.cc
index 6139245..ca79c4c 100644
--- a/shell/desktop/main.cc
+++ b/shell/desktop/main.cc
@@ -35,7 +35,8 @@
<< " [--" << switches::kDisableCache << "]"
<< " [--" << switches::kEnableMultiprocess << "]"
<< " [--" << switches::kOrigin << "=<url-lib-path>]"
- << " [--" << switches::kTraceStartup << "]"
+ << " [--" << switches::kTraceStartup << "[=\"list,of,categories\"]]"
+ << " [--" << switches::kTraceStartupDuration << "=<seconds>]"
<< " [--" << switches::kURLMappings << "=from1=to1,from2=to2]"
<< " [--" << switches::kPredictableAppFilenames << "]"
<< " [--" << switches::kWaitForDebugger << "]"
@@ -77,8 +78,12 @@
}
bool trace_startup = command_line.HasSwitch(switches::kTraceStartup);
- if (trace_startup)
- tracer.Start(command_line.GetSwitchValueASCII(switches::kTraceStartup));
+ if (trace_startup) {
+ tracer.Start(
+ command_line.GetSwitchValueASCII(switches::kTraceStartup),
+ command_line.GetSwitchValueASCII(switches::kTraceStartupDuration),
+ "mojo_shell.trace");
+ }
if (command_line.HasSwitch(switches::kCPUProfile)) {
#if !defined(NDEBUG) || !defined(ENABLE_PROFILING)
@@ -94,13 +99,7 @@
shell::Context shell_context(&tracer);
{
base::MessageLoop message_loop;
- if (trace_startup) {
- message_loop.PostDelayedTask(
- FROM_HERE, base::Bind(&shell::Tracer::StopAndFlushToFile,
- base::Unretained(&tracer), "mojo_shell.trace"),
- base::TimeDelta::FromSeconds(5));
- }
-
+ tracer.DidCreateMessageLoop();
if (!shell_context.Init()) {
Usage();
return 1;
@@ -123,6 +122,6 @@
if (command_line.HasSwitch(switches::kCPUProfile))
base::debug::StopProfiling();
if (trace_startup)
- tracer.StopAndFlushToFile("mojo_shell.trace");
+ tracer.StopAndFlushToFile();
return 0;
}
diff --git a/shell/in_process_native_runner.cc b/shell/in_process_native_runner.cc
index f12708d..f5ae3ee 100644
--- a/shell/in_process_native_runner.cc
+++ b/shell/in_process_native_runner.cc
@@ -14,7 +14,7 @@
namespace shell {
InProcessNativeRunner::InProcessNativeRunner(Context* context)
- : cleanup_(NativeApplicationCleanup::DONT_DELETE), app_library_(nullptr) {
+ : app_library_(nullptr) {
}
InProcessNativeRunner::~InProcessNativeRunner() {
@@ -30,11 +30,9 @@
void InProcessNativeRunner::Start(
const base::FilePath& app_path,
- NativeApplicationCleanup cleanup,
mojo::InterfaceRequest<mojo::Application> application_request,
const base::Closure& app_completed_callback) {
app_path_ = app_path;
- cleanup_ = cleanup;
DCHECK(!application_request_.is_pending());
application_request_ = application_request.Pass();
@@ -56,7 +54,7 @@
<< " thread id=" << base::PlatformThread::CurrentId();
// TODO(vtl): ScopedNativeLibrary doesn't have a .get() method!
- base::NativeLibrary app_library = LoadNativeApplication(app_path_, cleanup_);
+ base::NativeLibrary app_library = LoadNativeApplication(app_path_);
app_library_.Reset(app_library);
RunNativeApplication(app_library, application_request_.Pass());
app_completed_callback_runner_.Run();
diff --git a/shell/in_process_native_runner.h b/shell/in_process_native_runner.h
index 0d5e870..41065c1 100644
--- a/shell/in_process_native_runner.h
+++ b/shell/in_process_native_runner.h
@@ -28,7 +28,6 @@
// |NativeRunner| method:
void Start(const base::FilePath& app_path,
- NativeApplicationCleanup cleanup,
mojo::InterfaceRequest<mojo::Application> application_request,
const base::Closure& app_completed_callback) override;
@@ -37,7 +36,6 @@
void Run() override;
base::FilePath app_path_;
- NativeApplicationCleanup cleanup_;
mojo::InterfaceRequest<mojo::Application> application_request_;
base::Callback<bool(void)> app_completed_callback_runner_;
diff --git a/shell/native_application_support.cc b/shell/native_application_support.cc
index 378a014..f82b4ef 100644
--- a/shell/native_application_support.cc
+++ b/shell/native_application_support.cc
@@ -42,14 +42,11 @@
} // namespace
-base::NativeLibrary LoadNativeApplication(const base::FilePath& app_path,
- NativeApplicationCleanup cleanup) {
+base::NativeLibrary LoadNativeApplication(const base::FilePath& app_path) {
DVLOG(2) << "Loading Mojo app in process from library: " << app_path.value();
base::NativeLibraryLoadError error;
base::NativeLibrary app_library = base::LoadNativeLibrary(app_path, &error);
- if (cleanup == NativeApplicationCleanup::DELETE)
- DeleteFile(app_path, false);
LOG_IF(ERROR, !app_library)
<< "Failed to load app library (error: " << error.ToString() << ")";
return app_library;
diff --git a/shell/native_application_support.h b/shell/native_application_support.h
index 2c3b0db..5e24d4b 100644
--- a/shell/native_application_support.h
+++ b/shell/native_application_support.h
@@ -18,19 +18,15 @@
namespace shell {
-enum class NativeApplicationCleanup { DELETE, DONT_DELETE };
-
// Loads the native Mojo application from the DSO specified by |app_path|.
// Returns the |base::NativeLibrary| for the application on success (or null on
-// failure). If |cleanup| is |DELETE|, it will delete |app_path| (regardless of
-// sucess or failure).
+// failure).
//
// Note: The caller may choose to eventually unload the returned DSO. If so,
// this should be done only after the thread on which |LoadNativeApplication()|
// and |RunNativeApplication()| were called has terminated, so that any
// thread-local destructors have been executed.
-base::NativeLibrary LoadNativeApplication(const base::FilePath& app_path,
- NativeApplicationCleanup cleanup);
+base::NativeLibrary LoadNativeApplication(const base::FilePath& app_path);
// Runs the native Mojo application from the DSO that was loaded using
// |LoadNativeApplication()|; this tolerates |app_library| being null. This
diff --git a/shell/native_runner_unittest.cc b/shell/native_runner_unittest.cc
index 2b732c0..fe792c9 100644
--- a/shell/native_runner_unittest.cc
+++ b/shell/native_runner_unittest.cc
@@ -32,7 +32,6 @@
base::MessageLoop::current()->Quit();
}
void Start(const base::FilePath& app_path,
- NativeApplicationCleanup cleanup,
mojo::InterfaceRequest<mojo::Application> application_request,
const base::Closure& app_completed_callback) override {
state_->runner_was_started = true;
diff --git a/shell/out_of_process_native_runner.cc b/shell/out_of_process_native_runner.cc
index da3abdb..39cf1f7 100644
--- a/shell/out_of_process_native_runner.cc
+++ b/shell/out_of_process_native_runner.cc
@@ -29,7 +29,6 @@
void OutOfProcessNativeRunner::Start(
const base::FilePath& app_path,
- NativeApplicationCleanup cleanup,
mojo::InterfaceRequest<mojo::Application> application_request,
const base::Closure& app_completed_callback) {
app_path_ = app_path;
@@ -42,8 +41,7 @@
// TODO(vtl): |app_path.AsUTF8Unsafe()| is unsafe.
child_process_host_->StartApp(
- app_path.AsUTF8Unsafe(), cleanup == NativeApplicationCleanup::DELETE,
- application_request.Pass(),
+ app_path.AsUTF8Unsafe(), application_request.Pass(),
base::Bind(&OutOfProcessNativeRunner::AppCompleted,
base::Unretained(this)));
}
diff --git a/shell/out_of_process_native_runner.h b/shell/out_of_process_native_runner.h
index ff86668..23e0372 100644
--- a/shell/out_of_process_native_runner.h
+++ b/shell/out_of_process_native_runner.h
@@ -26,7 +26,6 @@
// |NativeRunner| method:
void Start(const base::FilePath& app_path,
- NativeApplicationCleanup cleanup,
mojo::InterfaceRequest<mojo::Application> application_request,
const base::Closure& app_completed_callback) override;
diff --git a/shell/switches.cc b/shell/switches.cc
index 518d668..5606fca 100644
--- a/shell/switches.cc
+++ b/shell/switches.cc
@@ -70,6 +70,10 @@
// seconds or when the shell exits.
const char kTraceStartup[] = "trace-startup";
+// Sets the time in seconds until startup tracing ends. If omitted a default of
+// 5 seconds is used.
+const char kTraceStartupDuration[] = "trace-startup-duration";
+
// Specifies a set of mappings to apply when resolving urls. The value is a set
// of ',' separated mappings, where each mapping consists of a pair of urls
// giving the to/from url to map. For example, 'a=b,c=d' contains two mappings,
@@ -91,6 +95,7 @@
kOrigin,
kPredictableAppFilenames,
kTraceStartup,
+ kTraceStartupDuration,
kURLMappings};
const std::set<std::string> GetAllSwitches() {
diff --git a/shell/switches.h b/shell/switches.h
index c6bb68e..a1ff4c4 100644
--- a/shell/switches.h
+++ b/shell/switches.h
@@ -26,6 +26,7 @@
extern const char kOrigin[];
extern const char kPredictableAppFilenames[];
extern const char kTraceStartup[];
+extern const char kTraceStartupDuration[];
extern const char kURLMappings[];
extern const std::set<std::string> GetAllSwitches();
diff --git a/shell/tracer.cc b/shell/tracer.cc
index 09981e1..329360a 100644
--- a/shell/tracer.cc
+++ b/shell/tracer.cc
@@ -8,6 +8,7 @@
#include <string.h>
#include "base/message_loop/message_loop.h"
+#include "base/strings/string_number_conversions.h"
#include "base/synchronization/waitable_event.h"
#include "base/threading/thread.h"
#include "base/trace_event/trace_event.h"
@@ -21,26 +22,46 @@
Tracer::~Tracer() {
}
-void Tracer::Start(const std::string& categories) {
+void Tracer::Start(const std::string& categories,
+ const std::string& duration_seconds_str,
+ const std::string& filename) {
+ trace_duration_secs_ = 5;
+ if (!duration_seconds_str.empty()) {
+ CHECK(base::StringToInt(duration_seconds_str, &trace_duration_secs_))
+ << "Could not parse --trace-startup-duration value "
+ << duration_seconds_str;
+ }
tracing_ = true;
+ trace_filename_ = filename;
+ categories_ = categories;
base::trace_event::CategoryFilter category_filter(categories);
base::trace_event::TraceLog::GetInstance()->SetEnabled(
category_filter, base::trace_event::TraceLog::RECORDING_MODE,
base::trace_event::TraceOptions(base::trace_event::RECORD_UNTIL_FULL));
}
+void Tracer::DidCreateMessageLoop() {
+ if (!tracing_)
+ return;
+
+ base::MessageLoop::current()->PostDelayedTask(
+ FROM_HERE,
+ base::Bind(&shell::Tracer::StopAndFlushToFile, base::Unretained(this)),
+ base::TimeDelta::FromSeconds(trace_duration_secs_));
+}
+
void Tracer::StartCollectingFromTracingService(
tracing::TraceCoordinatorPtr coordinator) {
coordinator_ = coordinator.Pass();
mojo::DataPipe data_pipe;
- coordinator_->Start(data_pipe.producer_handle.Pass(), "*");
+ coordinator_->Start(data_pipe.producer_handle.Pass(), categories_);
drainer_.reset(new mojo::common::DataPipeDrainer(
this, data_pipe.consumer_handle.Pass()));
}
-void Tracer::StopAndFlushToFile(const std::string& filename) {
+void Tracer::StopAndFlushToFile() {
if (tracing_)
- StopTracingAndFlushToDisk(filename);
+ StopTracingAndFlushToDisk();
}
void Tracer::ConnectToController(
@@ -49,11 +70,10 @@
impl->set_tracing_already_started(tracing_);
}
-void Tracer::StopTracingAndFlushToDisk(const std::string& filename) {
+void Tracer::StopTracingAndFlushToDisk() {
tracing_ = false;
- trace_file_ = fopen(filename.c_str(), "w+");
+ trace_file_ = fopen(trace_filename_.c_str(), "w+");
PCHECK(trace_file_);
- trace_filename_ = filename;
static const char kStart[] = "{\"traceEvents\":[";
PCHECK(fwrite(kStart, 1, strlen(kStart), trace_file_) == strlen(kStart));
@@ -78,7 +98,7 @@
flush_thread.message_loop()->PostTask(
FROM_HERE,
base::Bind(&Tracer::EndTraceAndFlush, base::Unretained(this),
- filename,
+ trace_filename_,
base::Bind(&base::WaitableEvent::Signal,
base::Unretained(&flush_complete_event))));
base::trace_event::TraceLog::GetInstance()
diff --git a/shell/tracer.h b/shell/tracer.h
index bcd4968..a32cbb8 100644
--- a/shell/tracer.h
+++ b/shell/tracer.h
@@ -29,24 +29,32 @@
Tracer();
~Tracer() override;
- // Starts tracing the current process with the given set of categories.
- void Start(const std::string& categories);
+ // Starts tracing the current process with the given set of categories. The
+ // tracing results will be saved into the specified filename when
+ // StopAndFlushToFile() is called.
+ void Start(const std::string& categories,
+ const std::string& duration_seconds_str,
+ const std::string& filename);
+
+ // Notifies the tracer that a message loop has been created. If startup
+ // tracing is active the tracer can use this to schedule when to stop tracing.
+ void DidCreateMessageLoop();
// Starts collecting data from the tracing service with the given set of
// categories.
void StartCollectingFromTracingService(
tracing::TraceCoordinatorPtr coordinator);
- // Stops tracing and flushes all collected trace data to the given filename.
- // Blocks until the file write is complete. May be called after the message
- // loop is shut down.
- void StopAndFlushToFile(const std::string& filename);
+ // Stops tracing and flushes all collected trace data to the file specified in
+ // Start(). Blocks until the file write is complete. May be called after the
+ // message loop is shut down.
+ void StopAndFlushToFile();
void ConnectToController(
mojo::InterfaceRequest<tracing::TraceController> request);
private:
- void StopTracingAndFlushToDisk(const std::string& filename);
+ void StopTracingAndFlushToDisk();
// Called from the flush thread. When all data is collected this runs
// |done_callback| on the flush thread.
@@ -75,6 +83,10 @@
// Whether we're currently tracing.
bool tracing_;
+ // How long to trace after message loop creation.
+ int trace_duration_secs_;
+ // Categories to trace.
+ std::string categories_;
// Whether we've written the first chunk.
bool first_chunk_written_;
diff --git a/shell/url_response_disk_cache_loader.cc b/shell/url_response_disk_cache_loader.cc
new file mode 100644
index 0000000..3371cb5
--- /dev/null
+++ b/shell/url_response_disk_cache_loader.cc
@@ -0,0 +1,23 @@
+// 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 "shell/url_response_disk_cache_loader.h"
+
+namespace shell {
+
+URLResponseDiskCacheLoader::URLResponseDiskCacheLoader() {
+}
+
+URLResponseDiskCacheLoader::~URLResponseDiskCacheLoader() {
+}
+
+void URLResponseDiskCacheLoader::Load(
+ const GURL& url,
+ mojo::InterfaceRequest<mojo::Application> application_request) {
+ DCHECK(application_request.is_pending());
+ application_.reset(new mojo::ApplicationImpl(&url_response_disk_cache_,
+ application_request.Pass()));
+}
+
+} // namespace shell
diff --git a/shell/url_response_disk_cache_loader.h b/shell/url_response_disk_cache_loader.h
new file mode 100644
index 0000000..4f75433
--- /dev/null
+++ b/shell/url_response_disk_cache_loader.h
@@ -0,0 +1,35 @@
+// 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.
+
+#ifndef SHELL_URL_RESPONSE_DISK_CACHE_LOADER_H_
+#define SHELL_URL_RESPONSE_DISK_CACHE_LOADER_H_
+
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "mojo/public/cpp/application/application_impl.h"
+#include "services/url_response_disk_cache/url_response_disk_cache_app.h"
+#include "shell/application_manager/application_loader.h"
+
+namespace shell {
+
+class URLResponseDiskCacheLoader : public ApplicationLoader {
+ public:
+ URLResponseDiskCacheLoader();
+ ~URLResponseDiskCacheLoader() override;
+
+ private:
+ // ApplicationLoader overrides:
+ void Load(
+ const GURL& url,
+ mojo::InterfaceRequest<mojo::Application> application_request) override;
+
+ mojo::URLResponseDiskCacheApp url_response_disk_cache_;
+ scoped_ptr<mojo::ApplicationImpl> application_;
+
+ DISALLOW_COPY_AND_ASSIGN(URLResponseDiskCacheLoader);
+};
+
+} // namespace shell
+
+#endif // SHELL_URL_RESPONSE_DISK_CACHE_LOADER_H_
diff --git a/sky/engine/bindings/BUILD.gn b/sky/engine/bindings/BUILD.gn
index b7dbafe..fd6a30c 100644
--- a/sky/engine/bindings/BUILD.gn
+++ b/sky/engine/bindings/BUILD.gn
@@ -30,6 +30,9 @@
"scheduled_action.cc",
"scheduled_action.h",
]
+
+ defines = [ "DART_IO_SECURE_SOCKET_DISABLED" ]
+
deps = [
"//base",
"//dart/runtime/bin:libdart_withcore",
@@ -38,12 +41,14 @@
"//sky/engine/platform:platform",
"//sky/engine/tonic",
"//sky/engine/wtf",
+ "//dart/runtime/bin:embedded_dart_io",
":generated_bindings",
":snapshot_cc",
]
include_dirs = [
"..",
"$root_build_dir",
+ rebase_path("//dart/runtime"),
]
}
diff --git a/sky/engine/bindings/builtin.cc b/sky/engine/bindings/builtin.cc
index 81fd9d3..3fa5c39 100644
--- a/sky/engine/bindings/builtin.cc
+++ b/sky/engine/bindings/builtin.cc
@@ -6,6 +6,7 @@
#include "sky/engine/bindings/builtin.h"
#include "base/logging.h"
+#include "bin/io_natives.h"
#include "dart/runtime/include/dart_api.h"
#include "gen/sky/bindings/DartGlobal.h"
#include "sky/engine/bindings/builtin_natives.h"
@@ -31,6 +32,7 @@
BuiltinNatives::NativeLookup},
{"dart:sky", true, skySnapshotSymbolizer, skySnapshotResolver},
{"dart:mojo.internal", true, MojoNativeSymbol, MojoNativeLookup},
+ {"dart:io", true, dart::bin::IONativeSymbol, dart::bin::IONativeLookup },
};
} // namespace
diff --git a/sky/engine/bindings/builtin.h b/sky/engine/bindings/builtin.h
index 8c3bb05..93f67a2 100644
--- a/sky/engine/bindings/builtin.h
+++ b/sky/engine/bindings/builtin.h
@@ -18,6 +18,7 @@
kBuiltinLibrary,
kSkyLibrary,
kMojoInternalLibrary,
+ kIOLibrary,
kInvalidLibrary,
};
diff --git a/sky/engine/bindings/builtin_natives.cc b/sky/engine/bindings/builtin_natives.cc
index 55584a3..90bec2e 100644
--- a/sky/engine/bindings/builtin_natives.cc
+++ b/sky/engine/bindings/builtin_natives.cc
@@ -87,7 +87,8 @@
return closure;
}
-static void InitDartInternal(Dart_Handle builtin_library) {
+static void InitDartInternal(Dart_Handle builtin_library,
+ BuiltinNatives::IsolateType isolate_type) {
Dart_Handle print = GetClosure(builtin_library, "_getPrintClosure");
Dart_Handle timer = GetClosure(builtin_library, "_getCreateTimerClosure");
@@ -96,35 +97,55 @@
DART_CHECK_VALID(Dart_SetField(
internal_library, ToDart("_printClosure"), print));
- Dart_Handle vm_hooks_name = ToDart("VMLibraryHooks");
- Dart_Handle vm_hooks = Dart_GetClass(internal_library, vm_hooks_name);
- DART_CHECK_VALID(vm_hooks);
- Dart_Handle timer_name = ToDart("timerFactory");
- DART_CHECK_VALID(Dart_SetField(vm_hooks, timer_name, timer));
+ if (isolate_type == BuiltinNatives::MainIsolate) {
+ Dart_Handle vm_hooks_name = ToDart("VMLibraryHooks");
+ Dart_Handle vm_hooks = Dart_GetClass(internal_library, vm_hooks_name);
+ DART_CHECK_VALID(vm_hooks);
+ Dart_Handle timer_name = ToDart("timerFactory");
+ DART_CHECK_VALID(Dart_SetField(vm_hooks, timer_name, timer));
+ } else {
+ CHECK(isolate_type == BuiltinNatives::DartIOIsolate);
+ Dart_Handle io_lib = DartBuiltin::LookupLibrary("dart:io");
+ Dart_Handle setup_hooks = Dart_NewStringFromCString("_setupHooks");
+ DART_CHECK_VALID(Dart_Invoke(io_lib, setup_hooks, 0, NULL));
+ Dart_Handle isolate_lib = DartBuiltin::LookupLibrary("dart:isolate");
+ DART_CHECK_VALID(Dart_Invoke(isolate_lib, setup_hooks, 0, NULL));
+ }
}
-static void InitDartCore(Dart_Handle builtin) {
+static void InitDartCore(Dart_Handle builtin,
+ BuiltinNatives::IsolateType isolate_type) {
Dart_Handle get_base_url = GetClosure(builtin, "_getGetBaseURLClosure");
Dart_Handle core_library = DartBuiltin::LookupLibrary("dart:core");
DART_CHECK_VALID(Dart_SetField(core_library,
ToDart("_uriBaseClosure"), get_base_url));
}
-static void InitDartAsync(Dart_Handle builtin_library) {
- Dart_Handle schedule_microtask =
- GetClosure(builtin_library, "_getScheduleMicrotaskClosure");
+static void InitDartAsync(Dart_Handle builtin_library,
+ BuiltinNatives::IsolateType isolate_type) {
+ Dart_Handle schedule_microtask;
+ if (isolate_type == BuiltinNatives::MainIsolate) {
+ schedule_microtask =
+ GetClosure(builtin_library, "_getScheduleMicrotaskClosure");
+ } else {
+ CHECK(isolate_type == BuiltinNatives::DartIOIsolate);
+ Dart_Handle isolate_lib = DartBuiltin::LookupLibrary("dart:isolate");
+ Dart_Handle method_name =
+ Dart_NewStringFromCString("_getIsolateScheduleImmediateClosure");
+ schedule_microtask = Dart_Invoke(isolate_lib, method_name, 0, NULL);
+ }
Dart_Handle async_library = DartBuiltin::LookupLibrary("dart:async");
Dart_Handle set_schedule_microtask = ToDart("_setScheduleImmediateClosure");
DART_CHECK_VALID(Dart_Invoke(async_library, set_schedule_microtask, 1,
&schedule_microtask));
}
-void BuiltinNatives::Init() {
+void BuiltinNatives::Init(IsolateType isolate_type) {
Dart_Handle builtin = Builtin::LoadAndCheckLibrary(Builtin::kBuiltinLibrary);
DART_CHECK_VALID(builtin);
- InitDartInternal(builtin);
- InitDartCore(builtin);
- InitDartAsync(builtin);
+ InitDartInternal(builtin, isolate_type);
+ InitDartCore(builtin, isolate_type);
+ InitDartAsync(builtin, isolate_type);
}
// Implementation of native functions which are used for some
@@ -163,6 +184,7 @@
if (LogIfError(closure) || !Dart_IsClosure(closure))
return;
DartState* dart_state = DartState::Current();
+ CHECK(dart_state);
Microtask::enqueueMicrotask(base::Bind(&ExecuteMicrotask,
dart_state->GetWeakPtr(), DartValue::Create(dart_state, closure)));
}
@@ -182,6 +204,7 @@
DART_CHECK_VALID(Dart_GetNativeBooleanArgument(args, 2, &repeating));
DOMDartState* state = DOMDartState::Current();
+ CHECK(state);
int timer_id = DOMTimer::install(state->document(),
ScheduledAction::Create(state, closure),
milliseconds,
@@ -194,6 +217,7 @@
DART_CHECK_VALID(Dart_GetNativeIntegerArgument(args, 0, &timer_id));
DOMDartState* state = DOMDartState::Current();
+ CHECK(state);
DOMTimer::removeByID(state->document(), timer_id);
}
diff --git a/sky/engine/bindings/builtin_natives.h b/sky/engine/bindings/builtin_natives.h
index a08fc19..3f525bc 100644
--- a/sky/engine/bindings/builtin_natives.h
+++ b/sky/engine/bindings/builtin_natives.h
@@ -12,12 +12,17 @@
class BuiltinNatives {
public:
+ enum IsolateType {
+ MainIsolate,
+ DartIOIsolate,
+ };
+
static Dart_NativeFunction NativeLookup(Dart_Handle name,
int argument_count,
bool* auto_setup_scope);
static const uint8_t* NativeSymbol(Dart_NativeFunction native_function);
- static void Init();
+ static void Init(IsolateType isolate_type);
private:
DISALLOW_IMPLICIT_CONSTRUCTORS(BuiltinNatives);
diff --git a/sky/engine/bindings/snapshot.dart b/sky/engine/bindings/snapshot.dart
index 51fd6f4..d620521 100644
--- a/sky/engine/bindings/snapshot.dart
+++ b/sky/engine/bindings/snapshot.dart
@@ -6,6 +6,7 @@
import 'dart:collection';
import 'dart:convert';
import 'dart:core';
+import 'dart:io';
import 'dart:isolate';
import 'dart:math';
import 'dart:mojo.internal';
diff --git a/sky/engine/core/BUILD.gn b/sky/engine/core/BUILD.gn
index d01e064..661835c 100644
--- a/sky/engine/core/BUILD.gn
+++ b/sky/engine/core/BUILD.gn
@@ -4,6 +4,7 @@
import("//sky/engine/build/scripts/scripts.gni")
import("//sky/engine/core/core.gni")
+import("//mojo/dart/embedder/embedder.gni")
visibility = [ "//sky/engine/*" ]
@@ -52,20 +53,40 @@
]
}
+dart_embedder_resources("generate_sky_embedder_service_isolate_resources_cc") {
+ deps = [
+ "//mojo/dart/embedder:deploy_observatory",
+ ]
+ inputs = [
+ "//sky/engine/core/script/dart_service_isolate/loader.dart",
+ "//sky/engine/core/script/dart_service_isolate/main.dart",
+ "//sky/engine/core/script/dart_service_isolate/resources.dart",
+ "//sky/engine/core/script/dart_service_isolate/server.dart",
+ ]
+ root_prefix = "//sky/engine/core/script/"
+ input_directory = "$root_out_dir/observatory/deployed/web/"
+ output = "$target_gen_dir/sky_embedder_service_isolate_resources.cc"
+ table_name = "sky_embedder_service_isolate"
+}
+
static_library("core") {
output_name = "sky_core"
deps = [
":core_generated",
+ ":generate_sky_embedder_service_isolate_resources_cc",
":libraries",
":prerequisites",
"//sky/engine/platform",
"//sky/engine/bindings",
+ "//dart/runtime/bin:embedded_dart_io",
"//dart/runtime/bin:libdart_withcore",
]
sources = sky_core_files
+ sources += [ "$target_gen_dir/sky_embedder_service_isolate_resources.cc" ]
+
include_dirs = [
# Needed for dart_mirrors_api.h in dart_controller.cc
rebase_path("//dart/runtime"),
diff --git a/sky/engine/core/core.gni b/sky/engine/core/core.gni
index 5ecbe0a..2a78c74 100644
--- a/sky/engine/core/core.gni
+++ b/sky/engine/core/core.gni
@@ -1009,12 +1009,17 @@
"rendering/VerticalPositionCache.h",
"script/dart_controller.cc",
"script/dart_controller.h",
+ "script/dart_debugger.cc",
+ "script/dart_debugger.h",
"script/dart_dependency_catcher.cc",
"script/dart_dependency_catcher.h",
"script/dart_loader.cc",
"script/dart_loader.h",
+ "script/dart_service_isolate.cc",
+ "script/dart_service_isolate.h",
"script/dom_dart_state.cc",
"script/dom_dart_state.h",
+ "script/monitor.h",
]
core_idl_files = get_path_info([
diff --git a/sky/engine/core/script/dart_controller.cc b/sky/engine/core/script/dart_controller.cc
index dc7ce4c..e03cb34 100644
--- a/sky/engine/core/script/dart_controller.cc
+++ b/sky/engine/core/script/dart_controller.cc
@@ -9,6 +9,7 @@
#include "base/logging.h"
#include "base/single_thread_task_runner.h"
#include "base/trace_event/trace_event.h"
+#include "dart/runtime/bin/embedded_dart_io.h"
#include "dart/runtime/include/dart_mirrors_api.h"
#include "sky/engine/bindings/builtin.h"
#include "sky/engine/bindings/builtin_natives.h"
@@ -21,8 +22,10 @@
#include "sky/engine/core/html/imports/HTMLImport.h"
#include "sky/engine/core/html/imports/HTMLImportChild.h"
#include "sky/engine/core/loader/FrameLoaderClient.h"
+#include "sky/engine/core/script/dart_debugger.h"
#include "sky/engine/core/script/dart_dependency_catcher.h"
#include "sky/engine/core/script/dart_loader.h"
+#include "sky/engine/core/script/dart_service_isolate.h"
#include "sky/engine/core/script/dom_dart_state.h"
#include "sky/engine/public/platform/Platform.h"
#include "sky/engine/tonic/dart_api_scope.h"
@@ -176,9 +179,36 @@
String(url_name) == DART_VM_SERVICE_ISOLATE_NAME;
}
+static void EnsureHandleWatcherStarted() {
+ static bool handle_watcher_started = false;
+ if (handle_watcher_started)
+ return;
+
+ // TODO(dart): Call Dart_Cleanup (ensure the handle watcher isolate is closed)
+ // during shutdown.
+ Dart_Handle mojo_core_lib =
+ Builtin::LoadAndCheckLibrary(Builtin::kMojoInternalLibrary);
+ CHECK(!LogIfError((mojo_core_lib)));
+ Dart_Handle handle_watcher_type = Dart_GetType(
+ mojo_core_lib,
+ Dart_NewStringFromCString("MojoHandleWatcher"),
+ 0,
+ nullptr);
+ CHECK(!LogIfError(handle_watcher_type));
+ CHECK(!LogIfError(Dart_Invoke(
+ handle_watcher_type,
+ Dart_NewStringFromCString("_start"),
+ 0,
+ nullptr)));
+
+ // RunLoop until the handle watcher isolate is spun-up.
+ CHECK(!LogIfError(Dart_RunLoop()));
+ handle_watcher_started = true;
+}
+
// TODO(rafaelw): Right now this only supports the creation of the handle
-// watcher isolate. Presumably, we'll want application isolates to spawn their
-// own isolates.
+// watcher isolate and the service isolate. Presumably, we'll want application
+// isolates to spawn their own isolates.
static Dart_Isolate IsolateCreateCallback(const char* script_uri,
const char* main,
const char* package_root,
@@ -186,8 +216,34 @@
char** error) {
if (IsServiceIsolateURL(script_uri)) {
- return Dart_CreateIsolate(script_uri, "main", kDartIsolateSnapshotBuffer,
- nullptr, error);
+ CHECK(kDartIsolateSnapshotBuffer);
+ DartState* dart_state = new DartState();
+ Dart_Isolate isolate = Dart_CreateIsolate(script_uri,
+ "main",
+ kDartIsolateSnapshotBuffer,
+ nullptr,
+ error);
+ CHECK(isolate) << error;
+ dart_state->set_isolate(isolate);
+ CHECK(Dart_IsServiceIsolate(isolate));
+ CHECK(!LogIfError(Dart_SetLibraryTagHandler(LibraryTagHandler)));
+ {
+ DartApiScope apiScope;
+ Builtin::SetNativeResolver(Builtin::kBuiltinLibrary);
+ Builtin::SetNativeResolver(Builtin::kMojoInternalLibrary);
+ Builtin::SetNativeResolver(Builtin::kIOLibrary);
+ BuiltinNatives::Init(BuiltinNatives::DartIOIsolate);
+ // Start the handle watcher from the service isolate so it isn't available
+ // for debugging or general Observatory interaction.
+ EnsureHandleWatcherStarted();
+ std::string ip = "127.0.0.1";
+ const intptr_t port = 0; // Automatic port assignment.
+ const bool service_isolate_booted =
+ DartServiceIsolate::Startup(ip, port, LibraryTagHandler, error);
+ CHECK(service_isolate_booted) << error;
+ }
+ Dart_ExitIsolate();
+ return isolate;
}
// Create & start the handle watcher isolate
@@ -204,6 +260,7 @@
DartApiScope apiScope;
Builtin::SetNativeResolver(Builtin::kBuiltinLibrary);
Builtin::SetNativeResolver(Builtin::kMojoInternalLibrary);
+ Builtin::SetNativeResolver(Builtin::kIOLibrary);
// Ensure the isolate has a root library.
Dart_LoadScript(Dart_NewStringFromCString("dart:empty"),
@@ -233,33 +290,6 @@
base::Bind(&CallHandleMessage, DartState::From(dest_isolate)->GetWeakPtr()));
}
-static void EnsureHandleWatcherStarted() {
- static bool handle_watcher_started = false;
- if (handle_watcher_started)
- return;
-
- // TODO(dart): Call Dart_Cleanup (ensure the handle watcher isolate is closed)
- // during shutdown.
- Dart_Handle mojo_core_lib =
- Builtin::LoadAndCheckLibrary(Builtin::kMojoInternalLibrary);
- CHECK(!LogIfError((mojo_core_lib)));
- Dart_Handle handle_watcher_type = Dart_GetType(
- mojo_core_lib,
- Dart_NewStringFromCString("MojoHandleWatcher"),
- 0,
- nullptr);
- CHECK(!LogIfError(handle_watcher_type));
- CHECK(!LogIfError(Dart_Invoke(
- handle_watcher_type,
- Dart_NewStringFromCString("_start"),
- 0,
- nullptr)));
-
- // RunLoop until the handle watcher isolate is spun-up.
- CHECK(!LogIfError(Dart_RunLoop()));
- handle_watcher_started = true;
-}
-
void DartController::CreateIsolateFor(Document* document) {
DCHECK(document);
CHECK(kDartIsolateSnapshotBuffer);
@@ -283,7 +313,8 @@
Builtin::SetNativeResolver(Builtin::kBuiltinLibrary);
Builtin::SetNativeResolver(Builtin::kMojoInternalLibrary);
- BuiltinNatives::Init();
+ Builtin::SetNativeResolver(Builtin::kIOLibrary);
+ BuiltinNatives::Init(BuiltinNatives::MainIsolate);
builtin_sky_ = adoptPtr(new BuiltinSky(dart_state()));
dart_state()->class_library().set_provider(builtin_sky_.get());
@@ -312,13 +343,20 @@
argv = kCheckedModeArgs;
#endif
+ dart::bin::BootstrapDartIo();
+
CHECK(Dart_SetVMFlags(argc, argv));
+ // This should be called before calling Dart_Initialize.
+ DartDebugger::InitDebugger();
CHECK(Dart_Initialize(kDartVmIsolateSnapshotBuffer,
IsolateCreateCallback,
nullptr, // Isolate interrupt callback.
UnhandledExceptionCallback, IsolateShutdownCallback,
// File IO callbacks.
nullptr, nullptr, nullptr, nullptr, nullptr));
+ // Wait for load port- ensures handle watcher and service isolates are
+ // running.
+ Dart_ServiceWaitForLoadPort();
}
} // namespace blink
diff --git a/sky/engine/core/script/dart_debugger.cc b/sky/engine/core/script/dart_debugger.cc
new file mode 100644
index 0000000..f7c1c49
--- /dev/null
+++ b/sky/engine/core/script/dart_debugger.cc
@@ -0,0 +1,138 @@
+// Copyright 2015 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 "sky/engine/core/script/dart_debugger.h"
+
+#include "dart/runtime/include/dart_api.h"
+#include "dart/runtime/include/dart_debugger_api.h"
+#include "dart/runtime/include/dart_native_api.h"
+
+
+namespace blink {
+
+void DartDebuggerIsolate::MessageLoop() {
+ MonitorLocker ml(&monitor_);
+ // Request notification on isolate messages. This allows us to
+ // respond to vm service messages while at breakpoint.
+ Dart_SetMessageNotifyCallback(DartDebugger::NotifyIsolate);
+ while (true) {
+ // Handle all available vm service messages, up to a resume
+ // request.
+ bool resume = false;
+ while (!resume && Dart_HasServiceMessages()) {
+ monitor_.Exit();
+ resume = Dart_HandleServiceMessages();
+ monitor_.Enter();
+ }
+ if (resume) {
+ break;
+ }
+ ml.Wait();
+ }
+ Dart_SetMessageNotifyCallback(nullptr);
+}
+
+void DartDebugger::BptResolvedHandler(Dart_IsolateId isolate_id,
+ intptr_t bp_id,
+ const Dart_CodeLocation& location) {
+ // Nothing to do here. Service event is dispatched to let Observatory know
+ // that a breakpoint was resolved.
+}
+
+void DartDebugger::PausedEventHandler(Dart_IsolateId isolate_id,
+ intptr_t bp_id,
+ const Dart_CodeLocation& loc) {
+ Dart_EnterScope();
+ intptr_t isolate_index = FindIsolateIndexById(isolate_id);
+ CHECK(isolate_index != -1);
+ (*isolates_)[isolate_index]->MessageLoop();
+ Dart_ExitScope();
+}
+
+void DartDebugger::ExceptionThrownHandler(Dart_IsolateId isolate_id,
+ Dart_Handle exception,
+ Dart_StackTrace stack_trace) {
+ Dart_EnterScope();
+ intptr_t isolate_index = FindIsolateIndexById(isolate_id);
+ CHECK(isolate_index != -1);
+ (*isolates_)[isolate_index]->MessageLoop();
+ Dart_ExitScope();
+}
+
+void DartDebugger::IsolateEventHandler(Dart_IsolateId isolate_id,
+ Dart_IsolateEvent kind) {
+ Dart_EnterScope();
+ if (kind == Dart_IsolateEvent::kCreated) {
+ AddIsolate(isolate_id);
+ } else {
+ intptr_t isolate_index = FindIsolateIndexById(isolate_id);
+ CHECK(isolate_index != -1);
+ if (kind == Dart_IsolateEvent::kInterrupted) {
+ (*isolates_)[isolate_index]->MessageLoop();
+ } else {
+ CHECK(kind == Dart_IsolateEvent::kShutdown);
+ RemoveIsolate(isolate_id);
+ }
+ }
+ Dart_ExitScope();
+}
+
+void DartDebugger::NotifyIsolate(Dart_Isolate isolate) {
+ base::AutoLock al(*lock_);
+ Dart_IsolateId isolate_id = Dart_GetIsolateId(isolate);
+ intptr_t isolate_index = FindIsolateIndexByIdLocked(isolate_id);
+ if (isolate_index >= 0) {
+ (*isolates_)[isolate_index]->Notify();
+ }
+}
+
+void DartDebugger::InitDebugger() {
+ Dart_SetIsolateEventHandler(IsolateEventHandler);
+ Dart_SetPausedEventHandler(PausedEventHandler);
+ Dart_SetBreakpointResolvedHandler(BptResolvedHandler);
+ Dart_SetExceptionThrownHandler(ExceptionThrownHandler);
+ lock_ = new base::Lock();
+ isolates_ = new std::vector<std::unique_ptr<DartDebuggerIsolate>>();
+}
+
+intptr_t DartDebugger::FindIsolateIndexById(Dart_IsolateId id) {
+ base::AutoLock al(*lock_);
+ return FindIsolateIndexByIdLocked(id);
+}
+
+intptr_t DartDebugger::FindIsolateIndexByIdLocked(
+ Dart_IsolateId id) {
+ lock_->AssertAcquired();
+ for (size_t i = 0; i < isolates_->size(); i++) {
+ if ((*isolates_)[i]->id() == id) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+void DartDebugger::AddIsolate(Dart_IsolateId id) {
+ base::AutoLock al(*lock_);
+ CHECK(FindIsolateIndexByIdLocked(id) == -1);
+ std::unique_ptr<DartDebuggerIsolate> debugger_isolate =
+ std::unique_ptr<DartDebuggerIsolate>(new DartDebuggerIsolate(id));
+ isolates_->push_back(std::move(debugger_isolate));
+}
+
+void DartDebugger::RemoveIsolate(Dart_IsolateId id) {
+ base::AutoLock al(*lock_);
+ for (size_t i = 0; i < isolates_->size(); i++) {
+ if (id == (*isolates_)[i]->id()) {
+ isolates_->erase(isolates_->begin() + i);
+ return;
+ }
+ }
+ NOTREACHED();
+}
+
+base::Lock* DartDebugger::lock_ = nullptr;
+std::vector<std::unique_ptr<DartDebuggerIsolate>>* DartDebugger::isolates_ =
+ nullptr;
+
+} // namespace blink
diff --git a/sky/engine/core/script/dart_debugger.h b/sky/engine/core/script/dart_debugger.h
new file mode 100644
index 0000000..8c93b6c
--- /dev/null
+++ b/sky/engine/core/script/dart_debugger.h
@@ -0,0 +1,81 @@
+// Copyright 2015 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.
+
+#ifndef SKY_ENGINE_CORE_SCRIPT_DART_DEBUGGER_H_
+#define SKY_ENGINE_CORE_SCRIPT_DART_DEBUGGER_H_
+
+#include <memory>
+#include <vector>
+
+#include "dart/runtime/include/dart_api.h"
+#include "dart/runtime/include/dart_debugger_api.h"
+#include "dart/runtime/include/dart_native_api.h"
+#include "sky/engine/core/script/monitor.h"
+
+namespace base {
+ class Lock;
+}
+
+namespace blink {
+
+class DartDebuggerIsolate {
+ public:
+ DartDebuggerIsolate(Dart_IsolateId id)
+ : id_(id) {
+ }
+
+ Dart_IsolateId id() const {
+ return id_;
+ }
+
+ void Notify() {
+ monitor_.Notify();
+ }
+
+ void MessageLoop();
+
+ private:
+ const Dart_IsolateId id_;
+ Monitor monitor_;
+};
+
+class DartDebugger {
+ public:
+ static void InitDebugger();
+
+ private:
+ static void BptResolvedHandler(Dart_IsolateId isolate_id,
+ intptr_t bp_id,
+ const Dart_CodeLocation& location);
+
+ static void PausedEventHandler(Dart_IsolateId isolate_id,
+ intptr_t bp_id,
+ const Dart_CodeLocation& loc);
+
+ static void ExceptionThrownHandler(Dart_IsolateId isolate_id,
+ Dart_Handle exception,
+ Dart_StackTrace stack_trace);
+
+ static void IsolateEventHandler(Dart_IsolateId isolate_id,
+ Dart_IsolateEvent kind);
+
+ static void NotifyIsolate(Dart_Isolate isolate);
+
+ static intptr_t FindIsolateIndexById(Dart_IsolateId id);
+
+ static intptr_t FindIsolateIndexByIdLocked(Dart_IsolateId id);
+
+ static void AddIsolate(Dart_IsolateId id);
+
+ static void RemoveIsolate(Dart_IsolateId id);
+
+ static base::Lock* lock_;
+ static std::vector<std::unique_ptr<DartDebuggerIsolate>>* isolates_;
+
+ friend class DartDebuggerIsolate;
+};
+
+} // namespace blink
+
+#endif // SKY_ENGINE_CORE_SCRIPT_DART_DEBUGGER_H_
\ No newline at end of file
diff --git a/sky/engine/core/script/dart_service_isolate.cc b/sky/engine/core/script/dart_service_isolate.cc
new file mode 100644
index 0000000..d1bf545
--- /dev/null
+++ b/sky/engine/core/script/dart_service_isolate.cc
@@ -0,0 +1,304 @@
+// Copyright 2015 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 "dart_service_isolate.h"
+
+#include "base/logging.h"
+#include "dart/runtime/include/dart_api.h"
+#include "sky/engine/tonic/dart_error.h"
+#include "sky/engine/tonic/dart_string.h"
+
+#define RETURN_ERROR_HANDLE(handle) \
+ if (Dart_IsError(handle)) { \
+ return handle; \
+ }
+
+#define SHUTDOWN_ON_ERROR(handle) \
+ if (Dart_IsError(handle)) { \
+ *error = strdup(Dart_GetError(handle)); \
+ Dart_ExitScope(); \
+ Dart_ShutdownIsolate(); \
+ return false; \
+ }
+
+#define kLibrarySourceNamePrefix "/dart_service_isolate"
+static const char* kServiceIsolateScript = "main.dart";
+
+struct ResourcesEntry {
+ const char* path_;
+ const char* resource_;
+ int length_;
+};
+
+namespace mojo {
+ namespace dart {
+ extern ResourcesEntry __sky_embedder_service_isolate_resources_[];
+ }
+}
+
+namespace blink {
+
+class Resources {
+ public:
+ static const int kNoSuchInstance = -1;
+ static int ResourceLookup(const char* path, const char** resource) {
+ ResourcesEntry* table = ResourcesTable();
+ for (int i = 0; table[i].path_ != NULL; i++) {
+ const ResourcesEntry& entry = table[i];
+ if (strcmp(path, entry.path_) == 0) {
+ *resource = entry.resource_;
+ DCHECK(entry.length_ > 0);
+ return entry.length_;
+ }
+ }
+ return kNoSuchInstance;
+ }
+
+ static const char* Path(int idx) {
+ DCHECK(idx >= 0);
+ ResourcesEntry* entry = At(idx);
+ if (entry == NULL) {
+ return NULL;
+ }
+ DCHECK(entry->path_ != NULL);
+ return entry->path_;
+ }
+
+ private:
+ static ResourcesEntry* At(int idx) {
+ DCHECK(idx >= 0);
+ ResourcesEntry* table = ResourcesTable();
+ for (int i = 0; table[i].path_ != NULL; i++) {
+ if (idx == i) {
+ return &table[i];
+ }
+ }
+ return NULL;
+ }
+ static ResourcesEntry* ResourcesTable() {
+ return &mojo::dart::__sky_embedder_service_isolate_resources_[0];
+ }
+};
+
+void DartServiceIsolate::TriggerResourceLoad(Dart_NativeArguments args) {
+ Dart_Handle library = Dart_RootLibrary();
+ DCHECK(!Dart_IsError(library));
+ Dart_Handle result = LoadResources(library);
+ DCHECK(!Dart_IsError(result));
+}
+
+void DartServiceIsolate::NotifyServerState(Dart_NativeArguments args) {
+ // NO-OP.
+}
+
+void DartServiceIsolate::Shutdown(Dart_NativeArguments args) {
+ // NO-OP.
+}
+
+DartBuiltin::Natives DartServiceIsolate::native_entries_[] = {
+ {"ServiceIsolate_TriggerResourceLoad", TriggerResourceLoad, 0 },
+ {"ServiceIsolate_NotifyServerState", NotifyServerState, 2 },
+ {"ServiceIsolate_Shutdown", Shutdown, 0 },
+};
+
+Dart_NativeFunction DartServiceIsolate::NativeResolver(Dart_Handle name,
+ int argument_count,
+ bool* auto_setup_scope) {
+ CHECK(builtins_);
+ return builtins_->Resolver(name, argument_count, auto_setup_scope);
+}
+
+const uint8_t* DartServiceIsolate::NativeSymbolizer(
+ Dart_NativeFunction native_function) {
+ CHECK(builtins_);
+ return builtins_->Symbolizer(native_function);
+}
+
+Dart_LibraryTagHandler DartServiceIsolate::embedder_tag_handler_ = nullptr;
+DartBuiltin* DartServiceIsolate::builtins_ = nullptr;
+
+bool DartServiceIsolate::Startup(std::string server_ip,
+ intptr_t server_port,
+ Dart_LibraryTagHandler embedder_tag_handler,
+ char** error) {
+ Dart_Isolate isolate = Dart_CurrentIsolate();
+ CHECK(isolate);
+
+ // Remember the embedder's library tag handler.
+ embedder_tag_handler_ = embedder_tag_handler;
+ CHECK(embedder_tag_handler_);
+
+ // Setup native entries.
+ builtins_ =
+ new DartBuiltin(&DartServiceIsolate::native_entries_[0],
+ arraysize(native_entries_));
+
+ Dart_Handle result;
+
+ // Use our own library tag handler when loading service isolate sources.
+ Dart_SetLibraryTagHandler(DartServiceIsolate::LibraryTagHandler);
+ // Load main script.
+ Dart_Handle library = LoadScript(kServiceIsolateScript);
+ DCHECK(library != Dart_Null());
+ SHUTDOWN_ON_ERROR(library);
+ // Setup native entry resolution.
+ result = Dart_SetNativeResolver(library, NativeResolver, NativeSymbolizer);
+
+ SHUTDOWN_ON_ERROR(result);
+ // Finalize loading.
+ result = Dart_FinalizeLoading(false);
+ SHUTDOWN_ON_ERROR(result);
+
+ // Make runnable.
+ Dart_ExitScope();
+ Dart_ExitIsolate();
+ bool retval = Dart_IsolateMakeRunnable(isolate);
+ if (!retval) {
+ Dart_EnterIsolate(isolate);
+ Dart_ShutdownIsolate();
+ *error = strdup("Invalid isolate state - Unable to make it runnable.");
+ return false;
+ }
+ Dart_EnterIsolate(isolate);
+ Dart_EnterScope();
+
+ library = Dart_RootLibrary();
+ SHUTDOWN_ON_ERROR(library);
+
+ // Set the HTTP server's ip.
+ result = Dart_SetField(library,
+ Dart_NewStringFromCString("_ip"),
+ Dart_NewStringFromCString(server_ip.c_str()));
+ SHUTDOWN_ON_ERROR(result);
+ // If we have a port specified, start the server immediately.
+ bool auto_start = server_port >= 0;
+ if (server_port < 0) {
+ // Adjust server_port to port 0 which will result in the first available
+ // port when the HTTP server is started.
+ server_port = 0;
+ }
+ // Set the HTTP's servers port.
+ result = Dart_SetField(library,
+ Dart_NewStringFromCString("_port"),
+ Dart_NewInteger(server_port));
+ SHUTDOWN_ON_ERROR(result);
+ result = Dart_SetField(library,
+ Dart_NewStringFromCString("_autoStart"),
+ Dart_NewBoolean(auto_start));
+ SHUTDOWN_ON_ERROR(result);
+ return true;
+}
+
+Dart_Handle DartServiceIsolate::GetSource(const char* name) {
+ const intptr_t kBufferSize = 512;
+ char buffer[kBufferSize];
+ snprintf(&buffer[0], kBufferSize-1, "%s/%s", kLibrarySourceNamePrefix, name);
+ const char* vmservice_source = NULL;
+ int r = Resources::ResourceLookup(buffer, &vmservice_source);
+ DCHECK(r != Resources::kNoSuchInstance);
+ return Dart_NewStringFromCString(vmservice_source);
+}
+
+Dart_Handle DartServiceIsolate::LoadScript(const char* name) {
+ Dart_Handle url = Dart_NewStringFromCString("dart:vmservice_sky");
+ Dart_Handle source = GetSource(name);
+ return Dart_LoadScript(url, source, 0, 0);
+}
+
+Dart_Handle DartServiceIsolate::LoadSource(Dart_Handle library, const char* name) {
+ Dart_Handle url = Dart_NewStringFromCString(name);
+ Dart_Handle source = GetSource(name);
+ return Dart_LoadSource(library, url, source, 0, 0);
+}
+
+Dart_Handle DartServiceIsolate::LoadResource(Dart_Handle library,
+ const char* resource_name) {
+ // Prepare for invoke call.
+ Dart_Handle name = Dart_NewStringFromCString(resource_name);
+ RETURN_ERROR_HANDLE(name);
+ const char* data_buffer = NULL;
+ int data_buffer_length = Resources::ResourceLookup(resource_name,
+ &data_buffer);
+ DCHECK(data_buffer_length != Resources::kNoSuchInstance);
+ Dart_Handle data_list = Dart_NewTypedData(Dart_TypedData_kUint8,
+ data_buffer_length);
+ RETURN_ERROR_HANDLE(data_list);
+ Dart_TypedData_Type type = Dart_TypedData_kInvalid;
+ void* data_list_buffer = NULL;
+ intptr_t data_list_buffer_length = 0;
+ Dart_Handle result = Dart_TypedDataAcquireData(data_list, &type,
+ &data_list_buffer,
+ &data_list_buffer_length);
+ RETURN_ERROR_HANDLE(result);
+ DCHECK(data_buffer_length == data_list_buffer_length);
+ DCHECK(data_list_buffer != NULL);
+ DCHECK(type = Dart_TypedData_kUint8);
+ memmove(data_list_buffer, &data_buffer[0], data_buffer_length);
+ result = Dart_TypedDataReleaseData(data_list);
+ RETURN_ERROR_HANDLE(result);
+
+ // Make invoke call.
+ const intptr_t kNumArgs = 2;
+ Dart_Handle args[kNumArgs] = { name, data_list };
+ result = Dart_Invoke(library, Dart_NewStringFromCString("_addResource"),
+ kNumArgs, args);
+ return result;
+}
+
+Dart_Handle DartServiceIsolate::LoadResources(Dart_Handle library) {
+ Dart_Handle result = Dart_Null();
+ intptr_t prefixLen = strlen(kLibrarySourceNamePrefix);
+ for (intptr_t i = 0; Resources::Path(i) != NULL; i++) {
+ const char* path = Resources::Path(i);
+ // If it doesn't begin with kLibrarySourceNamePrefix it is a frontend
+ // resource.
+ if (strncmp(path, kLibrarySourceNamePrefix, prefixLen) != 0) {
+ result = LoadResource(library, path);
+ if (Dart_IsError(result)) {
+ break;
+ }
+ }
+ }
+ return result;
+}
+
+Dart_Handle DartServiceIsolate::LibraryTagHandler(Dart_LibraryTag tag,
+ Dart_Handle library,
+ Dart_Handle url) {
+ if (!Dart_IsLibrary(library)) {
+ return Dart_NewApiError("not a library");
+ }
+ if (!Dart_IsString(url)) {
+ return Dart_NewApiError("url is not a string");
+ }
+ const char* url_string = NULL;
+ Dart_Handle result = Dart_StringToCString(url, &url_string);
+ if (Dart_IsError(result)) {
+ return result;
+ }
+ Dart_Handle library_url = Dart_LibraryUrl(library);
+ const char* library_url_string = NULL;
+ result = Dart_StringToCString(library_url, &library_url_string);
+ if (Dart_IsError(result)) {
+ return result;
+ }
+ if (tag == Dart_kImportTag) {
+ // Embedder handles all requests for external libraries.
+ return embedder_tag_handler_(tag, library, url);
+ }
+ DCHECK((tag == Dart_kSourceTag) || (tag == Dart_kCanonicalizeUrl));
+ if (tag == Dart_kCanonicalizeUrl) {
+ // url is already canonicalized.
+ return url;
+ }
+ // Get source from builtin resources.
+ Dart_Handle source = GetSource(url_string);
+ if (Dart_IsError(source)) {
+ return source;
+ }
+ return Dart_LoadSource(library, url, source, 0, 0);
+}
+
+
+} // namespace blink
diff --git a/sky/engine/core/script/dart_service_isolate.h b/sky/engine/core/script/dart_service_isolate.h
new file mode 100644
index 0000000..450ad16
--- /dev/null
+++ b/sky/engine/core/script/dart_service_isolate.h
@@ -0,0 +1,55 @@
+// Copyright 2015 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.
+
+#ifndef SKY_ENGINE_CORE_SCRIPT_DART_SERVICE_ISOLATE_H_
+#define SKY_ENGINE_CORE_SCRIPT_DART_SERVICE_ISOLATE_H_
+
+#include <string>
+
+#include "include/dart_api.h"
+#include "sky/engine/config.h"
+#include "sky/engine/tonic/dart_builtin.h"
+
+namespace blink {
+
+class DartServiceIsolate {
+ public:
+ static bool Bootstrap();
+
+ static bool Startup(std::string server_ip,
+ intptr_t server_port,
+ Dart_LibraryTagHandler embedder_tag_handler,
+ char** error);
+
+ private:
+ // Native entries.
+ static void TriggerResourceLoad(Dart_NativeArguments args);
+ static void NotifyServerState(Dart_NativeArguments args);
+ static void Shutdown(Dart_NativeArguments args);
+ // Native entry resolution.
+ static Dart_NativeFunction NativeResolver(Dart_Handle name,
+ int argument_count,
+ bool* auto_setup_scope);
+ static const uint8_t* NativeSymbolizer(Dart_NativeFunction native_function);
+ static DartBuiltin::Natives native_entries_[];
+ static DartBuiltin* builtins_;
+
+ // Script loading.
+ static Dart_Handle GetSource(const char* name);
+ static Dart_Handle LoadScript(const char* name);
+ static Dart_Handle LoadSource(Dart_Handle library, const char* name);
+ static Dart_Handle LibraryTagHandler(Dart_LibraryTag tag, Dart_Handle library,
+ Dart_Handle url);
+
+ // Observatory resource loading.
+ static Dart_Handle LoadResources(Dart_Handle library);
+ static Dart_Handle LoadResource(Dart_Handle library, const char* name);
+
+ static Dart_LibraryTagHandler embedder_tag_handler_;
+};
+
+
+} // namespace blink
+
+#endif // SKY_ENGINE_CORE_SCRIPT_DART_SERVICE_ISOLATE_H_
diff --git a/sky/engine/core/script/dart_service_isolate/loader.dart b/sky/engine/core/script/dart_service_isolate/loader.dart
new file mode 100644
index 0000000..a8d0d94
--- /dev/null
+++ b/sky/engine/core/script/dart_service_isolate/loader.dart
@@ -0,0 +1,11 @@
+// Copyright 2015 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.
+
+part of dart_controller_service_isolate;
+
+_processLoadRequest(request) {
+ var sp = request[0];
+ var uri = Uri.parse(request[1]);
+ sp.send('Service isolate loading not supported by embedder (uri = $uri).');
+}
\ No newline at end of file
diff --git a/sky/engine/core/script/dart_service_isolate/main.dart b/sky/engine/core/script/dart_service_isolate/main.dart
new file mode 100644
index 0000000..1eb027d
--- /dev/null
+++ b/sky/engine/core/script/dart_service_isolate/main.dart
@@ -0,0 +1,60 @@
+// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+library sky_shell_dart_controller_service_isolate;
+
+import 'dart:async';
+import 'dart:convert';
+import 'dart:io';
+import 'dart:isolate';
+import 'dart:vmservice';
+
+part 'loader.dart';
+part 'resources.dart';
+part 'server.dart';
+
+// The TCP ip/port that the HTTP server listens on.
+int _port;
+String _ip;
+// Should the HTTP server auto start?
+bool _autoStart;
+
+// HTTP server.
+Server server;
+
+_onShutdown() {
+ if (server != null) {
+ server.close(true).catchError((e, st) {
+ print(e);
+ }).whenComplete(_shutdown);
+ } else {
+ _shutdown();
+ }
+}
+
+void _bootServer() {
+ // Load resources.
+ _triggerResourceLoad();
+ // Lazily create service.
+ var service = new VMService();
+ service.onShutdown = _onShutdown;
+ // Lazily create server.
+ server = new Server(service, _ip, _port);
+}
+
+main() {
+ if (_autoStart) {
+ _bootServer();
+ if (server != null) {
+ server.startup();
+ }
+ }
+ scriptLoadPort.handler = _processLoadRequest;
+ // It's just here to push an event on the event loop so that we invoke the
+ // scheduled microtasks.
+ Timer.run(() {});
+ return scriptLoadPort;
+}
+
+_shutdown() native "ServiceIsolate_Shutdown";
diff --git a/sky/engine/core/script/dart_service_isolate/resources.dart b/sky/engine/core/script/dart_service_isolate/resources.dart
new file mode 100644
index 0000000..11e347e
--- /dev/null
+++ b/sky/engine/core/script/dart_service_isolate/resources.dart
@@ -0,0 +1,50 @@
+// Copyright 2015 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.
+
+part of dart_controller_service_isolate;
+
+String detectMimeType(String name) {
+ var extensionStart = name.lastIndexOf('.');
+ var extension = name.substring(extensionStart+1);
+ switch (extension) {
+ case 'html':
+ return 'text/html; charset=UTF-8';
+ case 'dart':
+ return 'application/dart; charset=UTF-8';
+ case 'js':
+ return 'application/javascript; charset=UTF-8';
+ case 'css':
+ return 'text/css; charset=UTF-8';
+ case 'gif':
+ return 'image/gif';
+ case 'png':
+ return 'image/png';
+ case 'jpg':
+ return 'image/jpeg';
+ case 'jpeg':
+ return 'image/jpeg';
+ case 'svg':
+ return 'image/svg+xml';
+ default:
+ return 'text/plain';
+ }
+}
+
+
+class Resource {
+ final String name;
+ final String mimeType;
+ final List<int> data;
+ Resource(this.name, this.mimeType, this.data);
+ static final Map<String, Resource> resources = new Map<String, Resource>();
+}
+
+
+_addResource(String name, List<int> data) {
+ var mimeType = detectMimeType(name);
+ Resource resource = new Resource(name, mimeType, data);
+ Resource.resources[name] = resource;
+}
+
+_triggerResourceLoad() native "ServiceIsolate_TriggerResourceLoad";
diff --git a/sky/engine/core/script/dart_service_isolate/server.dart b/sky/engine/core/script/dart_service_isolate/server.dart
new file mode 100644
index 0000000..4e069f9
--- /dev/null
+++ b/sky/engine/core/script/dart_service_isolate/server.dart
@@ -0,0 +1,225 @@
+// Copyright 2015 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.
+
+part of dart_controller_service_isolate;
+
+class WebSocketClient extends Client {
+ static const int PARSE_ERROR_CODE = 4000;
+ static const int BINARY_MESSAGE_ERROR_CODE = 4001;
+ static const int NOT_MAP_ERROR_CODE = 4002;
+ final WebSocket socket;
+
+ WebSocketClient(this.socket, VMService service) : super(service) {
+ socket.listen((message) => onWebSocketMessage(message));
+ socket.done.then((_) => close());
+ }
+
+ onWebSocketMessage(message) {
+ if (message is String) {
+ var map;
+ try {
+ map = JSON.decode(message);
+ } catch (e) {
+ socket.close(PARSE_ERROR_CODE, 'Message parse error: $e');
+ return;
+ }
+ if (map is! Map) {
+ socket.close(NOT_MAP_ERROR_CODE, 'Message must be a JSON map.');
+ return;
+ }
+ var serial = map['id'];
+ onMessage(serial, new Message.fromJsonRpc(map));
+ } else {
+ socket.close(BINARY_MESSAGE_ERROR_CODE, 'Message must be a string.');
+ }
+ }
+
+ post(dynamic result) {
+ try {
+ socket.add(result);
+ } catch (_) {
+ print("Ignoring error posting over WebSocket.");
+ }
+ }
+
+ dynamic toJson() {
+ Map map = super.toJson();
+ map['type'] = 'WebSocketClient';
+ map['socket'] = '$socket';
+ return map;
+ }
+}
+
+
+class HttpRequestClient extends Client {
+ static ContentType jsonContentType =
+ new ContentType("application", "json", charset: "utf-8");
+ final HttpRequest request;
+
+ HttpRequestClient(this.request, VMService service) : super(service);
+
+ post(String result) {
+ request.response..headers.contentType = jsonContentType
+ ..write(result)
+ ..close();
+ close();
+ }
+
+ dynamic toJson() {
+ Map map = super.toJson();
+ map['type'] = 'HttpRequestClient';
+ map['request'] = '$request';
+ return map;
+ }
+}
+
+class Server {
+ static const WEBSOCKET_PATH = '/ws';
+ static const ROOT_REDIRECT_PATH = '/index.html';
+
+ final VMService _service;
+ final String _ip;
+ final int _port;
+
+ HttpServer _server;
+ bool get running => _server != null;
+ bool _displayMessages = false;
+
+ Server(this._service, this._ip, this._port) {
+ _displayMessages = (_ip != '127.0.0.1' || _port != 8181);
+ }
+
+ bool _shouldServeObservatory(HttpRequest request) {
+ if (request.headers['Observatory-Version'] != null) {
+ // Request is already coming from Observatory.
+ return false;
+ }
+ // TODO(johnmccutchan): Test with obscure browsers.
+ if (request.headers.value(HttpHeaders.USER_AGENT).contains('Mozilla')) {
+ // Request is coming from a browser but not Observatory application.
+ // Serve Observatory and let the Observatory make the real request.
+ return true;
+ }
+ // All other user agents are assumed to be textual.
+ return false;
+ }
+
+ _onServerShutdown() {
+ }
+
+ _serverError(error, stackTrace) {
+ _onServerShutdown();
+ }
+
+ _serverDone() {
+ _onServerShutdown();
+ }
+
+ _requestHandler(HttpRequest request) {
+ // Allow cross origin requests with 'observatory' header.
+ request.response.headers.add('Access-Control-Allow-Origin', '*');
+ request.response.headers.add('Access-Control-Allow-Headers',
+ 'Observatory-Version');
+
+ if (request.method != 'GET') {
+ // Not a GET request. Do nothing.
+ request.response.close();
+ return;
+ }
+
+ final String path =
+ request.uri.path == '/' ? ROOT_REDIRECT_PATH : request.uri.path;
+
+ if (path == WEBSOCKET_PATH) {
+ WebSocketTransformer.upgrade(request).then((WebSocket webSocket) {
+ new WebSocketClient(webSocket, _service);
+ });
+ return;
+ }
+
+ var resource = Resource.resources[path];
+ if (resource == null && _shouldServeObservatory(request)) {
+ resource = Resource.resources[ROOT_REDIRECT_PATH];
+ assert(resource != null);
+ }
+ if (resource != null) {
+ // Serving up a static resource (e.g. .css, .html, .png).
+ request.response.headers.contentType =
+ ContentType.parse(resource.mimeType);
+ request.response.add(resource.data);
+ request.response.close();
+ return;
+ }
+ var message = new Message.fromUri(request.uri);
+ var client = new HttpRequestClient(request, _service);
+ client.onMessage(null, message);
+ }
+
+ Future startup() {
+ if (_server != null) {
+ // Already running.
+ return new Future.value(this);
+ }
+
+ // Startup HTTP server.
+ var address = new InternetAddress('127.0.0.1');
+ return HttpServer.bind(address, _port).then((s) {
+ _server = s;
+ _server.listen(_requestHandler,
+ onError: _serverError,
+ onDone: _serverDone,
+ cancelOnError: true);
+ var ip = _server.address.address.toString();
+ if (_displayMessages) {
+ var port = _server.port.toString();
+ print('Observatory listening on http://$ip:$port');
+ }
+ // Server is up and running.
+ _notifyServerState(ip, _server.port);
+ return this;
+ }).catchError((e, st) {
+ print('Could not start Observatory HTTP server:\n$e\n$st\n');
+ _notifyServerState("", 0);
+ return this;
+ });
+ }
+
+ close(bool force) {
+ if (_server == null) {
+ return new Future.value(null);
+ }
+ return _server.close(force: force);
+ }
+
+ Future shutdown(bool forced) {
+ if (_server == null) {
+ // Not started.
+ return new Future.value(this);
+ }
+
+ // Force displaying of status messages if we are forcibly shutdown.
+ _displayMessages = _displayMessages || forced;
+
+ // Shutdown HTTP server and subscription.
+ var ip = _server.address.address.toString();
+ var port = _server.port.toString();
+ return close(forced).then((_) {
+ if (_displayMessages) {
+ print('Observatory no longer listening on http://$ip:$port');
+ }
+ _server = null;
+ _notifyServerState("", 0);
+ return this;
+ }).catchError((e, st) {
+ _server = null;
+ print('Could not shutdown Observatory HTTP server:\n$e\n$st\n');
+ _notifyServerState("", 0);
+ return this;
+ });
+ }
+
+}
+
+_notifyServerState(String ip, int port)
+ native "ServiceIsolate_NotifyServerState";
diff --git a/sky/engine/core/script/monitor.h b/sky/engine/core/script/monitor.h
new file mode 100644
index 0000000..af784ec
--- /dev/null
+++ b/sky/engine/core/script/monitor.h
@@ -0,0 +1,75 @@
+// Copyright 2015 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.
+
+#ifndef SKY_ENGINE_CORE_SCRIPT_MONITOR_H_
+#define SKY_ENGINE_CORE_SCRIPT_MONITOR_H_
+
+#include "base/synchronization/condition_variable.h"
+#include "base/synchronization/lock.h"
+
+namespace blink {
+
+class Monitor {
+ public:
+ Monitor() {
+ lock_ = new base::Lock();
+ condition_variable_ = new base::ConditionVariable(lock_);
+ }
+
+ ~Monitor() {
+ delete condition_variable_;
+ delete lock_;
+ }
+
+ void Enter() {
+ lock_->Acquire();
+ }
+
+ void Exit() {
+ lock_->Release();
+ }
+
+ void Notify() {
+ condition_variable_->Signal();
+ }
+
+ void Wait() {
+ condition_variable_->Wait();
+ }
+
+ private:
+ base::Lock* lock_;
+ base::ConditionVariable* condition_variable_;
+ DISALLOW_COPY_AND_ASSIGN(Monitor);
+};
+
+class MonitorLocker {
+ public:
+ explicit MonitorLocker(Monitor* monitor) : monitor_(monitor) {
+ CHECK(monitor_);
+ monitor_->Enter();
+ }
+
+ virtual ~MonitorLocker() {
+ monitor_->Exit();
+ }
+
+ void Wait() {
+ return monitor_->Wait();
+ }
+
+ void Notify() {
+ monitor_->Notify();
+ }
+
+ private:
+ Monitor* const monitor_;
+
+ DISALLOW_COPY_AND_ASSIGN(MonitorLocker);
+};
+
+} // namespace blink
+
+
+#endif // SKY_ENGINE_CORE_SCRIPT_MONITOR_H_
\ No newline at end of file
diff --git a/sky/examples/stocks/lib/stock_menu.dart b/sky/examples/stocks/lib/stock_menu.dart
index 9023ed2..03b0998 100644
--- a/sky/examples/stocks/lib/stock_menu.dart
+++ b/sky/examples/stocks/lib/stock_menu.dart
@@ -4,7 +4,7 @@
import 'package:sky/framework/components/popup_menu.dart';
import 'package:sky/framework/fn.dart';
-import 'package:sky/framework/theme/view-configuration.dart';
+import 'package:sky/framework/theme/view_configuration.dart';
class StockMenu extends Component {
static final Style _style = new Style('''
diff --git a/sky/framework/README.md b/sky/framework/README.md
index c937981..a15a276 100644
--- a/sky/framework/README.md
+++ b/sky/framework/README.md
@@ -78,7 +78,7 @@
Checkbox({ Object key, this.onChanged, this.checked }) : super(key: key);
```
-What this means is that the `Checkbox` component is *never* "owns" the state of
+What this means is that the `Checkbox` component *never* "owns" the state of
the checkbox. It's current state is handed into the `checked` parameter, and
when a click occurs, the checkbox invokes its `onChanged` callback with the
value it thinks it should be changed to -- but it never directly changes the
diff --git a/sky/framework/components/action_bar.dart b/sky/framework/components/action_bar.dart
index 0e34fba..e0b2ddf 100644
--- a/sky/framework/components/action_bar.dart
+++ b/sky/framework/components/action_bar.dart
@@ -3,7 +3,7 @@
// found in the LICENSE file.
import '../fn.dart';
-import '../theme/view-configuration.dart';
+import '../theme/view_configuration.dart';
import 'material.dart';
class ActionBar extends Component {
diff --git a/sky/framework/components/drawer_header.dart b/sky/framework/components/drawer_header.dart
index da00446..c1c1d52 100644
--- a/sky/framework/components/drawer_header.dart
+++ b/sky/framework/components/drawer_header.dart
@@ -4,7 +4,7 @@
import '../fn.dart';
import '../theme/colors.dart';
-import '../theme/view-configuration.dart';
+import '../theme/view_configuration.dart';
class DrawerHeader extends Component {
static final Style _style = new Style('''
diff --git a/sky/framework/components/fixed_height_scrollable.dart b/sky/framework/components/fixed_height_scrollable.dart
index 3f3e973..2838acb 100644
--- a/sky/framework/components/fixed_height_scrollable.dart
+++ b/sky/framework/components/fixed_height_scrollable.dart
@@ -6,7 +6,6 @@
import '../debug/tracing.dart';
import '../fn.dart';
import 'dart:math' as math;
-import 'dart:sky' as sky;
import 'dart:async';
import 'scrollable.dart';
@@ -52,14 +51,11 @@
var item = root.firstChild.firstChild;
if (item == null)
return;
- sky.ClientRect scrollRect = root.getBoundingClientRect();
- sky.ClientRect itemRect = item.getBoundingClientRect();
- assert(scrollRect.height > 0);
- assert(itemRect.height > 0);
-
setState(() {
- _height = scrollRect.height;
- _itemHeight = itemRect.height;
+ _height = root.height;
+ assert(_height > 0);
+ _itemHeight = item.height;
+ assert(_itemHeight > 0);
scrollBehavior.containerHeight = _height;
scrollBehavior.contentsHeight = _itemHeight * _itemCount;
});
@@ -82,7 +78,6 @@
'transform: translateY(${(-scrollOffset).toStringAsFixed(2)}px)';
} else {
drawCount = (_height / _itemHeight).round() + 1;
- double alignmentOffset = math.max(0.0, scrollOffset);
double alignmentDelta = -scrollOffset % _itemHeight;
if (alignmentDelta != 0.0)
alignmentDelta -= _itemHeight;
diff --git a/sky/framework/components/ink_splash.dart b/sky/framework/components/ink_splash.dart
index 0d147ad..3bd18e1 100644
--- a/sky/framework/components/ink_splash.dart
+++ b/sky/framework/components/ink_splash.dart
@@ -5,19 +5,19 @@
import '../animation/animated_value.dart';
import '../animation/curves.dart';
import '../fn.dart';
-import '../theme/view-configuration.dart' as config;
+import '../theme/view_configuration.dart' as config;
import 'dart:async';
import 'dart:math' as math;
-import 'dart:sky' as sky;
+import '../layout.dart';
const double _kSplashConfirmedDuration = 350.0;
const double _kSplashUnconfirmedDuration = config.kDefaultLongPressTimeout;
const double _kSplashAbortDuration = 100.0;
const double _kSplashInitialDelay = 0.0; // we could delay initially in case the user scrolls
-double _getSplashTargetSize(sky.ClientRect rect, double x, double y) {
- return 2.0 * math.max(math.max(x - rect.left, rect.right - x),
- math.max(y - rect.top, rect.bottom - y));
+double _getSplashTargetSize(Rect rect, double x, double y) {
+ return 2.0 * math.max(math.max(x - rect.x, rect.x + rect.width - x),
+ math.max(y - rect.y, rect.y + rect.height - y));
}
class SplashController {
@@ -56,10 +56,10 @@
_size.stop();
}
- SplashController(sky.ClientRect rect, double x, double y,
+ SplashController(Rect rect, double x, double y,
{ this.pointer, Function onDone })
- : _offsetX = x - rect.left,
- _offsetY = y - rect.top,
+ : _offsetX = x - rect.x,
+ _offsetY = y - rect.y,
_targetSize = _getSplashTargetSize(rect, x, y) {
_styleStream = _size.onValueChanged.map((p) {
diff --git a/sky/framework/components/ink_well.dart b/sky/framework/components/ink_well.dart
index 096fa00..cc7181b 100644
--- a/sky/framework/components/ink_well.dart
+++ b/sky/framework/components/ink_well.dart
@@ -45,14 +45,13 @@
);
}
- sky.ClientRect _getBoundingRect() => (getRoot() as sky.Element).getBoundingClientRect();
-
void _startSplash(sky.GestureEvent event) {
setState(() {
if (_splashes == null)
_splashes = new LinkedHashSet<SplashController>();
var splash;
- splash = new SplashController(_getBoundingRect(), event.x, event.y,
+ var root = getRoot();
+ splash = new SplashController(root.rect, event.x, event.y,
pointer: event.primaryPointer,
onDone: () { _splashDone(splash); });
_splashes.add(splash);
diff --git a/sky/framework/components/popup_menu.dart b/sky/framework/components/popup_menu.dart
index 4375eef..90e0994 100644
--- a/sky/framework/components/popup_menu.dart
+++ b/sky/framework/components/popup_menu.dart
@@ -92,8 +92,8 @@
void _measureSize() {
setState(() {
var root = getRoot();
- _width = root.clientWidth;
- _height = root.clientHeight;
+ _width = root.width.round();
+ _height = root.height.round();
});
}
diff --git a/sky/framework/components/scrollable.dart b/sky/framework/components/scrollable.dart
index 49310c1..3858a25 100644
--- a/sky/framework/components/scrollable.dart
+++ b/sky/framework/components/scrollable.dart
@@ -6,7 +6,7 @@
import '../animation/mechanics.dart';
import '../animation/scroll_behavior.dart';
import '../fn.dart';
-import '../theme/view-configuration.dart' as config;
+import '../theme/view_configuration.dart' as config;
import 'dart:math' as math;
import 'dart:sky' as sky;
diff --git a/sky/framework/fn.dart b/sky/framework/fn.dart
index 850ffd2..78672ce 100644
--- a/sky/framework/fn.dart
+++ b/sky/framework/fn.dart
@@ -8,51 +8,15 @@
import 'dart:collection';
import 'dart:sky' as sky;
import 'reflect.dart' as reflect;
+import 'layout.dart';
+
+export 'layout.dart' show Style;
final sky.Tracing _tracing = sky.window.tracing;
final bool _shouldLogRenderDuration = false;
final bool _shouldTrace = false;
-class Style {
- final String _className;
- static final Map<String, Style> _cache = new HashMap<String, Style>();
-
- static int _nextStyleId = 1;
-
- static String _getNextClassName() { return "style${_nextStyleId++}"; }
-
- Style extend(Style other) {
- var className = "$_className ${other._className}";
-
- return _cache.putIfAbsent(className, () {
- return new Style._internal(className);
- });
- }
-
- factory Style(String styles) {
- return _cache.putIfAbsent(styles, () {
- var className = _getNextClassName();
- sky.Element styleNode = sky.document.createElement('style');
- styleNode.setChild(new sky.Text(".$className { $styles }"));
- sky.document.appendChild(styleNode);
- return new Style._internal(className);
- });
- }
-
- Style._internal(this._className);
-}
-
-void _parentInsertBefore(sky.ParentNode parent,
- sky.Node node,
- sky.Node ref) {
- if (ref != null) {
- ref.insertBefore([node]);
- } else {
- parent.appendChild(node);
- }
-}
-
enum _SyncOperation { IDENTICAL, INSERTION, STATEFUL, STATELESS, REMOVAL }
/*
@@ -63,7 +27,7 @@
String _key;
UINode _parent;
UINode get parent => _parent;
- sky.Node _root;
+ RenderCSS _root;
bool _defunct = false;
UINode({ Object key }) {
@@ -74,7 +38,7 @@
// if the |old| node has become stateful and should be retained.
bool _willSync(UINode old) => false;
- void _sync(UINode old, sky.ParentNode host, sky.Node insertBefore);
+ void _sync(UINode old, RenderCSSContainer host, RenderCSS insertBefore);
void _remove() {
_defunct = true;
@@ -118,8 +82,8 @@
}
// Returns the child which should be retained as the child of this node.
- UINode _syncChild(UINode node, UINode oldNode, sky.ParentNode host,
- sky.Node insertBefore) {
+ UINode _syncChild(UINode node, UINode oldNode, RenderCSSContainer host,
+ RenderCSS insertBefore) {
assert(oldNode == null || node._key == oldNode._key);
@@ -139,7 +103,7 @@
_traceSync(_SyncOperation.STATEFUL, node._key);
oldNode._sync(node, host, insertBefore);
node._defunct = true;
- assert(oldNode._root is sky.Node);
+ assert(oldNode._root is RenderCSS);
return oldNode;
}
@@ -154,7 +118,7 @@
if (oldNode != null)
oldNode._defunct = true;
- assert(node._root is sky.Node);
+ assert(node._root is RenderCSS);
return node;
}
}
@@ -164,14 +128,16 @@
ContentNode(UINode content) : this.content = content, super(key: content._key);
- void _sync(UINode old, sky.ParentNode host, sky.Node insertBefore) {
+ void _sync(UINode old, RenderCSSContainer host, RenderCSS insertBefore) {
UINode oldContent = old == null ? null : (old as ContentNode).content;
content = _syncChild(content, oldContent, host, insertBefore);
+ assert(content._root != null);
_root = content._root;
}
void _remove() {
- _removeChild(content);
+ if (content != null)
+ _removeChild(content);
super._remove();
}
}
@@ -183,31 +149,33 @@
}
/*
- * SkyNodeWrappers correspond to a desired state of a sky.Node. They are fully
+ * SkyNodeWrappers correspond to a desired state of a RenderCSS. They are fully
* immutable, with one exception: A UINode which is a Component which lives within
* an SkyElementWrapper's children list, may be replaced with the "old" instance if it
* has become stateful.
*/
abstract class SkyNodeWrapper extends UINode {
- static final Map<sky.Node, SkyNodeWrapper> _nodeMap =
- new HashMap<sky.Node, SkyNodeWrapper>();
+ static final Map<RenderCSS, SkyNodeWrapper> _nodeMap =
+ new HashMap<RenderCSS, SkyNodeWrapper>();
- static SkyNodeWrapper _getMounted(sky.Node node) => _nodeMap[node];
+ static SkyNodeWrapper _getMounted(RenderCSS node) => _nodeMap[node];
SkyNodeWrapper({ Object key }) : super(key: key);
SkyNodeWrapper get _emptyNode;
- sky.Node _createNode();
+ RenderCSS _createNode();
- void _sync(UINode old, sky.ParentNode host, sky.Node insertBefore) {
+ void _sync(UINode old, RenderCSSContainer host, RenderCSS insertBefore) {
if (old == null) {
_root = _createNode();
- _parentInsertBefore(host, _root, insertBefore);
+ assert(_root != null);
+ host.add(_root, before: insertBefore);
old = _emptyNode;
} else {
_root = old._root;
+ assert(_root != null);
}
_nodeMap[_root] = this;
@@ -216,9 +184,14 @@
void _syncNode(SkyNodeWrapper old);
+ void _removeChild(UINode node) {
+ assert(_root is RenderCSSContainer);
+ _root.remove(node._root);
+ super._removeChild(node);
+ }
+
void _remove() {
assert(_root != null);
- _root.remove();
_nodeMap.remove(_root);
super._remove();
}
@@ -314,12 +287,12 @@
}
static void _dispatchEvent(sky.Event e) {
- UINode target = SkyNodeWrapper._getMounted(e.target);
+ UINode target = SkyNodeWrapper._getMounted(bridgeEventTargetToRenderNode(e.target));
// TODO(rafaelw): StopPropagation?
while (target != null) {
if (target is EventListenerNode) {
- (target as EventListenerNode)._handleEvent(e);
+ target._handleEvent(e);
}
target = target._parent;
@@ -332,7 +305,7 @@
}
}
- void _sync(UINode old, sky.ParentNode host, sky.Node insertBefore) {
+ void _sync(UINode old, RenderCSSContainer host, RenderCSS insertBefore) {
for (var type in listeners.keys) {
_ensureDocumentListener(type);
}
@@ -354,19 +327,15 @@
SkyNodeWrapper get _emptyNode => _emptyText;
- static final Style _displayParagraph = new Style('display:paragraph');
-
- sky.Node _createNode() {
- return sky.document.createElement('div')
- ..setChild(new sky.Text(this.data))
- ..setAttribute('class', _displayParagraph._className);
+ RenderCSSText _root;
+ RenderCSS _createNode() {
+ return new RenderCSSText(this, this.data);
}
void _syncNode(SkyNodeWrapper old) {
if (old == _emptyText)
return; // we set inside _createNode();
-
- (_root.firstChild as sky.Text).data = data;
+ _root.data = data;
}
}
@@ -374,16 +343,10 @@
abstract class SkyElementWrapper extends SkyNodeWrapper {
- String get _tagName;
-
- sky.Node _createNode() => sky.document.createElement(_tagName);
-
final List<UINode> children;
final Style style;
final String inlineStyle;
- String _class;
-
SkyElementWrapper({
Object key,
List<UINode> children,
@@ -396,17 +359,18 @@
}
void _remove() {
- super._remove();
- if (children != null) {
- for (var child in children) {
- _removeChild(child);
- }
+ assert(children != null);
+ for (var child in children) {
+ assert(child != null);
+ _removeChild(child);
}
+ super._remove();
}
bool _debugHasDuplicateIds() {
var idSet = new HashSet<String>();
for (var child in children) {
+ assert(child != null);
if (child is Text) {
continue; // Text nodes all have the same key and are never reordered.
}
@@ -419,42 +383,28 @@
return false;
}
- void _ensureClass() {
- if (_class == null) {
- List<Style> styles = new List<Style>();
- if (style != null) {
- styles.add(style);
- }
-
- UINode parent = _parent;
- while (parent != null && parent is! SkyNodeWrapper) {
- if (parent is StyleNode && (parent as StyleNode).style != null)
- styles.add((parent as StyleNode).style);
-
- parent = parent._parent;
- }
-
- _class = styles.map((s) => s._className).join(' ');
- }
- }
-
void _syncNode(SkyNodeWrapper old) {
SkyElementWrapper oldSkyElementWrapper = old as SkyElementWrapper;
- sky.Element root = _root as sky.Element;
- _ensureClass();
- if (_class != oldSkyElementWrapper._class && _class != '')
- root.setAttribute('class', _class);
+ List<Style> styles = new List<Style>();
+ if (style != null)
+ styles.add(style);
+ UINode parent = _parent;
+ while (parent != null && parent is! SkyNodeWrapper) {
+ if (parent is StyleNode && parent.style != null)
+ styles.add(parent.style);
+ parent = parent._parent;
+ }
+ _root.updateStyles(styles);
- if (inlineStyle != oldSkyElementWrapper.inlineStyle)
- root.setAttribute('style', inlineStyle);
+ _root.updateInlineStyle(inlineStyle);
_syncChildren(oldSkyElementWrapper);
}
void _syncChildren(SkyElementWrapper oldSkyElementWrapper) {
- sky.Element root = _root as sky.Element;
- assert(root != null);
+ if (_root is! RenderCSSContainer)
+ return;
var startIndex = 0;
var endIndex = children.length;
@@ -463,12 +413,13 @@
var oldStartIndex = 0;
var oldEndIndex = oldChildren.length;
- sky.Node nextSibling = null;
+ RenderCSS nextSibling = null;
UINode currentNode = null;
UINode oldNode = null;
void sync(int atIndex) {
children[atIndex] = _syncChild(currentNode, oldNode, _root, nextSibling);
+ assert(children[atIndex] != null);
}
// Scan backwards from end of list while nodes can be directly synced
@@ -484,7 +435,6 @@
endIndex--;
oldEndIndex--;
sync(endIndex);
- nextSibling = currentNode._root;
}
HashMap<String, UINode> oldNodeIdMap = null;
@@ -526,19 +476,22 @@
return false;
oldNodeIdMap[currentNode._key] = null; // mark it reordered.
- _parentInsertBefore(root, oldNode._root, nextSibling);
+ assert(_root is RenderCSSContainer);
+ assert(oldNode._root is RenderCSSContainer);
+ oldSkyElementWrapper._root.remove(oldNode._root);
+ _root.add(oldNode._root, before: nextSibling);
return true;
}
// Scan forwards, this time we may re-order;
- nextSibling = root.firstChild;
+ nextSibling = _root.firstChild;
while (startIndex < endIndex && oldStartIndex < oldEndIndex) {
currentNode = children[startIndex];
oldNode = oldChildren[oldStartIndex];
if (currentNode._key == oldNode._key) {
assert(currentNode.runtimeType == oldNode.runtimeType);
- nextSibling = nextSibling.nextSibling;
+ nextSibling = _root.childAfter(nextSibling);
sync(startIndex);
startIndex++;
advanceOldStartIndex();
@@ -571,7 +524,7 @@
class Container extends SkyElementWrapper {
- String get _tagName => 'div';
+ RenderCSS _createNode() => new RenderCSSContainer(this);
static final Container _emptyContainer = new Container();
@@ -590,49 +543,10 @@
);
}
-abstract class LayoutContainer extends Container {
-
- LayoutContainer({
- Object key,
- List<UINode> children,
- Style style,
- String inlineStyle
- }) : super(
- key: key,
- children: children,
- style: style,
- inlineStyle: inlineStyle
- );
-
- sky.Node _createNode() {
- var result = super._createNode();
- result.setLayoutManager(() => layout(_root));
- return result;
- }
-
- // If we ever reuse sky nodes for different classes, then we should
- // call _root.setLayoutManager(null) during _remove() here.
-
- void _syncNode(SkyNodeWrapper old) {
- super._syncNode(old);
- _root.setLayoutManager(() => layout(_root));
- _root.setNeedsLayout();
- }
-
- void layout(sky.Element skyNode);
- // set skyNode.width (e.g., set it to skyNode.parentNode.width)
- // for each skyNode.getChildNodes()[i]:
- // call .layout()
- // set .x, .y
- // set .width if you want to force a width
- // set .height if you want to force a height
- // set skyNode.height
-
-}
-
class Image extends SkyElementWrapper {
- String get _tagName => 'img';
+ RenderCSSImage _root;
+ RenderCSSImage _createNode() => new RenderCSSImage(this, this.src, this.width, this.height);
static final Image _emptyImage = new Image();
@@ -659,56 +573,7 @@
void _syncNode(UINode old) {
super._syncNode(old);
-
- Image oldImage = old as Image;
- sky.HTMLImageElement skyImage = _root as sky.HTMLImageElement;
-
- if (src != oldImage.src)
- skyImage.src = src;
-
- if (width != oldImage.width)
- skyImage.style['width'] = '${width}px';
-
- if (height != oldImage.height)
- skyImage.style['height'] = '${height}px';
- }
-}
-
-class Anchor extends SkyElementWrapper {
-
- String get _tagName => 'a';
-
- static final Anchor _emptyAnchor = new Anchor();
-
- UINode get _emptyNode => _emptyAnchor;
-
- final String href;
- final int width;
- final int height;
-
- Anchor({
- Object key,
- List<UINode> children,
- Style style,
- String inlineStyle,
- this.width,
- this.height,
- this.href
- }) : super(
- key: key,
- children: children,
- style: style,
- inlineStyle: inlineStyle
- );
-
- void _syncNode(UINode old) {
- super._syncNode(old);
-
- Anchor oldAnchor = old as Anchor;
- sky.HTMLAnchorElement skyAnchor = _root as sky.HTMLAnchorElement;
-
- if (href != oldAnchor.href)
- skyAnchor.href = href;
+ _root.configure(this.src, this.width, this.height);
}
}
@@ -789,9 +654,6 @@
bool get _isBuilding => _currentlyBuilding == this;
bool _dirty = true;
- sky.Node get _host => _root.parentNode;
- sky.Node get _insertionPoint => _root == null ? _root : _root.nextSibling;
-
UINode _built;
final int _order;
static int _currentOrder = 0;
@@ -835,7 +697,7 @@
// TODO(rafaelw): It seems wrong to expose DOM at all. This is presently
// needed to get sizing info.
- sky.Node getRoot() => _root;
+ RenderCSS getRoot() => _root;
void _remove() {
assert(_built != null);
@@ -871,7 +733,7 @@
* 3) Syncing against an old version
* assert(_built == null && old != null)
*/
- void _sync(UINode old, sky.ParentNode host, sky.Node insertBefore) {
+ void _sync(UINode old, RenderCSSContainer host, RenderCSS insertBefore) {
assert(!_defunct);
assert(_built == null || old == null);
@@ -898,15 +760,15 @@
_built = _syncChild(_built, oldBuilt, host, insertBefore);
_dirty = false;
_root = _built._root;
+ assert(_root != null);
}
void _buildIfDirty() {
if (!_dirty || _defunct)
return;
- assert(_host != null);
_trace('$_key rebuilding...');
- _sync(null, _host, _insertionPoint);
+ _sync(null, null, null); // TODO(ianh): figure out how passing "null, null, null" here is ok
}
void scheduleBuild() {
@@ -927,11 +789,18 @@
}
abstract class App extends Component {
- sky.Node _host;
+ RenderCSS _host;
App() : super(stateful: true) {
- _host = sky.document.createElement('div');
- sky.document.appendChild(_host);
+ _host = new RenderCSSRoot(this);
_scheduleComponentForRender(this);
}
+
+ void _buildIfDirty() {
+ if (!_dirty || _defunct)
+ return;
+
+ _trace('$_key rebuilding...');
+ _sync(null, _host, _root);
+ }
}
diff --git a/sky/framework/layout.dart b/sky/framework/layout.dart
new file mode 100644
index 0000000..eb5ff2f
--- /dev/null
+++ b/sky/framework/layout.dart
@@ -0,0 +1,406 @@
+library layout;
+
+import 'node.dart';
+import 'dart:sky' as sky;
+import 'dart:collection';
+
+// UTILS
+
+// Bridge to legacy CSS-like style specification
+// Eventually we'll replace this with something else
+class Style {
+ final String _className;
+ static final Map<String, Style> _cache = new HashMap<String, Style>();
+
+ static int _nextStyleId = 1;
+
+ static String _getNextClassName() { return "style${_nextStyleId++}"; }
+
+ Style extend(Style other) {
+ var className = "$_className ${other._className}";
+
+ return _cache.putIfAbsent(className, () {
+ return new Style._internal(className);
+ });
+ }
+
+ factory Style(String styles) {
+ return _cache.putIfAbsent(styles, () {
+ var className = _getNextClassName();
+ sky.Element styleNode = sky.document.createElement('style');
+ styleNode.setChild(new sky.Text(".$className { $styles }"));
+ sky.document.appendChild(styleNode);
+ return new Style._internal(className);
+ });
+ }
+
+ Style._internal(this._className);
+}
+
+class Rect {
+ const Rect(this.x, this.y, this.width, this.height);
+ final double x;
+ final double y;
+ final double width;
+ final double height;
+}
+
+
+// ABSTRACT LAYOUT
+
+class ParentData {
+ void detach() {
+ detachSiblings();
+ }
+ void detachSiblings() { } // workaround for lack of inter-class mixins in Dart
+}
+
+abstract class RenderNode extends Node {
+
+ // LAYOUT
+
+ // parentData is only for use by the RenderNode that actually lays this
+ // node out, and any other nodes who happen to know exactly what
+ // kind of node that is.
+ ParentData parentData;
+ void setupPos(RenderNode child) {
+ // override this to setup .parentData correctly for your class
+ if (child.parentData is! ParentData)
+ child.parentData = new ParentData();
+ }
+
+ void setAsChild(RenderNode child) { // only for use by subclasses
+ // call this whenever you decide a node is a child
+ assert(child != null);
+ setupPos(child);
+ super.setAsChild(child);
+ }
+ void dropChild(RenderNode child) { // only for use by subclasses
+ assert(child != null);
+ assert(child.parentData != null);
+ child.parentData.detach();
+ super.dropChild(child);
+ }
+
+}
+
+abstract class RenderBox extends RenderNode { }
+
+
+// GENERIC MIXIN FOR RENDER NODES THAT TAKE A LIST OF CHILDREN
+
+abstract class ContainerParentDataMixin<ChildType extends RenderNode> {
+ ChildType previousSibling;
+ ChildType nextSibling;
+ void detachSiblings() {
+ if (previousSibling != null) {
+ assert(previousSibling.parentData is ContainerParentDataMixin<ChildType>);
+ assert(previousSibling != this);
+ assert(previousSibling.parentData.nextSibling == this);
+ previousSibling.parentData.nextSibling = nextSibling;
+ }
+ if (nextSibling != null) {
+ assert(nextSibling.parentData is ContainerParentDataMixin<ChildType>);
+ assert(nextSibling != this);
+ assert(nextSibling.parentData.previousSibling == this);
+ nextSibling.parentData.previousSibling = previousSibling;
+ }
+ previousSibling = null;
+ nextSibling = null;
+ }
+}
+
+abstract class ContainerRenderNodeMixin<ChildType extends RenderNode, ParentDataType extends ContainerParentDataMixin<ChildType>> implements RenderNode {
+ // abstract class that has only InlineNode children
+
+ bool _debugUltimatePreviousSiblingOf(ChildType child, { ChildType equals }) {
+ assert(child.parentData is ParentDataType);
+ while (child.parentData.previousSibling != null) {
+ assert(child.parentData.previousSibling != child);
+ child = child.parentData.previousSibling;
+ assert(child.parentData is ParentDataType);
+ }
+ return child == equals;
+ }
+ bool _debugUltimateNextSiblingOf(ChildType child, { ChildType equals }) {
+ assert(child.parentData is ParentDataType);
+ while (child.parentData.nextSibling != null) {
+ assert(child.parentData.nextSibling != child);
+ child = child.parentData.nextSibling;
+ assert(child.parentData is ParentDataType);
+ }
+ return child == equals;
+ }
+
+ ChildType _firstChild;
+ ChildType _lastChild;
+ void add(ChildType child, { ChildType before }) {
+ assert(child != this);
+ assert(before != this);
+ assert(child != before);
+ assert(child != _firstChild);
+ assert(child != _lastChild);
+ setAsChild(child);
+ assert(child.parentData is ParentDataType);
+ assert(child.parentData.nextSibling == null);
+ assert(child.parentData.previousSibling == null);
+ if (before == null) {
+ // append at the end (_lastChild)
+ child.parentData.previousSibling = _lastChild;
+ if (_lastChild != null) {
+ assert(_lastChild.parentData is ParentDataType);
+ _lastChild.parentData.nextSibling = child;
+ }
+ _lastChild = child;
+ if (_firstChild == null)
+ _firstChild = child;
+ } else {
+ assert(_firstChild != null);
+ assert(_lastChild != null);
+ assert(_debugUltimatePreviousSiblingOf(before, equals: _firstChild));
+ assert(_debugUltimateNextSiblingOf(before, equals: _lastChild));
+ assert(before.parentData is ParentDataType);
+ if (before.parentData.previousSibling == null) {
+ // insert at the start (_firstChild); we'll end up with two or more children
+ assert(before == _firstChild);
+ child.parentData.nextSibling = before;
+ before.parentData.previousSibling = child;
+ _firstChild = child;
+ } else {
+ // insert in the middle; we'll end up with three or more children
+ // set up links from child to siblings
+ child.parentData.previousSibling = before.parentData.previousSibling;
+ child.parentData.nextSibling = before;
+ // set up links from siblings to child
+ assert(child.parentData.previousSibling.parentData is ParentDataType);
+ assert(child.parentData.nextSibling.parentData is ParentDataType);
+ child.parentData.previousSibling.parentData.nextSibling = child;
+ child.parentData.nextSibling.parentData.previousSibling = child;
+ assert(before.parentData.previousSibling == child);
+ }
+ }
+ markNeedsLayout();
+ }
+ void remove(ChildType child) {
+ assert(child.parentData is ParentDataType);
+ assert(_debugUltimatePreviousSiblingOf(child, equals: _firstChild));
+ assert(_debugUltimateNextSiblingOf(child, equals: _lastChild));
+ if (child.parentData.previousSibling == null) {
+ assert(_firstChild == child);
+ _firstChild = child.parentData.nextSibling;
+ } else {
+ assert(child.parentData.previousSibling.parentData is ParentDataType);
+ child.parentData.previousSibling.parentData.nextSibling = child.parentData.nextSibling;
+ }
+ if (child.parentData.nextSibling == null) {
+ assert(_lastChild == child);
+ _lastChild = child.parentData.previousSibling;
+ } else {
+ assert(child.parentData.nextSibling.parentData is ParentDataType);
+ child.parentData.nextSibling.parentData.previousSibling = child.parentData.previousSibling;
+ }
+ child.parentData.previousSibling = null;
+ child.parentData.nextSibling = null;
+ dropChild(child);
+ markNeedsLayout();
+ }
+ void redepthChildren() {
+ ChildType child = _firstChild;
+ while (child != null) {
+ redepthChild(child);
+ assert(child.parentData is ParentDataType);
+ child = child.parentData.nextSibling;
+ }
+ }
+ void attachChildren() {
+ ChildType child = _firstChild;
+ while (child != null) {
+ child.attach();
+ assert(child.parentData is ParentDataType);
+ child = child.parentData.nextSibling;
+ }
+ }
+ void detachChildren() {
+ ChildType child = _firstChild;
+ while (child != null) {
+ child.detach();
+ assert(child.parentData is ParentDataType);
+ child = child.parentData.nextSibling;
+ }
+ }
+
+ ChildType get firstChild => _firstChild;
+ ChildType get lastChild => _lastChild;
+ ChildType childAfter(ChildType child) {
+ assert(child.parentData is ParentDataType);
+ return child.parentData.nextSibling;
+ }
+
+}
+
+
+// CSS SHIMS
+
+abstract class RenderCSS extends RenderBox {
+
+ dynamic debug;
+ sky.Element _skyElement;
+
+ RenderCSS(this.debug) {
+ _skyElement = createSkyElement();
+ registerEventTarget(_skyElement, this);
+ }
+
+ sky.Element createSkyElement();
+
+ void updateStyles(List<Style> styles) {
+ _skyElement.setAttribute('class', styles.map((s) => s._className).join(' '));
+ }
+
+ void updateInlineStyle(String newStyle) {
+ _skyElement.setAttribute('style', newStyle);
+ }
+
+ double get width {
+ sky.ClientRect rect = _skyElement.getBoundingClientRect();
+ return rect.width;
+ }
+
+ double get height {
+ sky.ClientRect rect = _skyElement.getBoundingClientRect();
+ return rect.height;
+ }
+
+ Rect get rect {
+ sky.ClientRect rect = _skyElement.getBoundingClientRect();
+ return new Rect(rect.left, rect.top, rect.width, rect.height);
+ }
+
+}
+
+class CSSParentData extends ParentData with ContainerParentDataMixin<RenderCSS> { }
+
+class RenderCSSContainer extends RenderCSS with ContainerRenderNodeMixin<RenderCSS, CSSParentData> {
+
+ RenderCSSContainer(debug) : super(debug);
+
+ void setupPos(RenderNode child) {
+ if (child.parentData is! CSSParentData)
+ child.parentData = new CSSParentData();
+ }
+
+ sky.Element createSkyElement() => sky.document.createElement('div')
+ ..setAttribute('debug', debug.toString());
+
+ void markNeedsLayout() { }
+
+ void add(RenderCSS child, { RenderCSS before }) {
+ if (before != null) {
+ assert(before._skyElement.parentNode != null);
+ assert(before._skyElement.parentNode == _skyElement);
+ }
+ super.add(child, before: before);
+ if (before != null) {
+ before._skyElement.insertBefore([child._skyElement]);
+ assert(child._skyElement.parentNode != null);
+ assert(child._skyElement.parentNode == _skyElement);
+ assert(child._skyElement.parentNode == before._skyElement.parentNode);
+ } else {
+ _skyElement.appendChild(child._skyElement);
+ }
+ }
+ void remove(RenderCSS child) {
+ child._skyElement.remove();
+ super.remove(child);
+ }
+
+}
+
+class RenderCSSText extends RenderCSS {
+
+ RenderCSSText(debug, String newData) : super(debug) {
+ data = newData;
+ }
+
+ static final Style _displayParagraph = new Style('display:paragraph');
+
+ sky.Element createSkyElement() {
+ return sky.document.createElement('div')
+ ..setChild(new sky.Text())
+ ..setAttribute('class', _displayParagraph._className)
+ ..setAttribute('debug', debug.toString());
+ }
+
+ void set data (String value) {
+ (_skyElement.firstChild as sky.Text).data = value;
+ }
+
+}
+
+class RenderCSSImage extends RenderCSS {
+
+ RenderCSSImage(debug, String src, num width, num height) : super(debug) {
+ configure(src, width, height);
+ }
+
+ sky.Element createSkyElement() {
+ return sky.document.createElement('img')
+ ..setAttribute('debug', debug.toString());
+ }
+
+ void configure(String src, num width, num height) {
+ if (_skyElement.getAttribute('src') != src)
+ _skyElement.setAttribute('src', src);
+ _skyElement.style['width'] = '${width}px';
+ _skyElement.style['height'] = '${height}px';
+ }
+
+}
+
+class RenderCSSRoot extends RenderCSSContainer {
+ RenderCSSRoot(debug) : super(debug);
+ sky.Element createSkyElement() {
+ var result = super.createSkyElement();
+ assert(result != null);
+ sky.document.appendChild(result);
+ return result;
+ }
+}
+
+
+// legacy tools
+Map<sky.EventTarget, RenderNode> _eventTargetRegistry = {};
+void registerEventTarget(sky.EventTarget e, RenderNode n) {
+ _eventTargetRegistry[e] = n;
+}
+RenderNode bridgeEventTargetToRenderNode(sky.EventTarget e) {
+ return _eventTargetRegistry[e];
+}
+
+
+
+
+String _attributes(node) {
+ if (node is! sky.Element) return '';
+ var result = '';
+ var attrs = node.getAttributes();
+ for (var attr in attrs)
+ result += ' ${attr.name}="${attr.value}"';
+ return result;
+}
+
+void _serialiseDOM(node, [String prefix = '']) {
+ if (node is sky.Text) {
+ print(prefix + 'text: "' + node.data.replaceAll('\n', '\\n') + '"');
+ return;
+ }
+ print(prefix + node.toString() + _attributes(node));
+ var children = node.getChildNodes();
+ prefix = prefix + ' ';
+ for (var child in children)
+ _serialiseDOM(child, prefix);
+}
+
+void dumpState() {
+ _serialiseDOM(sky.document);
+}
diff --git a/sky/framework/node.dart b/sky/framework/node.dart
new file mode 100644
index 0000000..ec9a1f9
--- /dev/null
+++ b/sky/framework/node.dart
@@ -0,0 +1,60 @@
+library node;
+
+class Node {
+
+ // Nodes always have a 'depth' greater than their ancestors'.
+ // There's no guarantee regarding depth between siblings. The depth
+ // of a node is used to ensure that nodes are processed in depth
+ // order. The 'depth' of a child can be more than one greater than
+ // the 'depth' of the parent, because the 'depth' values are never
+ // decreased: all that matters is that it's greater than the parent.
+ // Consider a tree with a root node A, a child B, and a grandchild
+ // C. Initially, A will have 'depth' 0, B 'depth' 1, and C 'depth'
+ // 2. If C is moved to be a child of A, sibling of B, then the
+ // numbers won't change. C's 'depth' will still be 2.
+
+ int _depth = 0;
+ int get depth => _depth;
+ void redepthChild(Node child) { // internal, do not call
+ assert(child._attached == _attached);
+ if (child._depth <= _depth) {
+ child._depth = _depth + 1;
+ child.redepthChildren();
+ }
+ }
+ void redepthChildren() { // internal, do not call
+ // override this in subclasses with child nodes
+ // simply call redepthChild(child) for each child
+ }
+
+ bool _attached = false;
+ bool get attached => _attached;
+ void attach() {
+ // override this in subclasses with child nodes
+ // simply call attach() for each child then call your superclass
+ _attached = true;
+ attachChildren();
+ }
+ attachChildren() { } // workaround for lack of inter-class mixins in Dart
+ void detach() {
+ // override this in subclasses with child nodes
+ // simply call detach() for each child then call your superclass
+ _attached = false;
+ detachChildren();
+ }
+ detachChildren() { } // workaround for lack of inter-class mixins in Dart
+
+ void setAsChild(Node child) { // only for use by subclasses
+ assert(child != null);
+ if (attached)
+ child.attach();
+ redepthChild(child);
+ }
+ void dropChild(Node child) { // only for use by subclasses
+ assert(child != null);
+ assert(child.attached == attached);
+ if (attached)
+ child.detach();
+ }
+
+}
diff --git a/sky/framework/theme/view-configuration.dart b/sky/framework/theme/view_configuration.dart
similarity index 100%
rename from sky/framework/theme/view-configuration.dart
rename to sky/framework/theme/view_configuration.dart
diff --git a/sky/sdk/README.md b/sky/sdk/README.md
index b28384e..eca9cf0 100644
--- a/sky/sdk/README.md
+++ b/sky/sdk/README.md
@@ -1,3 +1,14 @@
+Contributing
+============
+
+[sky_sdk](https://github.com/domokit/sky_sdk) is generated from the
+[mojo repository](https://github.com/domokit/mojo) using
+[deploy_sdk.py](https://github.com/domokit/mojo/blob/master/sky/tools/deploy_sdk.py)
+Static files (including this README.md) are located under
+[sky/sdk](https://github.com/domokit/mojo/tree/master/sky/sdk). Pull
+requests and issue reports are glady accepted at the
+[mojo repository](https://github.com/domokit/mojo)!
+
Sky
===
@@ -77,6 +88,9 @@
with the hierarchy returned last frame. Any differences are then applied as
mutations to the physical hierarchy retained by the engine.
+Skip down to "Running a Sky application" to learn how to load and run this
+example on your device.
+
For examples, please see the [examples directory](examples/).
Services
@@ -139,9 +153,10 @@
2. ``pub get`` to set up a copy of the sky package in the app directory.
-3. ``./packages/sky/sky_tool start --install``
- The --install flag is only necessary to install SkyDemo.apk if not already
- installed on the device.
+3. ``./packages/sky/sky_tool start`` to start the dev server and upload your
+ app to the device.
+ (NOTE: add a ``--install`` flag to install SkyDemo.apk if not already
+ installed on the device.)
4. Use ``adb logcat`` to view any errors or Dart print() output from the app.
``adb logcat -s chromium`` can be used to filter only adb messages from
diff --git a/sky/tools/deploy_sdk.py b/sky/tools/deploy_sdk.py
index c510afe..838afe8 100755
--- a/sky/tools/deploy_sdk.py
+++ b/sky/tools/deploy_sdk.py
@@ -174,10 +174,27 @@
# Mojo package, lots of overlap with gen, must be copied:
copy(src_path('mojo/public'), sdk_path('packages/mojo/lib/public'),
dart_filter)
- mojom_dirs = [ os.path.join(build_dir, 'gen/dart-gen/mojom') ]
- mojom_dirs += args.extra_mojom_dirs
- for mojom_dir in mojom_dirs:
- copy(mojom_dir, sdk_path('packages/mojom/lib/'), gen_filter)
+
+ # By convention the generated .mojom.dart files in a pub package
+ # go under $PACKAGE/lib/mojom.
+ # The mojo package owns all the .mojom.dart files that are not in the 'sky'
+ # mojom module.
+ def non_sky_gen_filter(path):
+ if os.path.isdir(path) and path.endswith('sky'):
+ return False
+ return gen_filter(path)
+ mojo_package_mojom_dir = sdk_path('packages/mojo/lib/mojom')
+ copy(os.path.join(build_dir, 'gen/dart-gen/mojom'), mojo_package_mojom_dir,
+ non_sky_gen_filter)
+
+ # The Sky package owns the .mojom.dart files in the 'sky' mojom module.
+ def sky_gen_filter(path):
+ if os.path.isfile(path) and not os.path.dirname(path).endswith('sky'):
+ return False
+ return gen_filter(path)
+ sky_package_mojom_dir = sdk_path('packages/sky/lib/mojom')
+ copy(os.path.join(build_dir, 'gen/dart-gen/mojom'), sky_package_mojom_dir,
+ sky_gen_filter)
# Mojo SDK additions:
copy_or_link(src_path('mojo/public/dart/bindings.dart'),
@@ -205,11 +222,14 @@
ensure_dir_exists(packages_dir)
make_relative_symlink(sdk_path('packages/mojo/lib'),
os.path.join(packages_dir, 'mojo'))
- make_relative_symlink(sdk_path('packages/mojom/lib'),
- os.path.join(packages_dir, 'mojom'))
make_relative_symlink(sdk_path('packages/sky/lib'),
os.path.join(packages_dir, 'sky'))
+ mojom_dirs = [ mojo_package_mojom_dir, sky_package_mojom_dir ]
+ mojom_dirs += args.extra_mojom_dirs
+ for mojom_dir in mojom_dirs:
+ copy(mojom_dir, os.path.join(packages_dir, 'mojom'), gen_filter)
+
if should_commit:
# Kinda a hack to make a prettier build dir for the commit:
script_path = os.path.relpath(os.path.abspath(__file__), SRC_ROOT)
diff --git a/sky/tools/webkitpy/layout_tests/controllers/single_test_runner.py b/sky/tools/webkitpy/layout_tests/controllers/single_test_runner.py
index 31d4d8c..56af871 100644
--- a/sky/tools/webkitpy/layout_tests/controllers/single_test_runner.py
+++ b/sky/tools/webkitpy/layout_tests/controllers/single_test_runner.py
@@ -307,6 +307,10 @@
failures.append(test_failures.FailureMissingAudio())
return failures
+ # FIXME: This won't be needed once we have flags for Sky that suppress the
+ # Observatory messages.
+ _filter_observatory_messages = re.compile(r"^CONSOLE: Observatory listening on.*\n", re.MULTILINE)
+
def _get_normalized_output_text(self, output):
"""Returns the normalized text output, i.e. the output in which
the end-of-line characters are normalized to "\n"."""
@@ -314,7 +318,9 @@
# changed to "\r\n" by our system (Python/Cygwin), resulting in
# "\r\r\n", when, in fact, we wanted to compare the text output with
# the normalized text expectation files.
- return output.replace("\r\r\n", "\r\n").replace("\r\n", "\n")
+ normalized_lines = output.replace("\r\r\n", "\r\n").replace("\r\n", "\n")
+ normalized_lines = re.sub(self._filter_observatory_messages, r"", normalized_lines)
+ return normalized_lines
# FIXME: This function also creates the image diff. Maybe that work should
# be handled elsewhere?
diff --git a/ui/gl/BUILD.gn b/ui/gl/BUILD.gn
index 04dea9c..99e03a1 100644
--- a/ui/gl/BUILD.gn
+++ b/ui/gl/BUILD.gn
@@ -207,10 +207,6 @@
"//ui/ozone:ozone_base",
]
}
-
- if (is_android) {
- deps += [ "//ui/android:ui_java" ]
- }
}
source_set("gl_unittest_utils") {