OLD | NEW |
(Empty) | |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "tonic/dart_library_loader.h" |
| 6 |
| 7 #include "base/callback.h" |
| 8 #include "base/trace_event/trace_event.h" |
| 9 #include "mojo/common/data_pipe_drainer.h" |
| 10 #include "tonic/dart_api_scope.h" |
| 11 #include "tonic/dart_converter.h" |
| 12 #include "tonic/dart_dependency_catcher.h" |
| 13 #include "tonic/dart_error.h" |
| 14 #include "tonic/dart_isolate_scope.h" |
| 15 #include "tonic/dart_library_provider.h" |
| 16 #include "tonic/dart_state.h" |
| 17 |
| 18 using mojo::common::DataPipeDrainer; |
| 19 |
| 20 namespace blink { |
| 21 |
| 22 namespace { |
| 23 |
| 24 // Helper to erase a T* from a container of std::unique_ptr<T>s. |
| 25 template<typename T, typename C> |
| 26 void EraseUniquePtr(C& container, T* item) { |
| 27 std::unique_ptr<T> key = std::unique_ptr<T>(item); |
| 28 container.erase(key); |
| 29 key.release(); |
| 30 } |
| 31 |
| 32 } |
| 33 |
| 34 // A DartLibraryLoader::Job represents a network load. It fetches data from the |
| 35 // network and buffers the data in std::vector. To cancel the job, delete this |
| 36 // object. |
| 37 class DartLibraryLoader::Job : public DartDependency, |
| 38 public DataPipeDrainer::Client { |
| 39 public: |
| 40 Job(DartLibraryLoader* loader, const std::string& name) |
| 41 : loader_(loader), name_(name), weak_factory_(this) { |
| 42 loader->library_provider()->GetLibraryAsStream( |
| 43 name, base::Bind(&Job::OnStreamAvailable, weak_factory_.GetWeakPtr())); |
| 44 } |
| 45 |
| 46 const std::string& name() const { return name_; } |
| 47 |
| 48 protected: |
| 49 DartLibraryLoader* loader_; |
| 50 // TODO(abarth): Should we be using SharedBuffer to buffer the data? |
| 51 std::vector<uint8_t> buffer_; |
| 52 |
| 53 private: |
| 54 void OnStreamAvailable(mojo::ScopedDataPipeConsumerHandle pipe) { |
| 55 if (!pipe.is_valid()) { |
| 56 loader_->DidFailJob(this); |
| 57 return; |
| 58 } |
| 59 drainer_ = adoptPtr(new DataPipeDrainer(this, pipe.Pass())); |
| 60 } |
| 61 |
| 62 // DataPipeDrainer::Client |
| 63 void OnDataAvailable(const void* data, size_t num_bytes) override { |
| 64 const uint8_t* bytes = static_cast<const uint8_t*>(data); |
| 65 buffer_.insert(buffer_.end(), bytes, bytes + num_bytes); |
| 66 } |
| 67 // Subclasses must implement OnDataComplete. |
| 68 |
| 69 std::string name_; |
| 70 OwnPtr<DataPipeDrainer> drainer_; |
| 71 |
| 72 base::WeakPtrFactory<Job> weak_factory_; |
| 73 }; |
| 74 |
| 75 class DartLibraryLoader::ImportJob : public Job { |
| 76 public: |
| 77 ImportJob(DartLibraryLoader* loader, const std::string& name) : Job(loader, na
me) { |
| 78 TRACE_EVENT_ASYNC_BEGIN1("sky", "DartLibraryLoader::ImportJob", this, "url", |
| 79 name); |
| 80 } |
| 81 |
| 82 private: |
| 83 // DataPipeDrainer::Client |
| 84 void OnDataComplete() override { |
| 85 TRACE_EVENT_ASYNC_END0("sky", "DartLibraryLoader::ImportJob", this); |
| 86 loader_->DidCompleteImportJob(this, buffer_); |
| 87 } |
| 88 }; |
| 89 |
| 90 class DartLibraryLoader::SourceJob : public Job { |
| 91 public: |
| 92 SourceJob(DartLibraryLoader* loader, const std::string& name, Dart_Handle libr
ary) |
| 93 : Job(loader, name), library_(loader->dart_state(), library) { |
| 94 TRACE_EVENT_ASYNC_BEGIN1("sky", "DartLibraryLoader::SourceJob", this, "url", |
| 95 name); |
| 96 } |
| 97 |
| 98 Dart_PersistentHandle library() const { return library_.value(); } |
| 99 |
| 100 private: |
| 101 // DataPipeDrainer::Client |
| 102 void OnDataComplete() override { |
| 103 TRACE_EVENT_ASYNC_END0("sky", "DartLibraryLoader::SourceJob", this); |
| 104 loader_->DidCompleteSourceJob(this, buffer_); |
| 105 } |
| 106 |
| 107 DartPersistentValue library_; |
| 108 }; |
| 109 |
| 110 // A DependencyWatcher represents a request to watch for when a given set of |
| 111 // dependencies (either libraries or parts of libraries) have finished loading. |
| 112 // When the dependencies are satisfied (including transitive dependencies), then |
| 113 // the |callback| will be invoked. |
| 114 class DartLibraryLoader::DependencyWatcher { |
| 115 public: |
| 116 DependencyWatcher(const std::unordered_set<DartDependency*>& dependencies, |
| 117 const base::Closure& callback) |
| 118 : dependencies_(dependencies), callback_(callback) { |
| 119 DCHECK(!dependencies_.empty()); |
| 120 } |
| 121 |
| 122 bool DidResolveDependency( |
| 123 DartDependency* resolved_dependency, |
| 124 const std::unordered_set<DartDependency*>& new_dependencies) { |
| 125 const auto& it = dependencies_.find(resolved_dependency); |
| 126 if (it == dependencies_.end()) |
| 127 return false; |
| 128 dependencies_.erase(it); |
| 129 for (const auto& dependency : new_dependencies) |
| 130 dependencies_.insert(dependency); |
| 131 return dependencies_.empty(); |
| 132 } |
| 133 |
| 134 const base::Closure& callback() const { return callback_; } |
| 135 |
| 136 private: |
| 137 std::unordered_set<DartDependency*> dependencies_; |
| 138 base::Closure callback_; |
| 139 }; |
| 140 |
| 141 // A WatcherSignaler is responsible for signaling DependencyWatchers when their |
| 142 // dependencies resolve and for calling the DependencyWatcher's callback. We use |
| 143 // a separate object of this task because we want to carefully manage when we |
| 144 // call the callbacks, which can call into us again reentrantly. |
| 145 // |
| 146 // WatcherSignaler is designed to be placed on the stack as a RAII. After its |
| 147 // destructor runs, we might have executed aribitrary script. |
| 148 class DartLibraryLoader::WatcherSignaler { |
| 149 public: |
| 150 WatcherSignaler(DartLibraryLoader& loader, |
| 151 DartDependency* resolved_dependency) |
| 152 : loader_(loader), |
| 153 catcher_(adoptPtr(new DartDependencyCatcher(loader))), |
| 154 resolved_dependency_(resolved_dependency) {} |
| 155 |
| 156 ~WatcherSignaler() { |
| 157 std::vector<DependencyWatcher*> completed_watchers; |
| 158 for (const auto& watcher : loader_.dependency_watchers_) { |
| 159 if (watcher->DidResolveDependency(resolved_dependency_, |
| 160 catcher_->dependencies())) |
| 161 completed_watchers.push_back(watcher.get()); |
| 162 } |
| 163 |
| 164 // Notice that we remove the dependency catcher and extract all the |
| 165 // callbacks before running any of them. We don't want to be re-entered |
| 166 // below the callbacks and end up in an inconsistent state. |
| 167 catcher_.clear(); |
| 168 std::vector<base::Closure> callbacks; |
| 169 for (const auto& watcher : completed_watchers) { |
| 170 callbacks.push_back(watcher->callback()); |
| 171 EraseUniquePtr(loader_.dependency_watchers_, watcher); |
| 172 } |
| 173 |
| 174 // Finally, run all the callbacks while touching only data on the stack. |
| 175 for (const auto& callback : callbacks) |
| 176 callback.Run(); |
| 177 } |
| 178 |
| 179 private: |
| 180 DartLibraryLoader& loader_; |
| 181 OwnPtr<DartDependencyCatcher> catcher_; |
| 182 DartDependency* resolved_dependency_; |
| 183 }; |
| 184 |
| 185 DartLibraryLoader::DartLibraryLoader(DartState* dart_state) |
| 186 : dart_state_(dart_state), |
| 187 library_provider_(nullptr), |
| 188 dependency_catcher_(nullptr) { |
| 189 } |
| 190 |
| 191 DartLibraryLoader::~DartLibraryLoader() { |
| 192 } |
| 193 |
| 194 Dart_Handle DartLibraryLoader::HandleLibraryTag(Dart_LibraryTag tag, |
| 195 Dart_Handle library, |
| 196 Dart_Handle url) { |
| 197 DCHECK(Dart_IsLibrary(library)); |
| 198 DCHECK(Dart_IsString(url)); |
| 199 if (tag == Dart_kCanonicalizeUrl) |
| 200 return DartState::Current()->library_loader().CanonicalizeURL(library, url); |
| 201 if (tag == Dart_kImportTag) { |
| 202 return DartState::Current()->library_loader().Import(library, url); |
| 203 } |
| 204 if (tag == Dart_kSourceTag) { |
| 205 return DartState::Current()->library_loader().Source(library, url); |
| 206 } |
| 207 DCHECK(false); |
| 208 return Dart_NewApiError("Unknown library tag."); |
| 209 } |
| 210 |
| 211 void DartLibraryLoader::WaitForDependencies( |
| 212 const std::unordered_set<DartDependency*>& dependencies, |
| 213 const base::Closure& callback) { |
| 214 if (dependencies.empty()) |
| 215 return callback.Run(); |
| 216 dependency_watchers_.insert( |
| 217 std::unique_ptr<DependencyWatcher>( |
| 218 new DependencyWatcher(dependencies, callback))); |
| 219 } |
| 220 |
| 221 void DartLibraryLoader::LoadLibrary(const std::string& name) { |
| 222 const auto& result = pending_libraries_.insert(std::make_pair(name, nullptr)); |
| 223 if (result.second) { |
| 224 // New entry. |
| 225 std::unique_ptr<Job> job = std::unique_ptr<Job>(new ImportJob(this, name)); |
| 226 result.first->second = job.get(); |
| 227 jobs_.insert(std::move(job)); |
| 228 } |
| 229 if (dependency_catcher_) |
| 230 dependency_catcher_->AddDependency(result.first->second); |
| 231 } |
| 232 |
| 233 Dart_Handle DartLibraryLoader::Import(Dart_Handle library, Dart_Handle url) { |
| 234 LoadLibrary(StdStringFromDart(url)); |
| 235 return Dart_True(); |
| 236 } |
| 237 |
| 238 Dart_Handle DartLibraryLoader::Source(Dart_Handle library, Dart_Handle url) { |
| 239 std::unique_ptr<Job> job = std::unique_ptr<Job>( |
| 240 new SourceJob(this, StdStringFromDart(url), library)); |
| 241 if (dependency_catcher_) |
| 242 dependency_catcher_->AddDependency(job.get()); |
| 243 jobs_.insert(std::move(job)); |
| 244 return Dart_True(); |
| 245 } |
| 246 |
| 247 Dart_Handle DartLibraryLoader::CanonicalizeURL(Dart_Handle library, |
| 248 Dart_Handle url) { |
| 249 return library_provider_->CanonicalizeURL(library, url); |
| 250 } |
| 251 |
| 252 void DartLibraryLoader::DidCompleteImportJob( |
| 253 ImportJob* job, |
| 254 const std::vector<uint8_t>& buffer) { |
| 255 DartIsolateScope scope(dart_state_->isolate()); |
| 256 DartApiScope api_scope; |
| 257 |
| 258 WatcherSignaler watcher_signaler(*this, job); |
| 259 |
| 260 Dart_Handle result = Dart_LoadLibrary( |
| 261 StdStringToDart(job->name()), |
| 262 Dart_NewStringFromUTF8(buffer.data(), buffer.size()), 0, 0); |
| 263 if (Dart_IsError(result)) { |
| 264 LOG(ERROR) << "Error Loading " << job->name() << " " |
| 265 << Dart_GetError(result); |
| 266 } |
| 267 |
| 268 pending_libraries_.erase(job->name()); |
| 269 EraseUniquePtr<Job>(jobs_, job); |
| 270 } |
| 271 |
| 272 void DartLibraryLoader::DidCompleteSourceJob( |
| 273 SourceJob* job, |
| 274 const std::vector<uint8_t>& buffer) { |
| 275 DartIsolateScope scope(dart_state_->isolate()); |
| 276 DartApiScope api_scope; |
| 277 |
| 278 WatcherSignaler watcher_signaler(*this, job); |
| 279 |
| 280 Dart_Handle result = Dart_LoadSource( |
| 281 Dart_HandleFromPersistent(job->library()), |
| 282 StdStringToDart(job->name()), |
| 283 Dart_NewStringFromUTF8(buffer.data(), buffer.size()), 0, 0); |
| 284 |
| 285 if (Dart_IsError(result)) { |
| 286 LOG(ERROR) << "Error Loading " << job->name() << " " |
| 287 << Dart_GetError(result); |
| 288 } |
| 289 |
| 290 EraseUniquePtr<Job>(jobs_, job); |
| 291 } |
| 292 |
| 293 void DartLibraryLoader::DidFailJob(Job* job) { |
| 294 DartIsolateScope scope(dart_state_->isolate()); |
| 295 DartApiScope api_scope; |
| 296 |
| 297 WatcherSignaler watcher_signaler(*this, job); |
| 298 |
| 299 LOG(ERROR) << "Library Load failed: " << job->name(); |
| 300 // TODO(eseidel): Call Dart_LibraryHandleError in the SourceJob case? |
| 301 |
| 302 EraseUniquePtr<Job>(jobs_, job); |
| 303 } |
| 304 |
| 305 } // namespace blink |
OLD | NEW |