Import Cobalt 4.13110
diff --git a/src/cobalt/browser/web_module.cc b/src/cobalt/browser/web_module.cc index 5ee42a0..29a4b1d 100644 --- a/src/cobalt/browser/web_module.cc +++ b/src/cobalt/browser/web_module.cc
@@ -615,9 +615,6 @@ // Stop the generation of render trees. layout_manager_->Suspend(); - // Clear out all image resources from the image cache. - image_cache_->Purge(); - #if defined(ENABLE_DEBUG_CONSOLE) // The debug overlay may be holding onto a render tree, clear that out. debug_overlay_->ClearInput(); @@ -627,6 +624,17 @@ // in-progress loads. loader_factory_->Suspend(); + // Ensure the document is not holding onto any more image cached resources so + // that they are eligible to be purged. + window_->document()->PurgeCachedResourceReferencesRecursively(); + + // Clear out all image resources from the image cache. We need to do this + // after we abort all in-progress loads, and after we clear all document + // references, or they will still be referenced and won't be cleared from the + // cache. + image_cache_->Purge(); + remote_typeface_cache_->Purge(); + // Finally mark that we have no resource provider. resource_provider_ = NULL; }
diff --git a/src/cobalt/build/build.id b/src/cobalt/build/build.id index ec9b96f..d5688f5 100644 --- a/src/cobalt/build/build.id +++ b/src/cobalt/build/build.id
@@ -1 +1 @@ -13031 \ No newline at end of file +13110 \ No newline at end of file
diff --git a/src/cobalt/dom/html_element.h b/src/cobalt/dom/html_element.h index 33ffb9a..4aad8a2 100644 --- a/src/cobalt/dom/html_element.h +++ b/src/cobalt/dom/html_element.h
@@ -261,6 +261,10 @@ void CopyDirectionality(const HTMLElement& other); + // Releases image resources and invalidates computed style if there are images + // associated with this html element in the image cache. + void ReleaseImagesAndInvalidateComputedStyleIfNecessary() OVERRIDE; + private: // From Node. void OnMutation() OVERRIDE; @@ -289,10 +293,6 @@ // https://www.w3.org/TR/html5/semantics.html#the-root-element. bool IsRootElement(); - // Releases image resources and invalidates computed style if there are images - // associated with this html element in the image cache. - void ReleaseImagesAndInvalidateComputedStyleIfNecessary(); - // The directionality of the html element is determined by the 'dir' // attribute. // https://dev.w3.org/html5/spec-preview/global-attributes.html#the-directionality
diff --git a/src/cobalt/dom/node.cc b/src/cobalt/dom/node.cc index 6ce2955..cac03aa 100644 --- a/src/cobalt/dom/node.cc +++ b/src/cobalt/dom/node.cc
@@ -467,6 +467,16 @@ } } +void Node::PurgeCachedResourceReferencesRecursively() { + ReleaseImagesAndInvalidateComputedStyleIfNecessary(); + + Node* child = first_child_; + while (child) { + child->PurgeCachedResourceReferencesRecursively(); + child = child->next_sibling_; + } +} + void Node::InvalidateLayoutBoxesFromNodeAndAncestors() { if (parent_) { parent_->InvalidateLayoutBoxesFromNodeAndAncestors();
diff --git a/src/cobalt/dom/node.h b/src/cobalt/dom/node.h index 0c2a4f6..7e349d6 100644 --- a/src/cobalt/dom/node.h +++ b/src/cobalt/dom/node.h
@@ -218,6 +218,10 @@ virtual scoped_refptr<Node> Duplicate() const = 0; + // Purges all cached resource reference from the current node and all + // descendents. + void PurgeCachedResourceReferencesRecursively(); + DEFINE_WRAPPABLE_TYPE(Node); protected: @@ -244,6 +248,10 @@ // Invalidate the render tree nodes within the layout boxes of this node. virtual void InvalidateRenderTreeNodesFromNode() {} + // Releases image resources and invalidates computed style if there are images + // associated with this html element in the image cache. + virtual void ReleaseImagesAndInvalidateComputedStyleIfNecessary() {} + // Triggers a generation update in this node and all its ancestor nodes. void UpdateGenerationForNodeAndAncestors();
diff --git a/src/cobalt/dom_parser/html_decoder.h b/src/cobalt/dom_parser/html_decoder.h index db142e7..cf57cf9 100644 --- a/src/cobalt/dom_parser/html_decoder.h +++ b/src/cobalt/dom_parser/html_decoder.h
@@ -20,6 +20,7 @@ #include <string> #include "base/callback.h" +#include "base/logging.h" #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" #include "base/threading/thread_checker.h" @@ -60,6 +61,11 @@ const scoped_refptr<net::HttpResponseHeaders>& headers) OVERRIDE; void DecodeChunk(const char* data, size_t size) OVERRIDE; void Finish() OVERRIDE; + bool Suspend() OVERRIDE { return false; } + + void Resume(render_tree::ResourceProvider* /*resource_provider*/) OVERRIDE { + NOTIMPLEMENTED(); + }; private: // This subclass is responsible for providing the handlers for the interface
diff --git a/src/cobalt/dom_parser/xml_decoder.h b/src/cobalt/dom_parser/xml_decoder.h index 1be11a7..2ee38e4 100644 --- a/src/cobalt/dom_parser/xml_decoder.h +++ b/src/cobalt/dom_parser/xml_decoder.h
@@ -52,6 +52,11 @@ // From Decoder. void DecodeChunk(const char* data, size_t size) OVERRIDE; void Finish() OVERRIDE; + bool Suspend() OVERRIDE { return false; } + + void Resume(render_tree::ResourceProvider* /*resource_provider*/) OVERRIDE { + NOTIMPLEMENTED(); + }; private: // This subclass is responsible for providing the handlers for the interface
diff --git a/src/cobalt/loader/decoder.h b/src/cobalt/loader/decoder.h index f307097..bdd4040 100644 --- a/src/cobalt/loader/decoder.h +++ b/src/cobalt/loader/decoder.h
@@ -18,6 +18,7 @@ #define COBALT_LOADER_DECODER_H_ #include "cobalt/loader/loader_types.h" +#include "cobalt/render_tree/resource_provider.h" #include "net/http/http_response_headers.h" namespace cobalt { @@ -45,7 +46,13 @@ // This is called when all data are sent in and decoding should be finalized. virtual void Finish() = 0; - virtual void Abort() { NOTREACHED() << "Abort not supported."; } + // Suspends the decode of this resource, resetting internal state. Returns + // whether the decoder was reset correctly. If not, the load will have to be + // aborted. + virtual bool Suspend() = 0; + + // Resumes the decode of this resource, starting over from the zero state. + virtual void Resume(render_tree::ResourceProvider* resource_provider) = 0; }; } // namespace loader
diff --git a/src/cobalt/loader/fetcher_factory.cc b/src/cobalt/loader/fetcher_factory.cc index 81b7d2f..e16c970 100644 --- a/src/cobalt/loader/fetcher_factory.cc +++ b/src/cobalt/loader/fetcher_factory.cc
@@ -44,6 +44,16 @@ *file_path = FilePath(path); return !file_path->empty(); } + +std::string ClipUrl(const GURL& url, size_t length) { + const std::string& spec = url.possibly_invalid_spec(); + if (spec.size() < length) { + return spec; + } + + return spec.substr(0, length - 5) + "[...]"; +} + } // namespace FetcherFactory::FetcherFactory(network::NetworkModule* network_module) @@ -72,6 +82,7 @@ return scoped_ptr<Fetcher>(NULL); } + DLOG(INFO) << "Fetching: " << ClipUrl(url, 60); scoped_ptr<Fetcher> fetcher; if (url.SchemeIs(kEmbeddedScheme)) { EmbeddedFetcher::Options options;
diff --git a/src/cobalt/loader/font/typeface_decoder.cc b/src/cobalt/loader/font/typeface_decoder.cc index 2f7556d..9d2e6db 100644 --- a/src/cobalt/loader/font/typeface_decoder.cc +++ b/src/cobalt/loader/font/typeface_decoder.cc
@@ -29,7 +29,7 @@ success_callback_(success_callback), error_callback_(error_callback), is_raw_data_too_large_(false), - aborted_(false) { + suspended_(false) { UNREFERENCED_PARAMETER(failure_callback); DCHECK(resource_provider_); @@ -38,6 +38,11 @@ } void TypefaceDecoder::DecodeChunk(const char* data, size_t size) { + if (suspended_) { + DLOG(WARNING) << __FUNCTION__ << "[" << this << "] while suspended."; + return; + } + // If the data was already too large, then there's no need to process this // chunk. Just early out. if (is_raw_data_too_large_) { @@ -65,16 +70,16 @@ } void TypefaceDecoder::Finish() { + if (suspended_) { + DLOG(WARNING) << __FUNCTION__ << "[" << this << "] while suspended."; + return; + } + if (is_raw_data_too_large_) { error_callback_.Run("Raw typeface data size too large"); return; } - if (aborted_) { - error_callback_.Run( - "TypefaceDecoder's ResourceProvider has been externally aborted."); - return; - } if (!resource_provider_) { error_callback_.Run( "No resource provider was passed to the TypefaceDecoder."); @@ -95,7 +100,24 @@ void TypefaceDecoder::ReleaseRawData() { raw_data_.reset(); } -void TypefaceDecoder::Abort() { aborted_ = true; } +bool TypefaceDecoder::Suspend() { + DCHECK(!suspended_); + suspended_ = true; + is_raw_data_too_large_ = false; + resource_provider_ = NULL; + ReleaseRawData(); + return true; +} + +void TypefaceDecoder::Resume(render_tree::ResourceProvider* resource_provider) { + if (!suspended_) { + DCHECK_EQ(resource_provider_, resource_provider); + return; + } + + suspended_ = false; + resource_provider_ = resource_provider; +} } // namespace font } // namespace loader
diff --git a/src/cobalt/loader/font/typeface_decoder.h b/src/cobalt/loader/font/typeface_decoder.h index 4af8965..83ed308 100644 --- a/src/cobalt/loader/font/typeface_decoder.h +++ b/src/cobalt/loader/font/typeface_decoder.h
@@ -47,20 +47,20 @@ // From Decoder. void DecodeChunk(const char* data, size_t size) OVERRIDE; void Finish() OVERRIDE; - - void Abort() OVERRIDE; + bool Suspend() OVERRIDE; + void Resume(render_tree::ResourceProvider* resource_provider) OVERRIDE; private: void ReleaseRawData(); - render_tree::ResourceProvider* const resource_provider_; + render_tree::ResourceProvider* resource_provider_; const SuccessCallback success_callback_; const ErrorCallback error_callback_; scoped_ptr<render_tree::ResourceProvider::RawTypefaceDataVector> raw_data_; bool is_raw_data_too_large_; - bool aborted_; + bool suspended_; }; } // namespace font
diff --git a/src/cobalt/loader/font/typeface_decoder_test.cc b/src/cobalt/loader/font/typeface_decoder_test.cc index 9b767d6..4d12892 100644 --- a/src/cobalt/loader/font/typeface_decoder_test.cc +++ b/src/cobalt/loader/font/typeface_decoder_test.cc
@@ -54,6 +54,8 @@ void DecodeChunk(const char* data, size_t size) OVERRIDE; void Finish() OVERRIDE; + bool Suspend() OVERRIDE; + void Resume(render_tree::ResourceProvider* resource_provider) OVERRIDE; scoped_refptr<render_tree::Typeface> Typeface(); @@ -83,6 +85,13 @@ void MockTypefaceDecoder::Finish() { typeface_decoder_->Finish(); } +bool MockTypefaceDecoder::Suspend() { return typeface_decoder_->Suspend(); } + +void MockTypefaceDecoder::Resume( + render_tree::ResourceProvider* resource_provider) { + typeface_decoder_->Resume(resource_provider); +} + scoped_refptr<render_tree::Typeface> MockTypefaceDecoder::Typeface() { return typeface_decoder_callback_.typeface; }
diff --git a/src/cobalt/loader/image/image_decoder.cc b/src/cobalt/loader/image/image_decoder.cc index 24a8f9b..5161dd3 100644 --- a/src/cobalt/loader/image/image_decoder.cc +++ b/src/cobalt/loader/image/image_decoder.cc
@@ -71,6 +71,11 @@ Fetcher* fetcher, const scoped_refptr<net::HttpResponseHeaders>& headers) { UNREFERENCED_PARAMETER(fetcher); + if (state_ == kSuspended) { + DLOG(WARNING) << __FUNCTION__ << "[" << this << "] while suspended."; + return kLoadResponseContinue; + } + if (headers->response_code() == net::HTTP_OK && headers->GetContentLength() == 0) { // The server successfully processed the request and expected some contents, @@ -137,8 +142,8 @@ case kNoResourceProvider: failure_callback_.Run("No resource provider was passed to the decoder."); break; - case kAborted: - error_callback_.Run("ImageDecoder received an external signal to abort."); + case kSuspended: + DLOG(WARNING) << __FUNCTION__ << "[" << this << "] while suspended."; break; case kNotApplicable: // no image is available. @@ -147,13 +152,28 @@ } } -void ImageDecoder::Abort() { +bool ImageDecoder::Suspend() { if (state_ == kDecoding) { DCHECK(decoder_); decoder_.reset(); - state_ = kAborted; - } else if (state_ == kWaitingForHeader) { - state_ = kAborted; + } + state_ = kSuspended; + signature_cache_.position = 0; + resource_provider_ = NULL; + return true; +} + +void ImageDecoder::Resume(render_tree::ResourceProvider* resource_provider) { + if (state_ != kSuspended) { + DCHECK_EQ(resource_provider_, resource_provider); + return; + } + + state_ = kWaitingForHeader; + resource_provider_ = resource_provider; + + if (!resource_provider_) { + state_ = kNoResourceProvider; } } @@ -182,7 +202,7 @@ } break; case kNotApplicable: case kUnsupportedImageFormat: - case kAborted: + case kSuspended: case kNoResourceProvider: default: { // Do not attempt to continue processing data.
diff --git a/src/cobalt/loader/image/image_decoder.h b/src/cobalt/loader/image/image_decoder.h index be6e056..610d27b 100644 --- a/src/cobalt/loader/image/image_decoder.h +++ b/src/cobalt/loader/image/image_decoder.h
@@ -52,8 +52,8 @@ const scoped_refptr<net::HttpResponseHeaders>& headers) OVERRIDE; void DecodeChunk(const char* data, size_t size) OVERRIDE; void Finish() OVERRIDE; - - void Abort() OVERRIDE; + bool Suspend() OVERRIDE; + void Resume(render_tree::ResourceProvider* resource_provider) OVERRIDE; // Call this function to use the StubImageDecoder which produces a small image // without decoding. @@ -66,7 +66,7 @@ kNotApplicable, kUnsupportedImageFormat, kNoResourceProvider, - kAborted, + kSuspended, }; // The current longest signature is WEBP signature. @@ -81,7 +81,7 @@ bool InitializeInternalDecoder(const uint8* input_bytes, size_t size, size_t* consumed_size); - render_tree::ResourceProvider* const resource_provider_; + render_tree::ResourceProvider* resource_provider_; const SuccessCallback success_callback_; const FailureCallback failure_callback_; const ErrorCallback error_callback_;
diff --git a/src/cobalt/loader/image/image_decoder_test.cc b/src/cobalt/loader/image/image_decoder_test.cc index 59c1d38..dafc54b 100644 --- a/src/cobalt/loader/image/image_decoder_test.cc +++ b/src/cobalt/loader/image/image_decoder_test.cc
@@ -58,6 +58,8 @@ void DecodeChunk(const char* data, size_t size) OVERRIDE; void Finish() OVERRIDE; + bool Suspend() OVERRIDE; + void Resume(render_tree::ResourceProvider* resource_provider) OVERRIDE; scoped_refptr<render_tree::Image> Image(); @@ -92,6 +94,13 @@ void MockImageDecoder::Finish() { image_decoder_->Finish(); } +bool MockImageDecoder::Suspend() { return image_decoder_->Suspend(); } + +void MockImageDecoder::Resume( + render_tree::ResourceProvider* resource_provider) { + image_decoder_->Resume(resource_provider); +} + scoped_refptr<render_tree::Image> MockImageDecoder::Image() { return image_decoder_callback_.image; }
diff --git a/src/cobalt/loader/loader.cc b/src/cobalt/loader/loader.cc index 90e6bfe..a5d86e7 100644 --- a/src/cobalt/loader/loader.cc +++ b/src/cobalt/loader/loader.cc
@@ -70,23 +70,23 @@ ////////////////////////////////////////////////////////////////// Loader::Loader(const FetcherCreator& fetcher_creator, - scoped_ptr<Decoder> decoder, - const base::Callback<void(const std::string&)>& error_callback, + scoped_ptr<Decoder> decoder, const OnErrorFunction& on_error, const OnDestructionFunction& on_destruction) - : decoder_(decoder.Pass()), - fetcher_to_decoder_adaptor_( - new FetcherToDecoderAdapter(decoder_.get(), error_callback)), - fetcher_(fetcher_creator.Run(fetcher_to_decoder_adaptor_.get())), - on_destruction_(on_destruction) { + : fetcher_creator_(fetcher_creator), + decoder_(decoder.Pass()), + on_error_(on_error), + on_destruction_(on_destruction), + suspended_(false) { DCHECK(decoder_); - DCHECK(!error_callback.is_null()); - DCHECK(!fetcher_creator.is_null()); + DCHECK(!on_error.is_null()); + + Start(); // Post the error callback on the current message loop in case loader is // destroyed in the callback. if (!fetcher_) { fetcher_creator_error_closure_.Reset( - base::Bind(error_callback, "Fetcher was not created.")); + base::Bind(on_error, "Fetcher was not created.")); MessageLoop::current()->PostTask(FROM_HERE, fetcher_creator_error_closure_.callback()); } @@ -101,7 +101,44 @@ fetcher_creator_error_closure_.Cancel(); } -void Loader::Abort() { decoder_->Abort(); } +void Loader::Suspend() { + DCHECK(thread_checker_.CalledOnValidThread()); + if (suspended_) { + return; + } + + bool suspendable = decoder_->Suspend(); + if (fetcher_) { + fetcher_.reset(); + } + + fetcher_to_decoder_adaptor_.reset(); + fetcher_creator_error_closure_.Cancel(); + suspended_ = true; + + if (!suspendable) { + on_error_.Run("Aborted."); + } +} + +void Loader::Resume(render_tree::ResourceProvider* resource_provider) { + DCHECK(thread_checker_.CalledOnValidThread()); + if (!suspended_) { + return; + } + suspended_ = false; + decoder_->Resume(resource_provider); + Start(); +} + +void Loader::Start() { + DCHECK(thread_checker_.CalledOnValidThread()); + DCHECK(decoder_); + DCHECK(!fetcher_creator_.is_null()); + fetcher_to_decoder_adaptor_.reset( + new FetcherToDecoderAdapter(decoder_.get(), on_error_)); + fetcher_ = fetcher_creator_.Run(fetcher_to_decoder_adaptor_.get()); +} } // namespace loader } // namespace cobalt
diff --git a/src/cobalt/loader/loader.h b/src/cobalt/loader/loader.h index 43da425..1a1bd1e 100644 --- a/src/cobalt/loader/loader.h +++ b/src/cobalt/loader/loader.h
@@ -26,6 +26,7 @@ #include "base/threading/thread_checker.h" #include "cobalt/loader/decoder.h" #include "cobalt/loader/fetcher.h" +#include "cobalt/render_tree/resource_provider.h" namespace cobalt { namespace loader { @@ -38,6 +39,7 @@ public: typedef base::Callback<scoped_ptr<Fetcher>(Fetcher::Handler*)> FetcherCreator; typedef base::Callback<void(Loader*)> OnDestructionFunction; + typedef base::Callback<void(const std::string&)> OnErrorFunction; // The construction of Loader initiates the loading. It takes the ownership // of a Decoder and creates and manages a Fetcher using the given creation @@ -45,16 +47,26 @@ // The fetcher creator, decoder and error callback shouldn't be NULL. // It is allowed to destroy the loader in the error callback. Loader(const FetcherCreator& fetcher_creator, scoped_ptr<Decoder> decoder, - const base::Callback<void(const std::string&)>& error_callback, + const OnErrorFunction& on_error, const OnDestructionFunction& on_destruction = OnDestructionFunction()); ~Loader(); - void Abort(); + // Suspends the load of this resource, expecting it to be resumed or destroyed + // later. + void Suspend(); + + // Resumes the load of this resource. Suspend must have been previously + // called. + void Resume(render_tree::ResourceProvider* resource_provider); private: class FetcherToDecoderAdapter; + // Starts the fetch-and-decode. + void Start(); + + FetcherCreator fetcher_creator_; scoped_ptr<Decoder> decoder_; scoped_ptr<FetcherToDecoderAdapter> fetcher_to_decoder_adaptor_; scoped_ptr<Fetcher> fetcher_; @@ -62,8 +74,11 @@ base::CancelableClosure fetcher_creator_error_closure_; base::ThreadChecker thread_checker_; + OnErrorFunction on_error_; OnDestructionFunction on_destruction_; + bool suspended_; + DISALLOW_COPY_AND_ASSIGN(Loader); };
diff --git a/src/cobalt/loader/loader_factory.cc b/src/cobalt/loader/loader_factory.cc index 9356c00..e39a512 100644 --- a/src/cobalt/loader/loader_factory.cc +++ b/src/cobalt/loader/loader_factory.cc
@@ -76,7 +76,7 @@ resource_provider_ = NULL; for (LoaderSet::const_iterator iter = active_loaders_.begin(); iter != active_loaders_.end(); ++iter) { - (*iter)->Abort(); + (*iter)->Suspend(); } } @@ -86,6 +86,10 @@ DCHECK(!resource_provider_); resource_provider_ = resource_provider; + for (LoaderSet::const_iterator iter = active_loaders_.begin(); + iter != active_loaders_.end(); ++iter) { + (*iter)->Resume(resource_provider); + } } void LoaderFactory::OnLoaderCreated(Loader* loader) {
diff --git a/src/cobalt/loader/loader_test.cc b/src/cobalt/loader/loader_test.cc index f3be464..c7e637c 100644 --- a/src/cobalt/loader/loader_test.cc +++ b/src/cobalt/loader/loader_test.cc
@@ -73,33 +73,47 @@ // Mocks & Stubs ////////////////////////////////////////////////////////////////////////// -class StubFetcherError : public Fetcher { - public: - explicit StubFetcherError(Handler* handler) : Fetcher(handler) { - handler->OnError(this, "Fake error for test"); - } - - static scoped_ptr<Fetcher> Create(Handler* handler) { - return scoped_ptr<Fetcher>(new StubFetcherError(handler)); - } -}; - -class StubFetcherReceivedDone : public Fetcher { - public: - explicit StubFetcherReceivedDone(Handler* handler) : Fetcher(handler) { - handler->OnReceived(this, NULL, 0); - handler->OnDone(this); - } - - static scoped_ptr<Fetcher> Create(Handler* handler) { - return scoped_ptr<Fetcher>(new StubFetcherReceivedDone(handler)); - } -}; - class MockDecoder : public Decoder { public: MOCK_METHOD2(DecodeChunk, void(const char*, size_t)); MOCK_METHOD0(Finish, void()); + MOCK_METHOD0(Suspend, bool()); + MOCK_METHOD1(Resume, void(render_tree::ResourceProvider*)); +}; + +class MockFetcher : public Fetcher { + public: + explicit MockFetcher(Handler* handler) : Fetcher(handler) {} + + void FireError(const char* message) { handler()->OnError(this, message); } + + void FireReceived(const char* data, size_t size) { + handler()->OnReceived(this, data, size); + } + + void FireDone() { handler()->OnDone(this); } + + static scoped_ptr<Fetcher> Create(Handler* handler) { + return scoped_ptr<Fetcher>(new MockFetcher(handler)); + } +}; + +struct MockFetcherFactory { + public: + MockFetcherFactory(): count(0) {} + // Way to access the last fetcher created by the fetcher factory. + MockFetcher* fetcher; + int count; + + scoped_ptr<Fetcher> Create(Fetcher::Handler* handler) { + fetcher = new MockFetcher(handler); + ++count; + return scoped_ptr<Fetcher>(fetcher); + } + + Loader::FetcherCreator GetFetcherCreator() { + return base::Bind(&MockFetcherFactory::Create, base::Unretained(this)); + } }; class MockLoaderCallback : public LoaderCallback { @@ -138,30 +152,77 @@ TEST_F(LoaderTest, FetcherError) { MockLoaderCallback mock_loader_callback; + MockFetcherFactory mock_fetcher_factory; MockDecoder* mock_decoder = new MockDecoder(); // To be owned by loader. EXPECT_CALL(*mock_decoder, DecodeChunk(_, _)).Times(0); EXPECT_CALL(*mock_decoder, Finish()).Times(0); EXPECT_CALL(mock_loader_callback, OnError(_)); - Loader loader(base::Bind(&StubFetcherError::Create), + Loader loader(mock_fetcher_factory.GetFetcherCreator(), scoped_ptr<Decoder>(mock_decoder), base::Bind(&MockLoaderCallback::OnError, base::Unretained(&mock_loader_callback))); + mock_fetcher_factory.fetcher->FireError("Fail"); +} + +TEST_F(LoaderTest, FetcherSuspendAbort) { + MockLoaderCallback mock_loader_callback; + MockFetcherFactory mock_fetcher_factory; + MockDecoder* mock_decoder = new MockDecoder(); // To be owned by loader. + EXPECT_CALL(*mock_decoder, DecodeChunk(_, _)).Times(0); + EXPECT_CALL(*mock_decoder, Finish()).Times(0); + EXPECT_CALL(*mock_decoder, Suspend()); + EXPECT_CALL(*mock_decoder, Resume(_)).Times(0); + EXPECT_CALL(mock_loader_callback, OnError(_)); + + Loader loader(mock_fetcher_factory.GetFetcherCreator(), + scoped_ptr<Decoder>(mock_decoder), + base::Bind(&MockLoaderCallback::OnError, + base::Unretained(&mock_loader_callback))); + loader.Suspend(); +} + +TEST_F(LoaderTest, FetcherSuspendResumeDone) { + MockLoaderCallback mock_loader_callback; + MockFetcherFactory mock_fetcher_factory; + MockDecoder* mock_decoder = new MockDecoder(); // To be owned by loader. + ON_CALL(*mock_decoder, Suspend()).WillByDefault(testing::Return(true)); + + EXPECT_CALL(*mock_decoder, Suspend()); + EXPECT_CALL(*mock_decoder, Resume(_)); + + EXPECT_CALL(*mock_decoder, DecodeChunk(_, _)).Times(0); + EXPECT_CALL(mock_loader_callback, OnError(_)).Times(0); + EXPECT_CALL(*mock_decoder, Finish()); + + Loader loader(mock_fetcher_factory.GetFetcherCreator(), + scoped_ptr<Decoder>(mock_decoder), + base::Bind(&MockLoaderCallback::OnError, + base::Unretained(&mock_loader_callback))); + loader.Suspend(); + loader.Resume(NULL); + + // The fetcher should have been torn down and recreated. + EXPECT_EQ(2, mock_fetcher_factory.count); + mock_fetcher_factory.fetcher->FireDone(); } TEST_F(LoaderTest, FetcherReceiveDone) { InSequence dummy; MockLoaderCallback mock_loader_callback; + MockFetcherFactory mock_fetcher_factory; MockDecoder* mock_decoder = new MockDecoder(); // To be owned by loader. EXPECT_CALL(mock_loader_callback, OnError(_)).Times(0); EXPECT_CALL(*mock_decoder, DecodeChunk(_, _)); EXPECT_CALL(*mock_decoder, Finish()); - Loader loader(base::Bind(&StubFetcherReceivedDone::Create), + Loader loader(mock_fetcher_factory.GetFetcherCreator(), scoped_ptr<Decoder>(mock_decoder), base::Bind(&MockLoaderCallback::OnError, base::Unretained(&mock_loader_callback))); + mock_fetcher_factory.fetcher->FireReceived(NULL, 0); + mock_fetcher_factory.fetcher->FireDone(); } // Typical usage of Loader.
diff --git a/src/cobalt/loader/text_decoder.h b/src/cobalt/loader/text_decoder.h index 3c7cf13..5de3c4c 100644 --- a/src/cobalt/loader/text_decoder.h +++ b/src/cobalt/loader/text_decoder.h
@@ -33,7 +33,7 @@ class TextDecoder : public Decoder { public: explicit TextDecoder(base::Callback<void(const std::string&)> done_callback) - : done_callback_(done_callback) {} + : done_callback_(done_callback), suspended_(false) {} ~TextDecoder() OVERRIDE {} // This function is used for binding callback for creating TextDecoder. @@ -45,17 +45,32 @@ // From Decoder. void DecodeChunk(const char* data, size_t size) OVERRIDE { DCHECK(thread_checker_.CalledOnValidThread()); + if (suspended_) { + return; + } text_.append(data, size); } void Finish() OVERRIDE { DCHECK(thread_checker_.CalledOnValidThread()); + if (suspended_) { + return; + } done_callback_.Run(text_); } + bool Suspend() OVERRIDE { + suspended_ = true; + text_.clear(); + return true; + } + void Resume(render_tree::ResourceProvider* /*resource_provider*/) OVERRIDE { + suspended_ = false; + } private: base::ThreadChecker thread_checker_; std::string text_; base::Callback<void(const std::string&)> done_callback_; + bool suspended_; }; } // namespace loader
diff --git a/src/cobalt/script/mozjs/mozjs_engine.cc b/src/cobalt/script/mozjs/mozjs_engine.cc index d5076b8..a2870e8 100644 --- a/src/cobalt/script/mozjs/mozjs_engine.cc +++ b/src/cobalt/script/mozjs/mozjs_engine.cc
@@ -87,6 +87,17 @@ "Total JavaScript engine registered.") { } +// Pretend we always preserve wrappers since we never call +// SetPreserveWrapperCallback anywhere else. This is necessary for +// TryPreserveReflector called by WeakMap to not crash. Disabling +// bindings to WeakMap does not appear to be an easy option because +// of its use in selfhosted.js. See bugzilla discussion linked where +// they decided to include a similar dummy in the mozjs shell. +// https://bugzilla.mozilla.org/show_bug.cgi?id=829798 +bool DummyPreserveWrapperCallback(JSContext *cx, JSObject *obj) { + return true; +} + } // namespace MozjsEngine::MozjsEngine() { @@ -124,6 +135,8 @@ // Callback to be called during garbage collection during the sweep phase. JS_SetFinalizeCallback(runtime_, &MozjsEngine::FinalizeCallback); + js::SetPreserveWrapperCallback(runtime_, DummyPreserveWrapperCallback); + EngineStats::GetInstance()->EngineCreated(); }
diff --git a/src/cobalt/speech/audio_encoder_flac.cc b/src/cobalt/speech/audio_encoder_flac.cc index d4403e1..16c5640 100644 --- a/src/cobalt/speech/audio_encoder_flac.cc +++ b/src/cobalt/speech/audio_encoder_flac.cc
@@ -57,16 +57,21 @@ FLAC__stream_encoder_delete(encoder_); } -void AudioEncoderFlac::Encode(const AudioBus* audio_bus) { +void AudioEncoderFlac::Encode(const ShellAudioBus* audio_bus) { DCHECK(thread_checker_.CalledOnValidThread()); DCHECK_EQ(audio_bus->channels(), 1); - const float* audio_data = audio_bus->channel(0); uint32 frames = static_cast<uint32>(audio_bus->frames()); scoped_array<FLAC__int32> flac_samples(new FLAC__int32[frames]); for (uint32 i = 0; i < frames; ++i) { - flac_samples[i] = - static_cast<FLAC__int32>(audio_data[i] * kMaxInt16AsFloat32); + if (audio_bus->sample_type() == ShellAudioBus::kFloat32) { + flac_samples[i] = static_cast<FLAC__int32>( + audio_bus->GetFloat32Sample(0, i) * kMaxInt16AsFloat32); + } else { + DCHECK_EQ(audio_bus->sample_type(), ShellAudioBus::kInt16); + flac_samples[i] = + static_cast<FLAC__int32>(audio_bus->GetInt16Sample(0, i)); + } } FLAC__int32* flac_samples_ptr = flac_samples.get();
diff --git a/src/cobalt/speech/audio_encoder_flac.h b/src/cobalt/speech/audio_encoder_flac.h index be993ca..62fcfb1 100644 --- a/src/cobalt/speech/audio_encoder_flac.h +++ b/src/cobalt/speech/audio_encoder_flac.h
@@ -22,7 +22,7 @@ #include "base/basictypes.h" #include "base/callback.h" #include "base/threading/thread_checker.h" -#include "media/base/audio_bus.h" +#include "media/base/shell_audio_bus.h" #include "third_party/flac/include/FLAC/stream_encoder.h" namespace cobalt { @@ -31,13 +31,13 @@ // Encode raw audio to using FLAC codec. class AudioEncoderFlac { public: - typedef ::media::AudioBus AudioBus; + typedef ::media::ShellAudioBus ShellAudioBus; explicit AudioEncoderFlac(int sample_rate); ~AudioEncoderFlac(); // Encode raw audio data. - void Encode(const AudioBus* audio_bus); + void Encode(const ShellAudioBus* audio_bus); // Finish encoding. void Finish();
diff --git a/src/cobalt/speech/mic.h b/src/cobalt/speech/mic.h index b83b875..506632a 100644 --- a/src/cobalt/speech/mic.h +++ b/src/cobalt/speech/mic.h
@@ -20,7 +20,7 @@ #include <string> #include "base/callback.h" -#include "media/base/audio_bus.h" +#include "media/base/shell_audio_bus.h" namespace cobalt { namespace speech { @@ -28,8 +28,8 @@ // An abstract class is used for interacting platform specific microphone. class Mic { public: - typedef ::media::AudioBus AudioBus; - typedef base::Callback<void(scoped_ptr<AudioBus>)> DataReceivedCallback; + typedef ::media::ShellAudioBus ShellAudioBus; + typedef base::Callback<void(scoped_ptr<ShellAudioBus>)> DataReceivedCallback; typedef base::Callback<void(void)> CompletionCallback; typedef base::Callback<void(void)> ErrorCallback;
diff --git a/src/cobalt/speech/speech_recognition_manager.cc b/src/cobalt/speech/speech_recognition_manager.cc index 2c69fc2..e531a77 100644 --- a/src/cobalt/speech/speech_recognition_manager.cc +++ b/src/cobalt/speech/speech_recognition_manager.cc
@@ -93,7 +93,8 @@ state_ = kAborted; } -void SpeechRecognitionManager::OnDataReceived(scoped_ptr<AudioBus> audio_bus) { +void SpeechRecognitionManager::OnDataReceived( + scoped_ptr<ShellAudioBus> audio_bus) { if (!main_message_loop_->BelongsToCurrentThread()) { // Called from mic thread. main_message_loop_->PostTask( @@ -123,9 +124,9 @@ // silence at the end in case encoder had no data already. size_t dummy_frames = static_cast<size_t>(kSampleRate * kAudioPacketDurationInSeconds); - scoped_ptr<AudioBus> dummy_audio_bus = - AudioBus::Create(1, static_cast<int>(dummy_frames)); - memset(dummy_audio_bus->channel(0), 0, dummy_frames); + scoped_ptr<ShellAudioBus> dummy_audio_bus(new ShellAudioBus( + 1, dummy_frames, ShellAudioBus::kInt16, ShellAudioBus::kPlanar)); + dummy_audio_bus->ZeroAllFrames(); recognizer_.RecognizeAudio(dummy_audio_bus.Pass(), true); } }
diff --git a/src/cobalt/speech/speech_recognition_manager.h b/src/cobalt/speech/speech_recognition_manager.h index dcc9457..852ede2 100644 --- a/src/cobalt/speech/speech_recognition_manager.h +++ b/src/cobalt/speech/speech_recognition_manager.h
@@ -26,6 +26,7 @@ #include "cobalt/speech/speech_recognition_error.h" #include "cobalt/speech/speech_recognition_event.h" #include "cobalt/speech/speech_recognizer.h" +#include "media/base/shell_audio_bus.h" namespace cobalt { namespace speech { @@ -37,7 +38,7 @@ // class would encode the audio data, then send it to recogniton service. class SpeechRecognitionManager { public: - typedef ::media::AudioBus AudioBus; + typedef ::media::ShellAudioBus ShellAudioBus; typedef base::Callback<bool(const scoped_refptr<dom::Event>&)> EventCallback; SpeechRecognitionManager(network::NetworkModule* network_module, @@ -59,7 +60,7 @@ }; // Callbacks from mic. - void OnDataReceived(scoped_ptr<AudioBus> audio_bus); + void OnDataReceived(scoped_ptr<ShellAudioBus> audio_bus); void OnDataCompletion(); void OnMicError();
diff --git a/src/cobalt/speech/speech_recognizer.cc b/src/cobalt/speech/speech_recognizer.cc index bdd0688..d5c1ce1 100644 --- a/src/cobalt/speech/speech_recognizer.cc +++ b/src/cobalt/speech/speech_recognizer.cc
@@ -169,7 +169,7 @@ base::Bind(&SpeechRecognizer::StopInternal, base::Unretained(this))); } -void SpeechRecognizer::RecognizeAudio(scoped_ptr<AudioBus> audio_bus, +void SpeechRecognizer::RecognizeAudio(scoped_ptr<ShellAudioBus> audio_bus, bool is_last_chunk) { // Called by the speech recognition manager thread. thread_.message_loop()->PostTask( @@ -293,8 +293,8 @@ chunked_byte_buffer_.Clear(); } -void SpeechRecognizer::UploadAudioDataInternal(scoped_ptr<AudioBus> audio_bus, - bool is_last_chunk) { +void SpeechRecognizer::UploadAudioDataInternal( + scoped_ptr<ShellAudioBus> audio_bus, bool is_last_chunk) { DCHECK_EQ(thread_.message_loop(), MessageLoop::current()); DCHECK(audio_bus);
diff --git a/src/cobalt/speech/speech_recognizer.h b/src/cobalt/speech/speech_recognizer.h index cb76a05..90e5dcd 100644 --- a/src/cobalt/speech/speech_recognizer.h +++ b/src/cobalt/speech/speech_recognizer.h
@@ -42,7 +42,7 @@ // manager. class SpeechRecognizer : public net::URLFetcherDelegate { public: - typedef ::media::AudioBus AudioBus; + typedef ::media::ShellAudioBus ShellAudioBus; typedef base::Callback<void(const scoped_refptr<dom::Event>&)> EventCallback; typedef SpeechRecognitionResultList::SpeechRecognitionResults SpeechRecognitionResults; @@ -58,7 +58,7 @@ // Stop speech recognizer. void Stop(); // An encoded audio data is available and ready to be recognized. - void RecognizeAudio(scoped_ptr<AudioBus> audio_bus, bool is_last_chunk); + void RecognizeAudio(scoped_ptr<ShellAudioBus> audio_bus, bool is_last_chunk); // net::URLFetcherDelegate interface void OnURLFetchDownloadData(const net::URLFetcher* source, @@ -71,7 +71,7 @@ private: void StartInternal(const SpeechRecognitionConfig& config, int sample_rate); void StopInternal(); - void UploadAudioDataInternal(scoped_ptr<AudioBus> audio_bus, + void UploadAudioDataInternal(scoped_ptr<ShellAudioBus> audio_bus, bool is_last_chunk); void ProcessAndFireSuccessEvent(const SpeechRecognitionResults& new_results);
diff --git a/src/cobalt/webdriver_benchmarks/partial_layout_benchmark.py b/src/cobalt/webdriver_benchmarks/partial_layout_benchmark.py index 41503c6..479ad5c 100755 --- a/src/cobalt/webdriver_benchmarks/partial_layout_benchmark.py +++ b/src/cobalt/webdriver_benchmarks/partial_layout_benchmark.py
@@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/python2 """Webdriver-based benchmarks for partial layout. Benchmarks for partial layout changes in Cobalt. @@ -108,11 +108,12 @@ sys.path.append(script_path + "/../../tools/lbshell/") app_launcher = importlib.import_module("app_launcher") self.launcher = app_launcher.CreateLauncher( - platform, executable, devkit_name=devkit_name) + platform, executable, devkit_name=devkit_name, close_output_file=False) self.launcher.SetArgs(["--enable_webdriver"]) self.launcher.SetOutputCallback(self._HandleLine) self.log_file_path = log_file_path + self.log_file = None def __enter__(self): self.thread = threading.Thread(target=self.Run) @@ -166,13 +167,15 @@ to_close = None try: if self.log_file_path: - log_file = open(self.log_file_path, "w") - to_close = log_file + self.log_file = open(self.log_file_path, "w") + to_close = self.log_file else: - log_file = sys.stdout + self.log_file = sys.stdout - self.launcher.SetOutputFile(log_file) + self.launcher.SetOutputFile(self.log_file) self.launcher.Run() + # This is watched for in webdriver_benchmark_test.py + sys.stdout.write("partial_layout_benchmark TEST COMPLETE\n") finally: if to_close: to_close.close() @@ -195,6 +198,8 @@ def main(): args = arg_parser.parse_args() + # Keep unittest module from seeing these args + sys.argv = sys.argv[:1] platform = args.platform if platform is None:
diff --git a/src/cobalt/webdriver_benchmarks/tests/all.py b/src/cobalt/webdriver_benchmarks/tests/all.py new file mode 100755 index 0000000..78924c0 --- /dev/null +++ b/src/cobalt/webdriver_benchmarks/tests/all.py
@@ -0,0 +1,38 @@ +#!/usr/bin/python2 +"""Target for running all tests cases.""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import importlib +import os +import sys +import unittest + +# The parent directory is a module +sys.path.insert(0, os.path.dirname(os.path.dirname( + os.path.realpath(__file__)))) + +# pylint: disable=C6204,C6203 +import tv_testcase + + +# pylint: disable=unused-argument +def load_tests(loader, tests, pattern): + """This is a Python unittest "load_tests protocol method.""" + s = unittest.TestSuite() + + for f in os.listdir(os.path.dirname(__file__)): + if not f.endswith(".py"): + continue + if f.startswith("_"): + continue + if f == "all.py": + continue + s.addTest(unittest.TestLoader().loadTestsFromModule( + importlib.import_module("tests." + f[:-3]))) + return s + +if __name__ == "__main__": + tv_testcase.main()
diff --git a/src/cobalt/webdriver_benchmarks/tests/guide.py b/src/cobalt/webdriver_benchmarks/tests/guide.py new file mode 100755 index 0000000..442eef1 --- /dev/null +++ b/src/cobalt/webdriver_benchmarks/tests/guide.py
@@ -0,0 +1,48 @@ +#!/usr/bin/python2 +"""Simple guide navigation test.""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import os +import sys + +# The parent directory is a module +sys.path.insert(0, os.path.dirname(os.path.dirname( + os.path.realpath(__file__)))) + +# pylint: disable=C6204,C6203 +import tv +import tv_testcase +import partial_layout_benchmark + +# selenium imports +keys = partial_layout_benchmark.ImportSeleniumModule("webdriver.common.keys") + +REPEAT_COUNT = 10 + + +class GuideTest(tv_testcase.TvTestCase): + + def test_simple(self): + self.load_tv() + self.assert_displayed(tv.FOCUSED_SHELF) + + print(str(self.get_webdriver().execute_script( + "h5vcc.system.recordStats = true"))) + + for _ in xrange(REPEAT_COUNT): + self.send_keys(tv.FOCUSED_SHELF, keys.Keys.ARROW_LEFT) + self.assert_displayed(tv.FOCUSED_GUIDE) + self.wait_for_layout_complete() + self.send_keys(tv.FOCUSED_GUIDE, keys.Keys.ARROW_RIGHT) + self.poll_until_found(tv.FOCUSED_SHELF) + self.assert_displayed(tv.FOCUSED_SHELF_TITLE) + self.wait_for_layout_complete() + + self.record_results("GuideTest.test_simple") + + +if __name__ == "__main__": + tv_testcase.main()
diff --git a/src/cobalt/webdriver_benchmarks/tests/shelf.py b/src/cobalt/webdriver_benchmarks/tests/shelf.py index b81478b..b47e379 100755 --- a/src/cobalt/webdriver_benchmarks/tests/shelf.py +++ b/src/cobalt/webdriver_benchmarks/tests/shelf.py
@@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/python2 """Simple shelf navigation test.""" from __future__ import absolute_import @@ -7,7 +7,6 @@ import os import sys -import time # The parent directory is a module sys.path.insert(0, os.path.dirname(os.path.dirname( @@ -27,11 +26,6 @@ class ShelfTest(tv_testcase.TvTestCase): - def _wait_for_layout(self): - while int(self.get_webdriver().execute_script( - "return h5vcc.cVal.getValue('Event.MainWebModule.IsProcessing')")): - time.sleep(0.1) - def test_simple(self): self.load_tv() self.assert_displayed(tv.FOCUSED_SHELF) @@ -43,18 +37,16 @@ self.send_keys(tv.FOCUSED_SHELF, keys.Keys.ARROW_DOWN) self.poll_until_found(tv.FOCUSED_SHELF) self.assert_displayed(tv.FOCUSED_SHELF_TITLE) - self._wait_for_layout() + self.wait_for_layout_complete() for _ in xrange(SHELF_ITEMS_COUNT): self.send_keys(tv.FOCUSED_TILE, keys.Keys.ARROW_RIGHT) self.poll_until_found(tv.FOCUSED_TILE) self.assert_displayed(tv.FOCUSED_SHELF_TITLE) - self._wait_for_layout() + self.wait_for_layout_complete() - print("ShelfTest event durations" - + str(self.get_webdriver().execute_script( - "return h5vcc.cVal.getValue(" - "'Event.Durations.MainWebModule.KeyUp')"))) + self.record_results("ShelfTest.test_simple") + if __name__ == "__main__": tv_testcase.main()
diff --git a/src/cobalt/webdriver_benchmarks/tv_testcase.py b/src/cobalt/webdriver_benchmarks/tv_testcase.py index 39bfe41..23649ec 100644 --- a/src/cobalt/webdriver_benchmarks/tv_testcase.py +++ b/src/cobalt/webdriver_benchmarks/tv_testcase.py
@@ -27,6 +27,7 @@ BASE_URL = "https://www.youtube.com/tv" PAGE_LOAD_WAIT_SECONDS = 30 +LAYOUT_TIMEOUT_SECONDS = 5 class TvTestCase(unittest.TestCase): @@ -36,6 +37,9 @@ with an internal class with the same name. """ + class LayoutTimeoutException(BaseException): + """Exception thrown when layout did not complete in time.""" + def get_webdriver(self): return partial_layout_benchmark.GetWebDriver() @@ -127,6 +131,10 @@ could not be immediately found. If the retries do not succeed, the underlying exception is passed through. + Args: + css_selector: A CSS selector + keys: key events + Raises: Underlying WebDriver exceptions """ @@ -144,6 +152,30 @@ raise time.sleep(1) + def wait_for_layout_complete(self): + """Waits for Cobalt to complete pending layouts.""" + start_time = time.time() + while int(self.get_webdriver().execute_script( + "return h5vcc.cVal.getValue('Event.MainWebModule.IsProcessing')")): + + if time.time() - start_time > LAYOUT_TIMEOUT_SECONDS: + raise TvTestCase.LayoutTimeoutException() + + time.sleep(0.1) + + def record_results(self, name): + """Records results of benchmark. + + The duration of KeyUp events will be recorded. + + Args: + name: name of test case + """ + print("tv_testcase RESULT: " + name + " " + + str(self.get_webdriver().execute_script( + "return h5vcc.cVal.getValue(" + "'Event.Durations.MainWebModule.KeyUp')"))) + def main(): partial_layout_benchmark.main()
diff --git a/src/media/base/shell_audio_bus.h b/src/media/base/shell_audio_bus.h index 6c43f8c..16c7012 100644 --- a/src/media/base/shell_audio_bus.h +++ b/src/media/base/shell_audio_bus.h
@@ -59,6 +59,7 @@ size_t channels() const { return channels_; } size_t frames() const { return frames_; } + SampleType sample_type() const { return sample_type_; } size_t GetSampleSizeInBytes() const; const uint8* interleaved_data() const; const uint8* planar_data(size_t channel) const;
diff --git a/src/starboard/shared/posix/log_raw.cc b/src/starboard/shared/posix/log_raw.cc index 10cfff0..f790d78 100644 --- a/src/starboard/shared/posix/log_raw.cc +++ b/src/starboard/shared/posix/log_raw.cc
@@ -23,14 +23,13 @@ // Cribbed from base/logging.cc's RawLog() function. size_t bytes_written = 0; const size_t message_len = strlen(message); - int rv; while (bytes_written < message_len) { - rv = HANDLE_EINTR(write(STDERR_FILENO, message + bytes_written, - message_len - bytes_written)); - if (rv < 0) { + int retval = HANDLE_EINTR(write(STDERR_FILENO, message + bytes_written, + message_len - bytes_written)); + if (retval < 0) { // Give up, nothing we can do now. break; } - bytes_written += rv; + bytes_written += retval; } }